Index: clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h =================================================================== --- clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h +++ clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h @@ -11,13 +11,19 @@ #ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H #define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H -namespace clang { -class CheckerManager; +// FIXME: This file goes against how a checker should be implemented either in +// a single file, or be exposed in a header file. Let's try to get rid of it! + +namespace clang { namespace ento { +class CheckerManager; + /// Register the part of MallocChecker connected to InnerPointerChecker. void registerInnerPointerCheckerAux(CheckerManager &Mgr); -}} +} // namespace ento +} // namespace clang + #endif /* INTERCHECKERAPI_H_ */ Index: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -44,13 +44,14 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "AllocationState.h" #include "InterCheckerAPI.h" #include "clang/AST/Attr.h" #include "clang/AST/ParentMap.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Lexer.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -63,7 +64,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" -#include "AllocationState.h" +#include "llvm/Support/ErrorHandling.h" #include #include @@ -71,7 +72,10 @@ using namespace ento; //===----------------------------------------------------------------------===// -// The types of allocation we're modeling. +// The types of allocation we're modeling. This is used to check whether a +// dynamically allocated object is deallocated with the correct function, like +// not using operator delete on an object created by malloc(), or alloca regions +// aren't ever deallocated manually. //===----------------------------------------------------------------------===// namespace { @@ -91,22 +95,14 @@ } // end of anonymous namespace -/// Determine family of a deallocation expression. -static AllocationFamily -getAllocationFamily(const MemFunctionInfoTy &MemFunctionInfo, CheckerContext &C, - const Stmt *S); - /// Print names of allocators and deallocators. /// /// \returns true on success. -static bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, - const Expr *E); +static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E); -/// Print expected name of an allocator based on the deallocator's -/// family derived from the DeallocExpr. -static void printExpectedAllocName(raw_ostream &os, - const MemFunctionInfoTy &MemFunctionInfo, - CheckerContext &C, const Expr *E); +/// Print expected name of an allocator based on the deallocator's family +/// derived from the DeallocExpr. +static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family); /// Print expected name of a deallocator based on the allocator's /// family. @@ -205,9 +201,9 @@ /// Update the RefState to reflect the new memory allocation. /// The optional \p RetVal parameter specifies the newly allocated pointer /// value; if unspecified, the value of expression \p E is used. +template static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, - AllocationFamily Family = AF_Malloc, Optional RetVal = None); //===----------------------------------------------------------------------===// @@ -400,6 +396,7 @@ /// Process C++ operator new()'s allocation, which is the part of C++ /// new-expression that goes before the constructor. + template void processNewAllocation(const CXXNewExpr *NE, CheckerContext &C, SVal Target) const; @@ -446,10 +443,10 @@ /// malloc leaves it undefined. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. + template static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, const Expr *SizeEx, SVal Init, - ProgramStateRef State, - AllocationFamily Family = AF_Malloc); + ProgramStateRef State); /// Models memory allocation. /// @@ -460,10 +457,10 @@ /// malloc leaves it undefined. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. + template static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, SVal Size, SVal Init, - ProgramStateRef State, - AllocationFamily Family = AF_Malloc); + ProgramStateRef State); static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE, ProgramStateRef State, SVal Target); @@ -514,6 +511,7 @@ /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function /// we're modeling returns with Null on failure. /// \returns The ProgramState right after deallocation. + template ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE, ProgramStateRef State, unsigned Num, bool Hold, bool &IsKnownToBeAllocated, @@ -538,6 +536,7 @@ /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function /// we're modeling returns with Null on failure. /// \returns The ProgramState right after deallocation. + template ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *ArgExpr, const Expr *ParentExpr, ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated, @@ -556,6 +555,7 @@ /// \param [in] SuffixWithN Whether the reallocation function we're modeling /// has an '_n' suffix, such as g_realloc_n. /// \returns The ProgramState right after reallocation. + template ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE, bool ShouldFreeOnFail, ProgramStateRef State, bool SuffixWithN = false) const; @@ -622,27 +622,37 @@ /// family/call/symbol. Optional getCheckIfTracked(AllocationFamily Family, bool IsALeakCheck = false) const; + + template Optional getCheckIfTracked(CheckerContext &C, const Stmt *AllocDeallocStmt, bool IsALeakCheck = false) const; + Optional getCheckIfTracked(CheckerContext &C, SymbolRef Sym, bool IsALeakCheck = false) const; ///@} static bool SummarizeValue(raw_ostream &os, SVal V); static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); + template void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr) const; + void ReportFreeAlloca(CheckerContext &C, SVal ArgVal, SourceRange Range) const; + void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range, const Expr *DeallocExpr, const RefState *RS, SymbolRef Sym, bool OwnershipTransferred) const; + + template void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr, const Expr *AllocExpr = nullptr) const; + void ReportUseAfterFree(CheckerContext &C, SourceRange Range, SymbolRef Sym) const; + void ReportDoubleFree(CheckerContext &C, SourceRange Range, bool Released, SymbolRef Sym, SymbolRef PrevSym) const; @@ -651,6 +661,7 @@ void ReportUseZeroAllocated(CheckerContext &C, SourceRange Range, SymbolRef Sym) const; + template void ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *FreeExpr) const; @@ -1035,7 +1046,7 @@ // If M_ZERO is set, treat this like calloc (initialized). if (TrueState && !FalseState) { SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy); - return MallocMemAux(C, CE, CE->getArg(0), ZeroVal, TrueState); + return MallocMemAux(C, CE, CE->getArg(0), ZeroVal, TrueState); } return None; @@ -1074,11 +1085,13 @@ default: return; case 1: - State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), + State); State = ProcessZeroAllocCheck(C, CE, 0, State); break; case 2: - State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), + State); break; case 3: llvm::Optional MaybeState = @@ -1086,7 +1099,8 @@ if (MaybeState.hasValue()) State = MaybeState.getValue(); else - State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), + State); break; } } else if (FunI == MemFunctionInfo.II_kmalloc) { @@ -1097,19 +1111,22 @@ if (MaybeState.hasValue()) State = MaybeState.getValue(); else - State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), + State); } else if (FunI == MemFunctionInfo.II_valloc) { if (CE->getNumArgs() < 1) return; - State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + State = + MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); State = ProcessZeroAllocCheck(C, CE, 0, State); } else if (FunI == MemFunctionInfo.II_realloc || FunI == MemFunctionInfo.II_g_realloc || FunI == MemFunctionInfo.II_g_try_realloc) { - State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State); + State = + ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State); State = ProcessZeroAllocCheck(C, CE, 1, State); } else if (FunI == MemFunctionInfo.II_reallocf) { - State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ true, State); + State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ true, State); State = ProcessZeroAllocCheck(C, CE, 1, State); } else if (FunI == MemFunctionInfo.II_calloc) { State = CallocMem(C, CE, State); @@ -1121,20 +1138,21 @@ if (suppressDeallocationsInSuspiciousContexts(CE, C)) return; - State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory); + State = FreeMemAux(C, CE, State, 0, false, + IsKnownToBeAllocatedMemory); } else if (FunI == MemFunctionInfo.II_strdup || FunI == MemFunctionInfo.II_win_strdup || FunI == MemFunctionInfo.II_wcsdup || FunI == MemFunctionInfo.II_win_wcsdup) { - State = MallocUpdateRefState(C, CE, State); + State = MallocUpdateRefState(C, CE, State); } else if (FunI == MemFunctionInfo.II_strndup) { - State = MallocUpdateRefState(C, CE, State); + State = MallocUpdateRefState(C, CE, State); } else if (FunI == MemFunctionInfo.II_alloca || FunI == MemFunctionInfo.II_win_alloca) { if (CE->getNumArgs() < 1) return; - State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, - AF_Alloca); + State = + MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); State = ProcessZeroAllocCheck(C, CE, 0, State); } else if (MemFunctionInfo.isStandardNewDelete(FD, C.getASTContext())) { // Process direct calls to operator new/new[]/delete/delete[] functions @@ -1143,18 +1161,22 @@ // CXXDeleteExpr. switch (FD->getOverloadedOperator()) { case OO_New: - State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, - AF_CXXNew); + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), + State); State = ProcessZeroAllocCheck(C, CE, 0, State); break; case OO_Array_New: - State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, - AF_CXXNewArray); + State = MallocMemAux(C, CE, CE->getArg(0), + UndefinedVal(), State); State = ProcessZeroAllocCheck(C, CE, 0, State); break; case OO_Delete: + State = FreeMemAux(C, CE, State, 0, false, + IsKnownToBeAllocatedMemory); + break; case OO_Array_Delete: - State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory); + State = FreeMemAux(C, CE, State, 0, false, + IsKnownToBeAllocatedMemory); break; default: llvm_unreachable("not a new/delete operator"); @@ -1162,22 +1184,24 @@ } else if (FunI == MemFunctionInfo.II_if_nameindex) { // Should we model this differently? We can allocate a fixed number of // elements with zeros in the last one. - State = MallocMemAux(C, CE, UnknownVal(), UnknownVal(), State, - AF_IfNameIndex); + State = MallocMemAux(C, CE, UnknownVal(), UnknownVal(), + State); } else if (FunI == MemFunctionInfo.II_if_freenameindex) { - State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory); + State = FreeMemAux(C, CE, State, 0, false, + IsKnownToBeAllocatedMemory); } else if (FunI == MemFunctionInfo.II_g_malloc0 || FunI == MemFunctionInfo.II_g_try_malloc0) { if (CE->getNumArgs() < 1) return; SValBuilder &svalBuilder = C.getSValBuilder(); SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); - State = MallocMemAux(C, CE, CE->getArg(0), zeroVal, State); + State = MallocMemAux(C, CE, CE->getArg(0), zeroVal, State); State = ProcessZeroAllocCheck(C, CE, 0, State); } else if (FunI == MemFunctionInfo.II_g_memdup) { if (CE->getNumArgs() < 2) return; - State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State); + State = + MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State); State = ProcessZeroAllocCheck(C, CE, 1, State); } else if (FunI == MemFunctionInfo.II_g_malloc_n || FunI == MemFunctionInfo.II_g_try_malloc_n || @@ -1192,15 +1216,15 @@ Init = SB.makeZeroVal(SB.getContext().CharTy); } SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); - State = MallocMemAux(C, CE, TotalSize, Init, State); + State = MallocMemAux(C, CE, TotalSize, Init, State); State = ProcessZeroAllocCheck(C, CE, 0, State); State = ProcessZeroAllocCheck(C, CE, 1, State); } else if (FunI == MemFunctionInfo.II_g_realloc_n || FunI == MemFunctionInfo.II_g_try_realloc_n) { if (CE->getNumArgs() < 3) return; - State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State, - /*SuffixWithN*/ true); + State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State, + /*SuffixWithN*/ true); State = ProcessZeroAllocCheck(C, CE, 1, State); State = ProcessZeroAllocCheck(C, CE, 2, State); } @@ -1330,9 +1354,9 @@ return false; } +template void MallocChecker::processNewAllocation(const CXXNewExpr *NE, - CheckerContext &C, - SVal Target) const { + CheckerContext &C, SVal Target) const { if (!MemFunctionInfo.isStandardNewDelete(NE->getOperatorNew(), C.getASTContext())) return; @@ -1351,8 +1375,7 @@ // value (if any) and we don't want to loose this value. So we call // MallocUpdateRefState() instead of MallocMemAux() which breaks the // existing binding. - State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray - : AF_CXXNew, Target); + State = MallocUpdateRefState(C, NE, State, Target); State = addExtentSize(C, NE, State, Target); State = ProcessZeroAllocCheck(C, NE, 0, State, Target); C.addTransition(State); @@ -1360,14 +1383,22 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const { - if (!C.getAnalysisManager().getAnalyzerOptions().MayInlineCXXAllocator) - processNewAllocation(NE, C, C.getSVal(NE)); + if (!C.getAnalysisManager().getAnalyzerOptions().MayInlineCXXAllocator) { + if (NE->isArray()) + processNewAllocation(NE, C, C.getSVal(NE)); + else + processNewAllocation(NE, C, C.getSVal(NE)); + } } void MallocChecker::checkNewAllocator(const CXXNewExpr *NE, SVal Target, CheckerContext &C) const { - if (!C.wasInlined) - processNewAllocation(NE, C, Target); + if (!C.wasInlined) { + if (NE->isArray()) + processNewAllocation(NE, C, Target); + else + processNewAllocation(NE, C, Target); + } } // Sets the extent value of the MemRegion allocated by @@ -1428,8 +1459,12 @@ ProgramStateRef State = C.getState(); bool IsKnownToBeAllocated; - State = FreeMemAux(C, DE->getArgument(), DE, State, - /*Hold*/ false, IsKnownToBeAllocated); + if (DE->isArrayForm()) + State = FreeMemAux(C, DE->getArgument(), DE, State, + /*Hold*/ false, IsKnownToBeAllocated); + else + State = FreeMemAux(C, DE->getArgument(), DE, State, + /*Hold*/ false, IsKnownToBeAllocated); C.addTransition(State); } @@ -1470,10 +1505,10 @@ return; bool IsKnownToBeAllocatedMemory; - ProgramStateRef State = - FreeMemAux(C, Call.getArgExpr(0), Call.getOriginExpr(), C.getState(), - /*Hold=*/true, IsKnownToBeAllocatedMemory, - /*RetNullOnFailure=*/true); + ProgramStateRef State = FreeMemAux( + C, Call.getArgExpr(0), Call.getOriginExpr(), C.getState(), + /*Hold=*/true, IsKnownToBeAllocatedMemory, + /*RetNullOnFailure=*/true); C.addTransition(State); } @@ -1490,28 +1525,27 @@ OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); if (I != E) { - return MallocMemAux(C, CE, CE->getArg(I->getASTIndex()), UndefinedVal(), - State); + return MallocMemAux(C, CE, CE->getArg(I->getASTIndex()), + UndefinedVal(), State); } - return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State); + return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State); } +template ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, const CallExpr *CE, const Expr *SizeEx, SVal Init, - ProgramStateRef State, - AllocationFamily Family) { + ProgramStateRef State) { if (!State) return nullptr; - return MallocMemAux(C, CE, C.getSVal(SizeEx), Init, State, Family); + return MallocMemAux(C, CE, C.getSVal(SizeEx), Init, State); } +template ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, - const CallExpr *CE, - SVal Size, SVal Init, - ProgramStateRef State, - AllocationFamily Family) { + const CallExpr *CE, SVal Size, + SVal Init, ProgramStateRef State) { if (!State) return nullptr; @@ -1548,12 +1582,12 @@ assert(State); } - return MallocUpdateRefState(C, CE, State, Family); + return MallocUpdateRefState(C, CE, State); } +template static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, - AllocationFamily Family, Optional RetVal) { if (!State) return nullptr; @@ -1588,7 +1622,7 @@ bool IsKnownToBeAllocated = false; for (const auto &Arg : Att->args()) { - ProgramStateRef StateI = FreeMemAux( + ProgramStateRef StateI = FreeMemAux( C, CE, State, Arg.getASTIndex(), Att->getOwnKind() == OwnershipAttr::Holds, IsKnownToBeAllocated); if (StateI) @@ -1597,6 +1631,7 @@ return State; } +template ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, ProgramStateRef State, unsigned Num, bool Hold, bool &IsKnownToBeAllocated, @@ -1607,8 +1642,8 @@ if (CE->getNumArgs() < (Num + 1)) return nullptr; - return FreeMemAux(C, CE->getArg(Num), CE, State, Hold, IsKnownToBeAllocated, - ReturnsNullOnFailure); + return FreeMemAux(C, CE->getArg(Num), CE, State, Hold, + IsKnownToBeAllocated, ReturnsNullOnFailure); } /// Checks if the previous call to free on the given symbol failed - if free @@ -1626,58 +1661,7 @@ return false; } -static AllocationFamily -getAllocationFamily(const MemFunctionInfoTy &MemFunctionInfo, CheckerContext &C, - const Stmt *S) { - - if (!S) - return AF_None; - - if (const CallExpr *CE = dyn_cast(S)) { - const FunctionDecl *FD = C.getCalleeDecl(CE); - - if (!FD) - FD = dyn_cast(CE->getCalleeDecl()); - - ASTContext &Ctx = C.getASTContext(); - - if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Malloc, - MemoryOperationKind::MOK_Any)) - return AF_Malloc; - - if (MemFunctionInfo.isStandardNewDelete(FD, Ctx)) { - OverloadedOperatorKind Kind = FD->getOverloadedOperator(); - if (Kind == OO_New || Kind == OO_Delete) - return AF_CXXNew; - else if (Kind == OO_Array_New || Kind == OO_Array_Delete) - return AF_CXXNewArray; - } - - if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_IfNameIndex, - MemoryOperationKind::MOK_Any)) - return AF_IfNameIndex; - - if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Alloca, - MemoryOperationKind::MOK_Any)) - return AF_Alloca; - - return AF_None; - } - - if (const CXXNewExpr *NE = dyn_cast(S)) - return NE->isArray() ? AF_CXXNewArray : AF_CXXNew; - - if (const CXXDeleteExpr *DE = dyn_cast(S)) - return DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew; - - if (isa(S)) - return AF_Malloc; - - return AF_None; -} - -static bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, - const Expr *E) { +static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E) { if (const CallExpr *CE = dyn_cast(E)) { // FIXME: This doesn't handle indirect calls. const FunctionDecl *FD = CE->getDirectCallee(); @@ -1716,10 +1700,7 @@ return false; } -static void printExpectedAllocName(raw_ostream &os, - const MemFunctionInfoTy &MemFunctionInfo, - CheckerContext &C, const Expr *E) { - AllocationFamily Family = getAllocationFamily(MemFunctionInfo, C, E); +static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family) { switch(Family) { case AF_Malloc: os << "malloc()"; return; @@ -1744,12 +1725,12 @@ } } -ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, - const Expr *ArgExpr, - const Expr *ParentExpr, - ProgramStateRef State, bool Hold, - bool &IsKnownToBeAllocated, - bool ReturnsNullOnFailure) const { +template +ProgramStateRef +MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr, + const Expr *ParentExpr, ProgramStateRef State, + bool Hold, bool &IsKnownToBeAllocated, + bool ReturnsNullOnFailure) const { if (!State) return nullptr; @@ -1779,7 +1760,7 @@ // Nonlocs can't be freed, of course. // Non-region locations (labels and fixed addresses) also shouldn't be freed. if (!R) { - ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); return nullptr; } @@ -1787,7 +1768,7 @@ // Blocks might show up as heap data, but should not be free()d if (isa(R)) { - ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); return nullptr; } @@ -1807,7 +1788,7 @@ if (isa(R)) ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange()); else - ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); return nullptr; } @@ -1846,9 +1827,7 @@ RsBase->isEscaped()) { // Check if an expected deallocation function matches the real one. - bool DeallocMatchesAlloc = - RsBase->getAllocationFamily() == - getAllocationFamily(MemFunctionInfo, C, ParentExpr); + bool DeallocMatchesAlloc = RsBase->getAllocationFamily() == Family; if (!DeallocMatchesAlloc) { ReportMismatchedDealloc(C, ArgExpr->getSourceRange(), ParentExpr, RsBase, SymBase, Hold); @@ -1862,15 +1841,16 @@ !Offset.hasSymbolicOffset() && Offset.getOffset() != 0) { const Expr *AllocExpr = cast(RsBase->getStmt()); - ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, - AllocExpr); + ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), + ParentExpr, AllocExpr); return nullptr; } } } if (SymBase->getType()->isFunctionPointerType()) { - ReportFunctionPointerFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); + ReportFunctionPointerFree(C, ArgVal, ArgExpr->getSourceRange(), + ParentExpr); return nullptr; } @@ -1888,9 +1868,12 @@ } } - AllocationFamily Family = - RsBase ? RsBase->getAllocationFamily() - : getAllocationFamily(MemFunctionInfo, C, ParentExpr); + // If we don't know anything about this symbol, a free on it may be totally + // valid. If this is the case, lets assume that the allocation family of the + // freeing function is the same as the symbols allocation family, and go with + // that. + assert(!RsBase || (RsBase && RsBase->getAllocationFamily() == Family)); + // Normal free. if (Hold) return State->set(SymBase, @@ -1936,12 +1919,10 @@ llvm_unreachable("unhandled family"); } -Optional -MallocChecker::getCheckIfTracked(CheckerContext &C, - const Stmt *AllocDeallocStmt, - bool IsALeakCheck) const { - return getCheckIfTracked( - getAllocationFamily(MemFunctionInfo, C, AllocDeallocStmt), IsALeakCheck); +template +Optional MallocChecker::getCheckIfTracked( + CheckerContext &C, const Stmt *AllocDeallocStmt, bool IsALeakCheck) const { + return getCheckIfTracked(Family, IsALeakCheck); } Optional @@ -2042,6 +2023,7 @@ } } +template void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr) const { @@ -2051,7 +2033,7 @@ return; Optional CheckKind = - getCheckIfTracked(C, DeallocExpr); + getCheckIfTracked(C, DeallocExpr); if (!CheckKind.hasValue()) return; @@ -2068,7 +2050,7 @@ MR = ER->getSuperRegion(); os << "Argument to "; - if (!printAllocDeallocName(os, C, DeallocExpr)) + if (!printMemFnName(os, C, DeallocExpr)) os << "deallocator"; os << " is "; @@ -2079,7 +2061,7 @@ else os << "not memory allocated by "; - printExpectedAllocName(os, MemFunctionInfo, C, DeallocExpr); + printExpectedAllocName(os, Family); auto R = std::make_unique(*BT_BadFree[*CheckKind], os.str(), N); @@ -2141,25 +2123,25 @@ llvm::raw_svector_ostream DeallocOs(DeallocBuf); if (OwnershipTransferred) { - if (printAllocDeallocName(DeallocOs, C, DeallocExpr)) + if (printMemFnName(DeallocOs, C, DeallocExpr)) os << DeallocOs.str() << " cannot"; else os << "Cannot"; os << " take ownership of memory"; - if (printAllocDeallocName(AllocOs, C, AllocExpr)) + if (printMemFnName(AllocOs, C, AllocExpr)) os << " allocated by " << AllocOs.str(); } else { os << "Memory"; - if (printAllocDeallocName(AllocOs, C, AllocExpr)) + if (printMemFnName(AllocOs, C, AllocExpr)) os << " allocated by " << AllocOs.str(); os << " should be deallocated by "; printExpectedDeallocName(os, RS->getAllocationFamily()); - if (printAllocDeallocName(DeallocOs, C, DeallocExpr)) - os << ", not " << DeallocOs.str(); + if (printMemFnName(DeallocOs, C, DeallocExpr)) + os << ", not " << DeallocOs.str(); } auto R = std::make_unique(*BT_MismatchedDealloc, @@ -2171,17 +2153,17 @@ } } +template void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr, const Expr *AllocExpr) const { - if (!ChecksEnabled[CK_MallocChecker] && !ChecksEnabled[CK_NewDeleteChecker]) return; Optional CheckKind = - getCheckIfTracked(C, AllocExpr); + getCheckIfTracked(C, AllocExpr); if (!CheckKind.hasValue()) return; @@ -2210,14 +2192,14 @@ int offsetBytes = Offset.getOffset() / C.getASTContext().getCharWidth(); os << "Argument to "; - if (!printAllocDeallocName(os, C, DeallocExpr)) + if (!printMemFnName(os, C, DeallocExpr)) os << "deallocator"; os << " is offset by " << offsetBytes << " " << ((abs(offsetBytes) > 1) ? "bytes" : "byte") << " from the start of "; - if (AllocExpr && printAllocDeallocName(AllocNameOs, C, AllocExpr)) + if (AllocExpr && printMemFnName(AllocNameOs, C, AllocExpr)) os << "memory allocated by " << AllocNameOs.str(); else os << "allocated memory"; @@ -2353,13 +2335,15 @@ } } +template void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *FreeExpr) const { if (!ChecksEnabled[CK_MallocChecker]) return; - Optional CheckKind = getCheckIfTracked(C, FreeExpr); + Optional CheckKind = + getCheckIfTracked(C, FreeExpr); if (!CheckKind.hasValue()) return; @@ -2376,7 +2360,7 @@ MR = ER->getSuperRegion(); Os << "Argument to "; - if (!printAllocDeallocName(Os, C, FreeExpr)) + if (!printMemFnName(Os, C, FreeExpr)) Os << "deallocator"; Os << " is a function pointer"; @@ -2389,11 +2373,11 @@ } } -ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C, - const CallExpr *CE, - bool ShouldFreeOnFail, - ProgramStateRef State, - bool SuffixWithN) const { +template +ProgramStateRef +MallocChecker::ReallocMemAux(CheckerContext &C, const CallExpr *CE, + bool ShouldFreeOnFail, ProgramStateRef State, + bool SuffixWithN) const { if (!State) return nullptr; @@ -2440,8 +2424,8 @@ // If the ptr is NULL and the size is not 0, the call is equivalent to // malloc(size). if (PrtIsNull && !SizeIsZero) { - ProgramStateRef stateMalloc = MallocMemAux(C, CE, TotalSize, - UndefinedVal(), StatePtrIsNull); + ProgramStateRef stateMalloc = + MallocMemAux(C, CE, TotalSize, UndefinedVal(), StatePtrIsNull); return stateMalloc; } @@ -2464,16 +2448,16 @@ // If size was equal to 0, either NULL or a pointer suitable to be passed // to free() is returned. We just free the input pointer and do not add // any constrains on the output pointer. - if (ProgramStateRef stateFree = - FreeMemAux(C, CE, StateSizeIsZero, 0, false, IsKnownToBeAllocated)) + if (ProgramStateRef stateFree = FreeMemAux( + C, CE, StateSizeIsZero, 0, false, IsKnownToBeAllocated)) return stateFree; // Default behavior. if (ProgramStateRef stateFree = - FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocated)) { + FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocated)) { - ProgramStateRef stateRealloc = MallocMemAux(C, CE, TotalSize, - UnknownVal(), stateFree); + ProgramStateRef stateRealloc = + MallocMemAux(C, CE, TotalSize, UnknownVal(), stateFree); if (!stateRealloc) return nullptr; @@ -2506,7 +2490,7 @@ SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); - return MallocMemAux(C, CE, TotalSize, zeroVal, State); + return MallocMemAux(C, CE, TotalSize, zeroVal, State); } MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N,