Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -182,7 +182,8 @@ II_g_free(nullptr), II_g_memdup(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_try_realloc_n(nullptr), II_reallocarray(nullptr), + II_recallocarray(nullptr), II_freezero(nullptr) {} /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. @@ -251,7 +252,8 @@ *II_g_try_realloc, *II_g_free, *II_g_memdup, *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_try_realloc_n, *II_reallocarray, + *II_recallocarray, *II_freezero; mutable Optional KernelZeroFlagVal; void initIdentifierInfo(ASTContext &C) const; @@ -349,7 +351,8 @@ static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, const Expr *BlockBytes); static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State); + ProgramStateRef State, + bool SuffixWithArray = false); ///\brief Check if the memory associated with this symbol was released. bool isReleased(SymbolRef Sym, CheckerContext &C) const; @@ -611,6 +614,11 @@ 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"); + + // BSD + II_reallocarray = &Ctx.Idents.get("reallocarray"); + II_recallocarray = &Ctx.Idents.get("recallocarray"); + II_freezero = &Ctx.Idents.get("freezero"); } bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { @@ -647,7 +655,7 @@ if (Family == AF_Malloc && CheckFree) { if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf || - FunI == II_g_free) + FunI == II_g_free || FunI == II_freezero) return true; } @@ -662,7 +670,8 @@ FunI == II_g_memdup || 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_try_realloc_n || FunI == II_reallocarray || + FunI == II_recallocarray) return true; } @@ -878,7 +887,11 @@ State = CallocMem(C, CE, State); State = ProcessZeroAllocation(C, CE, 0, State); State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_free || FunI == II_g_free) { + } else if (FunI == II_recallocarray) { + State = CallocMem(C, CE, State, true); + State = ProcessZeroAllocation(C, CE, 0, State); + State = ProcessZeroAllocation(C, CE, 1, State); + } else if (FunI == II_free || FunI == II_g_free || FunI == II_freezero) { State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); } else if (FunI == II_strdup || FunI == II_win_strdup || FunI == II_wcsdup || FunI == II_win_wcsdup) { @@ -943,7 +956,8 @@ State = MallocMemAux(C, CE, TotalSize, Init, 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) { + } else if (FunI == II_g_realloc_n || FunI == II_g_try_realloc_n || + FunI == II_reallocarray) { if (CE->getNumArgs() < 3) return; State = ReallocMemAux(C, CE, false, State, true); @@ -2215,7 +2229,8 @@ } ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) { + ProgramStateRef State, + bool SuffixWithArray) { if (!State) return nullptr; @@ -2224,7 +2239,12 @@ SValBuilder &svalBuilder = C.getSValBuilder(); SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); - SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); + SVal TotalSize; + if (!SuffixWithArray) { + TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); + } else { + TotalSize = evalMulForBufferSize(C, CE->getArg(2), CE->getArg(3)); + } return MallocMemAux(C, CE, TotalSize, zeroVal, State); } Index: test/Analysis/bsd-malloc.c =================================================================== --- /dev/null +++ test/Analysis/bsd-malloc.c @@ -0,0 +1,28 @@ +// RUN: %clang_analyze_cc1 -triple x86_64-unknown-openbsd -analyzer-checker=unix.Malloc -verify %s + +#define NULL ((void *) 0) + +typedef __typeof(sizeof(int)) size_t; + +void *reallocarray(void *ptr, size_t nmemb, size_t size); +void *recallocarray(void *ptr, size_t onmemb, size_t nmemb, size_t size); +void freezero(void *ptr, size_t size); + +void f1() { + int *parr = NULL; + parr = reallocarray(NULL, 10, sizeof(*parr)); + return; // expected-warning{{Potential leak of memory pointed to by 'parr'}} +} + +void f2() { + int *parr = NULL; + parr = recallocarray(NULL, 10, 20, sizeof(*parr)); + return; // expected-warning{{Potential leak of memory pointed to by 'parr'}} +} + +void f3() { + int *parr = NULL; + parr = reallocarray(NULL, 10, sizeof(*parr)); + freezero(parr, 10 * sizeof(*parr)); // expected-no-warning + freezero(parr, 10 * sizeof(*parr)); // expected-warning{{Attempt to free released memory}} +}