Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -171,18 +171,14 @@ { public: MallocChecker() - : II_alloca(nullptr), II_win_alloca(nullptr), II_malloc(nullptr), - II_free(nullptr), II_realloc(nullptr), II_calloc(nullptr), - 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_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_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_kmalloc(nullptr), II_if_nameindex(nullptr), + II_if_freenameindex(nullptr), II_valloc(nullptr), + II_realloc(nullptr), II_calloc(nullptr), + II_reallocarray(nullptr), II_recallocarray(nullptr), + II_strndup(nullptr), II_g_memdup(nullptr), + II_g_malloc0(nullptr), II_g_try_malloc0(nullptr), + II_g_malloc0_n(nullptr), II_g_try_malloc0_n(nullptr), + II_reallocf(nullptr), II_malloc(nullptr) {} /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. @@ -242,18 +238,27 @@ mutable std::unique_ptr BT_MismatchedDealloc; mutable std::unique_ptr BT_OffsetFree[CK_NumCheckKinds]; mutable std::unique_ptr BT_UseZerroAllocated[CK_NumCheckKinds]; - mutable IdentifierInfo *II_alloca, *II_win_alloca, *II_malloc, *II_free, - *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_g_malloc, *II_g_malloc0, - *II_g_realloc, *II_g_try_malloc, *II_g_try_malloc0, - *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; mutable Optional KernelZeroFlagVal; + mutable llvm::SmallSet AllocFunctions; + mutable llvm::SmallSet FreeFunctions; + + mutable llvm::SmallSet AllocaFunctions; + mutable llvm::SmallSet SimpleMallocFunctions; + + mutable llvm::SmallSet NZeroMallocFunctions; + + mutable llvm::SmallSet NReallocFunctions; + mutable llvm::SmallSet ReallocFunctions; + + mutable llvm::SmallSet StrdupFunctions; + + mutable IdentifierInfo *II_kmalloc, *II_if_nameindex, *II_if_freenameindex, + *II_valloc, *II_realloc, *II_calloc, *II_reallocarray, + *II_recallocarray, *II_strndup, *II_g_memdup, + *II_g_malloc0, *II_g_try_malloc0, *II_g_malloc0_n, + *II_g_try_malloc0_n, *II_reallocf, *II_malloc; + void initIdentifierInfo(ASTContext &C) const; /// \brief Determine family of a deallocation expression. @@ -345,7 +350,8 @@ ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE, bool FreesMemOnFailure, ProgramStateRef State, - bool SuffixWithN = false) const; + bool SuffixWithN = false, + bool ZeroVal = false) const; static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, const Expr *BlockBytes); static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE, @@ -575,42 +581,86 @@ } // end anonymous namespace void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { - if (II_malloc) + if (!AllocFunctions.empty()) return; - II_alloca = &Ctx.Idents.get("alloca"); - II_malloc = &Ctx.Idents.get("malloc"); - II_free = &Ctx.Idents.get("free"); - II_realloc = &Ctx.Idents.get("realloc"); - II_reallocf = &Ctx.Idents.get("reallocf"); - II_calloc = &Ctx.Idents.get("calloc"); - II_valloc = &Ctx.Idents.get("valloc"); - II_strdup = &Ctx.Idents.get("strdup"); - II_strndup = &Ctx.Idents.get("strndup"); - II_wcsdup = &Ctx.Idents.get("wcsdup"); - II_kmalloc = &Ctx.Idents.get("kmalloc"); - II_if_nameindex = &Ctx.Idents.get("if_nameindex"); - II_if_freenameindex = &Ctx.Idents.get("if_freenameindex"); + AllocFunctions.insert(&Ctx.Idents.get("malloc")); + AllocFunctions.insert(&Ctx.Idents.get("realloc")); + AllocFunctions.insert(&Ctx.Idents.get("reallocf")); + AllocFunctions.insert(&Ctx.Idents.get("calloc")); + AllocFunctions.insert(&Ctx.Idents.get("valloc")); + AllocFunctions.insert(&Ctx.Idents.get("kmalloc")); + + AllocaFunctions.insert(&Ctx.Idents.get("alloca")); + AllocaFunctions.insert(&Ctx.Idents.get("_alloca")); + + SimpleMallocFunctions.insert(&Ctx.Idents.get("malloc")); + SimpleMallocFunctions.insert(&Ctx.Idents.get("g_malloc")); + SimpleMallocFunctions.insert(&Ctx.Idents.get("g_try_malloc")); + + NZeroMallocFunctions.insert(&Ctx.Idents.get("g_malloc_n")); + NZeroMallocFunctions.insert(&Ctx.Idents.get("g_try_malloc_n")); + NZeroMallocFunctions.insert(&Ctx.Idents.get("g_malloc0_n")); + NZeroMallocFunctions.insert(&Ctx.Idents.get("g_try_malloc0_n")); + + NReallocFunctions.insert(&Ctx.Idents.get("g_realloc_n")); + NReallocFunctions.insert(&Ctx.Idents.get("g_try_realloc_n")); + NReallocFunctions.insert(&Ctx.Idents.get("reallocarray")); + + ReallocFunctions.insert(&Ctx.Idents.get("realloc")); + ReallocFunctions.insert(&Ctx.Idents.get("g_realloc")); + ReallocFunctions.insert(&Ctx.Idents.get("g_try_realloc")); + + StrdupFunctions.insert(&Ctx.Idents.get("strdup")); + StrdupFunctions.insert(&Ctx.Idents.get("wcsdup")); + StrdupFunctions.insert(&Ctx.Idents.get("_strdup")); + StrdupFunctions.insert(&Ctx.Idents.get("_wcsdup")); + + FreeFunctions.insert(&Ctx.Idents.get("free")); //MSVC uses `_`-prefixed instead, so we check for them too. - II_win_strdup = &Ctx.Idents.get("_strdup"); - II_win_wcsdup = &Ctx.Idents.get("_wcsdup"); - II_win_alloca = &Ctx.Idents.get("_alloca"); + + AllocaFunctions.insert(&Ctx.Idents.get("_alloca")); // Glib - II_g_malloc = &Ctx.Idents.get("g_malloc"); + AllocFunctions.insert(&Ctx.Idents.get("g_malloc")); + AllocFunctions.insert(&Ctx.Idents.get("g_malloc0")); + AllocFunctions.insert(&Ctx.Idents.get("g_realloc")); + AllocFunctions.insert(&Ctx.Idents.get("g_try_malloc")); + AllocFunctions.insert(&Ctx.Idents.get("g_try_malloc0")); + AllocFunctions.insert(&Ctx.Idents.get("g_try_realloc")); + AllocFunctions.insert(&Ctx.Idents.get("g_memdup")); + AllocFunctions.insert(&Ctx.Idents.get("g_malloc_n")); + AllocFunctions.insert(&Ctx.Idents.get("g_malloc0_n")); + AllocFunctions.insert(&Ctx.Idents.get("g_realloc_n")); + AllocFunctions.insert(&Ctx.Idents.get("g_try_malloc_n")); + AllocFunctions.insert(&Ctx.Idents.get("g_try_malloc0_n")); + AllocFunctions.insert(&Ctx.Idents.get("g_try_realloc_n")); + + FreeFunctions.insert(&Ctx.Idents.get("g_free")); + + // BSD + AllocFunctions.insert(&Ctx.Idents.get("reallocarray")); + AllocFunctions.insert(&Ctx.Idents.get("recallocarray")); + + FreeFunctions.insert(&Ctx.Idents.get("freezero")); + + II_kmalloc = &Ctx.Idents.get("kmalloc"); + II_valloc = &Ctx.Idents.get("valloc"); + II_if_nameindex = &Ctx.Idents.get("if_nameindex"); + II_if_freenameindex = &Ctx.Idents.get("if_freenameindex"); + II_realloc = &Ctx.Idents.get("realloc"); + II_calloc = &Ctx.Idents.get("calloc"); + II_reallocarray = &Ctx.Idents.get("reallocarray"); + II_recallocarray = &Ctx.Idents.get("recallocarray"); + II_reallocf = &Ctx.Idents.get("reallocf"); 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_free = &Ctx.Idents.get("g_free"); - II_g_memdup = &Ctx.Idents.get("g_memdup"); - 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_strndup = &Ctx.Idents.get("strndup"); + II_g_memdup = &Ctx.Idents.get("g_memdup"); + II_malloc = &Ctx.Idents.get("malloc"); + } bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { @@ -646,23 +696,16 @@ initIdentifierInfo(C); if (Family == AF_Malloc && CheckFree) { - if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf || - FunI == II_g_free) + if (FreeFunctions.count(FunI) == 1 || + FunI == II_realloc || FunI == II_reallocf) return true; } if (Family == AF_Malloc && CheckAlloc) { - 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_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_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) + if (AllocFunctions.count(FunI) == 1 || + AllocaFunctions.count(FunI) == 1 || + StrdupFunctions.count(FunI) || + FunI == II_if_nameindex) return true; } @@ -677,7 +720,7 @@ } if (Family == AF_Alloca && CheckAlloc) { - if (FunI == II_alloca || FunI == II_win_alloca) + if (AllocaFunctions.count(FunI) == 1) return true; } } @@ -838,7 +881,7 @@ initIdentifierInfo(C.getASTContext()); IdentifierInfo *FunI = FD->getIdentifier(); - if (FunI == II_malloc || FunI == II_g_malloc || FunI == II_g_try_malloc) { + if (SimpleMallocFunctions.count(FunI) == 1) { if (CE->getNumArgs() < 1) return; if (CE->getNumArgs() < 3) { @@ -867,8 +910,7 @@ return; State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); State = ProcessZeroAllocation(C, CE, 0, State); - } else if (FunI == II_realloc || FunI == II_g_realloc || - FunI == II_g_try_realloc) { + } else if (ReallocFunctions.count(FunI) == 1) { State = ReallocMemAux(C, CE, false, State); State = ProcessZeroAllocation(C, CE, 1, State); } else if (FunI == II_reallocf) { @@ -878,14 +920,17 @@ 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 = ReallocMemAux(C, CE, false, State, true, true); + State = ProcessZeroAllocation(C, CE, 0, State); + State = ProcessZeroAllocation(C, CE, 1, State); + } else if (FreeFunctions.count(FunI) == 1) { State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); - } else if (FunI == II_strdup || FunI == II_win_strdup || - FunI == II_wcsdup || FunI == II_win_wcsdup) { + } else if (StrdupFunctions.count(FunI) == 1) { State = MallocUpdateRefState(C, CE, State); } else if (FunI == II_strndup) { State = MallocUpdateRefState(C, CE, State); - } else if (FunI == II_alloca || FunI == II_win_alloca) { + } else if (AllocaFunctions.count(FunI) == 1) { if (CE->getNumArgs() < 1) return; State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, @@ -930,8 +975,7 @@ return; State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State); State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n || - FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) { + } else if (NZeroMallocFunctions.count(FunI) == 1) { if (CE->getNumArgs() < 2) return; SVal Init = UndefinedVal(); @@ -943,7 +987,7 @@ 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 (NReallocFunctions.count(FunI) == 1) { if (CE->getNumArgs() < 3) return; State = ReallocMemAux(C, CE, false, State, true); @@ -2112,7 +2156,8 @@ const CallExpr *CE, bool FreesOnFail, ProgramStateRef State, - bool SuffixWithN) const { + bool SuffixWithN, + bool ZeroVal) const { if (!State) return nullptr; @@ -2121,6 +2166,7 @@ else if (CE->getNumArgs() < 2) return nullptr; + size_t OffsetSize = ZeroVal ? 1 : 0; const Expr *arg0Expr = CE->getArg(0); SVal Arg0Val = C.getSVal(arg0Expr); if (!Arg0Val.getAs()) @@ -2128,17 +2174,18 @@ DefinedOrUnknownSVal arg0Val = Arg0Val.castAs(); SValBuilder &svalBuilder = C.getSValBuilder(); + SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); DefinedOrUnknownSVal PtrEQ = svalBuilder.evalEQ(State, arg0Val, svalBuilder.makeNull()); // Get the size argument. - const Expr *Arg1 = CE->getArg(1); + const Expr *Arg1 = CE->getArg(1 + OffsetSize); // Get the value of the size argument. SVal TotalSize = C.getSVal(Arg1); if (SuffixWithN) - TotalSize = evalMulForBufferSize(C, Arg1, CE->getArg(2)); + TotalSize = evalMulForBufferSize(C, Arg1, CE->getArg(2 + OffsetSize)); if (!TotalSize.getAs()) return nullptr; @@ -2159,8 +2206,9 @@ // If the ptr is NULL and the size is not 0, the call is equivalent to // malloc(size). if (PrtIsNull && !SizeIsZero) { + SVal MemVal = ZeroVal ? zeroVal : UndefinedVal(); ProgramStateRef stateMalloc = MallocMemAux(C, CE, TotalSize, - UndefinedVal(), StatePtrIsNull); + MemVal, StatePtrIsNull); return stateMalloc; } 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}} +}