Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -174,7 +174,13 @@ II_valloc(nullptr), II_reallocf(nullptr), II_strndup(nullptr), II_strdup(nullptr), II_win_strdup(nullptr), II_kmalloc(nullptr), II_if_nameindex(nullptr), II_if_freenameindex(nullptr), - II_wcsdup(nullptr), II_win_wcsdup(nullptr) {} + II_wcsdup(nullptr), II_win_wcsdup(nullptr), II_g_malloc(nullptr), + II_g_malloc0(nullptr), II_g_realloc(nullptr), II_g_try_malloc(nullptr), + II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr), + II_g_malloc_n(nullptr), II_g_malloc0_n(nullptr), + II_g_realloc_n(nullptr), II_g_try_malloc_n(nullptr), + II_g_try_malloc0_n(nullptr), II_g_try_realloc_n(nullptr), + II_g_free(nullptr), II_g_memdup(nullptr) {} /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. @@ -236,7 +242,12 @@ *II_realloc, *II_calloc, *II_valloc, *II_reallocf, *II_strndup, *II_strdup, *II_win_strdup, *II_kmalloc, *II_if_nameindex, *II_if_freenameindex, *II_wcsdup, - *II_win_wcsdup; + *II_win_wcsdup, *II_g_malloc, *II_g_malloc0, + *II_g_realloc, *II_g_try_malloc, *II_g_try_malloc0, + *II_g_try_realloc, *II_g_malloc_n, *II_g_malloc0_n, + *II_g_realloc_n, *II_g_try_malloc_n, + *II_g_try_malloc0_n, *II_g_try_realloc_n, + *II_g_free, *II_g_memdup; mutable Optional KernelZeroFlagVal; void initIdentifierInfo(ASTContext &C) const; @@ -554,6 +565,22 @@ II_win_strdup = &Ctx.Idents.get("_strdup"); II_win_wcsdup = &Ctx.Idents.get("_wcsdup"); II_win_alloca = &Ctx.Idents.get("_alloca"); + + // Glib + II_g_malloc = &Ctx.Idents.get("g_malloc"); + II_g_malloc0 = &Ctx.Idents.get("g_malloc0"); + II_g_realloc = &Ctx.Idents.get("g_realloc"); + II_g_try_malloc = &Ctx.Idents.get("g_try_malloc"); + II_g_try_malloc0 = &Ctx.Idents.get("g_try_malloc0"); + II_g_try_realloc = &Ctx.Idents.get("g_try_realloc"); + II_g_malloc_n = &Ctx.Idents.get("g_malloc_n"); + II_g_malloc0_n = &Ctx.Idents.get("g_malloc0_n"); + II_g_realloc_n = &Ctx.Idents.get("g_realloc_n"); + II_g_try_malloc_n = &Ctx.Idents.get("g_try_malloc_n"); + II_g_try_malloc0_n = &Ctx.Idents.get("g_try_malloc0_n"); + II_g_try_realloc_n = &Ctx.Idents.get("g_try_realloc_n"); + II_g_free = &Ctx.Idents.get("g_free"); + II_g_memdup = &Ctx.Idents.get("g_memdup"); } bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { @@ -589,7 +616,8 @@ initIdentifierInfo(C); if (Family == AF_Malloc && CheckFree) { - if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf) + if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf || + FunI == II_g_free) return true; } @@ -597,7 +625,14 @@ if (FunI == II_malloc || FunI == II_realloc || FunI == II_reallocf || FunI == II_calloc || FunI == II_valloc || FunI == II_strdup || FunI == II_win_strdup || FunI == II_strndup || FunI == II_wcsdup || - FunI == II_win_wcsdup || FunI == II_kmalloc) + FunI == II_win_wcsdup || FunI == II_kmalloc || + FunI == II_g_malloc || FunI == II_g_malloc0 || + FunI == II_g_realloc || FunI == II_g_try_malloc || + FunI == II_g_try_malloc0 || FunI == II_g_try_realloc || + FunI == II_g_malloc_n || FunI == II_g_malloc0_n || + FunI == II_g_realloc_n || FunI == II_g_try_malloc_n || + FunI == II_g_try_malloc0_n || FunI == II_g_try_realloc_n || + FunI == II_g_memdup) return true; } @@ -762,7 +797,8 @@ initIdentifierInfo(C.getASTContext()); IdentifierInfo *FunI = FD->getIdentifier(); - if (FunI == II_malloc) { + if (FunI == II_malloc || FunI == II_g_malloc || FunI == II_g_malloc0 || + FunI == II_g_try_malloc || FunI == II_g_try_malloc0) { if (CE->getNumArgs() < 1) return; if (CE->getNumArgs() < 3) { @@ -791,7 +827,8 @@ return; State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); State = ProcessZeroAllocation(C, CE, 0, State); - } else if (FunI == II_realloc) { + } else if (FunI == II_realloc || FunI == II_g_realloc || + FunI == II_g_try_realloc) { State = ReallocMem(C, CE, false, State); State = ProcessZeroAllocation(C, CE, 1, State); } else if (FunI == II_reallocf) { @@ -801,7 +838,7 @@ State = CallocMem(C, CE, State); State = ProcessZeroAllocation(C, CE, 0, State); State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_free) { + } else if (FunI == II_free || FunI == II_g_free) { State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); } else if (FunI == II_strdup || FunI == II_win_strdup || FunI == II_wcsdup || FunI == II_win_wcsdup) { @@ -841,6 +878,24 @@ AF_IfNameIndex); } else if (FunI == II_if_freenameindex) { State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); + } else if (FunI == II_g_malloc_n || FunI == II_g_malloc0_n || + FunI == II_g_try_malloc_n || FunI == II_g_try_malloc0_n) { + if (CE->getNumArgs() < 2) + return; + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + State = ProcessZeroAllocation(C, CE, 0, State); + State = ProcessZeroAllocation(C, CE, 1, State); + } else if (FunI == II_g_realloc_n || FunI == II_g_try_realloc_n) { + if (CE->getNumArgs() < 2) + return; + State = ReallocMem(C, CE, false, State); + State = ProcessZeroAllocation(C, CE, 1, State); + State = ProcessZeroAllocation(C, CE, 2, State); + } else if (FunI == II_g_memdup) { + if (CE->getNumArgs() < 2) + return; + State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State); + State = ProcessZeroAllocation(C, CE, 1, State); } } Index: test/Analysis/gmalloc.c =================================================================== --- test/Analysis/gmalloc.c +++ test/Analysis/gmalloc.c @@ -0,0 +1,169 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.Malloc -analyzer-store=region -verify %s + +#include "Inputs/system-header-simulator.h" + +typedef void* gpointer; +typedef const void* gconstpointer; +typedef unsigned long gsize; +typedef unsigned int guint; + +gpointer g_malloc(gsize n_bytes); +gpointer g_malloc0(gsize n_bytes); +gpointer g_realloc(gpointer mem, gsize n_bytes); +gpointer g_try_malloc(gsize n_bytes); +gpointer g_try_malloc0(gsize n_bytes); +gpointer g_try_realloc(gpointer mem, gsize n_bytes); +gpointer g_malloc_n(gsize n_blocks, gsize n_block_bytes); +gpointer g_malloc0_n(gsize n_blocks, gsize n_block_bytes); +gpointer g_realloc_n(gpointer mem, gsize n_blocks, gsize n_block_bytes); +gpointer g_try_malloc_n(gsize n_blocks, gsize n_block_bytes); +gpointer g_try_malloc0_n(gsize n_blocks, gsize n_block_bytes); +gpointer g_try_realloc_n(gpointer mem, gsize n_blocks, gsize n_block_bytes); +void g_free(gpointer mem); +gpointer g_memdup(gconstpointer mem, guint byte_size); + +static const gsize n_bytes = 1024; + +void f1() { + gpointer g1 = g_malloc(n_bytes); + gpointer g2 = g_malloc0(n_bytes); + g1 = g_realloc(g1, n_bytes * 2); + gpointer g3 = g_try_malloc(n_bytes); + gpointer g4 = g_try_malloc0(n_bytes); + g3 = g_try_realloc(g3, n_bytes * 2); + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); + + g_free(g1); + g_free(g2); + g_free(g2); // expected-warning{{Attempt to free released memory}} +} + +void f2() { + gpointer g1 = g_malloc(n_bytes); + gpointer g2 = g_malloc0(n_bytes); + g1 = g_realloc(g1, n_bytes * 2); + gpointer g3 = g_try_malloc(n_bytes); + gpointer g4 = g_try_malloc0(n_bytes); + g3 = g_try_realloc(g3, n_bytes * 2); + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); + + g_free(g1); + g_free(g2); + g_free(g3); + g3 = g_memdup(g3, n_bytes); // expected-warning{{Use of memory after it is freed}} +} + +void f3() { + gpointer g1 = g_malloc(n_bytes); + gpointer g2 = g_malloc0(n_bytes); + g1 = g_realloc(g1, n_bytes * 2); + gpointer g3 = g_try_malloc(n_bytes); + gpointer g4 = g_try_malloc0(n_bytes); + g3 = g_try_realloc(g3, n_bytes * 2); // expected-warning{{Potential leak of memory pointed to by 'g4'}} + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g6'}} + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g5'}} + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}} + + g_free(g1); // expected-warning{{Potential leak of memory pointed to by 'g7'}} + g_free(g2); + g_free(g3); +} + +void f4() { + gpointer g1 = g_malloc(n_bytes); + gpointer g2 = g_malloc0(n_bytes); + g1 = g_realloc(g1, n_bytes * 2); + gpointer g3 = g_try_malloc(n_bytes); + gpointer g4 = g_try_malloc0(n_bytes); + g3 = g_try_realloc(g3, n_bytes * 2); + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g6'}} + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g5'}} + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}} + + g_free(g1); // expected-warning{{Potential leak of memory pointed to by 'g7'}} + g_free(g2); + g_free(g3); + g_free(g4); +} + +void f5() { + gpointer g1 = g_malloc(n_bytes); + gpointer g2 = g_malloc0(n_bytes); + g1 = g_realloc(g1, n_bytes * 2); + gpointer g3 = g_try_malloc(n_bytes); + gpointer g4 = g_try_malloc0(n_bytes); + g3 = g_try_realloc(g3, n_bytes * 2); + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g6'}} + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}} + + g_free(g1); // expected-warning{{Potential leak of memory pointed to by 'g7'}} + g_free(g2); + g_free(g3); + g_free(g4); + g_free(g5); +} + +void f6() { + gpointer g1 = g_malloc(n_bytes); + gpointer g2 = g_malloc0(n_bytes); + g1 = g_realloc(g1, n_bytes * 2); + gpointer g3 = g_try_malloc(n_bytes); + gpointer g4 = g_try_malloc0(n_bytes); + g3 = g_try_realloc(g3, n_bytes * 2); + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}} + + g_free(g1); // expected-warning{{Potential leak of memory pointed to by 'g7'}} + g_free(g2); + g_free(g3); + g_free(g4); + g_free(g5); + g_free(g6); +} + +void f7() { + gpointer g1 = g_malloc(n_bytes); + gpointer g2 = g_malloc0(n_bytes); + g1 = g_realloc(g1, n_bytes * 2); + gpointer g3 = g_try_malloc(n_bytes); + gpointer g4 = g_try_malloc0(n_bytes); + g3 = g_try_realloc(g3, n_bytes * 2); + gpointer g5 = g_malloc_n(n_bytes, sizeof(char)); + gpointer g6 = g_malloc0_n(n_bytes, sizeof(char)); + g5 = g_realloc_n(g5, n_bytes * 2, sizeof(char)); + gpointer g7 = g_try_malloc_n(n_bytes, sizeof(char)); + gpointer g8 = g_try_malloc0_n(n_bytes, sizeof(char)); + g7 = g_try_realloc_n(g7, n_bytes * 2, sizeof(char)); // expected-warning{{Potential leak of memory pointed to by 'g8'}} + + g_free(g1); + g_free(g2); + g_free(g3); + g_free(g4); + g_free(g5); + g_free(g6); + g_free(g7); +}