diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -47,7 +47,10 @@ #include "AllocationState.h" #include "InterCheckerAPI.h" #include "clang/AST/Attr.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" #include "clang/AST/ParentMap.h" +#include "clang/Basic/LLVM.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Lexer.h" @@ -63,10 +66,12 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include #include @@ -268,7 +273,7 @@ /// placement operators and other standard overloads. static bool isStandardNewDelete(const FunctionDecl *FD); static bool isStandardNewDelete(const CallEvent &Call) { - if (!Call.getDecl()) + if (!Call.getDecl() || !isa(Call.getDecl())) return false; return isStandardNewDelete(cast(Call.getDecl())); } @@ -283,9 +288,8 @@ : public Checker, check::EndFunction, check::PreCall, check::PostCall, - check::PostStmt, check::NewAllocator, - check::PostStmt, check::PostObjCMessage, - check::Location, eval::Assume> { + check::NewAllocator, check::PostStmt, + check::PostObjCMessage, check::Location, eval::Assume> { public: /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. @@ -314,8 +318,6 @@ void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; - void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const; - void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; void checkNewAllocator(const CXXAllocatorCall &Call, CheckerContext &C) const; void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const; void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; @@ -351,7 +353,7 @@ mutable std::unique_ptr BT_UseZerroAllocated[CK_NumCheckKinds]; #define CHECK_FN(NAME) \ - void NAME(CheckerContext &C, const CallExpr *CE, ProgramStateRef State) const; + void NAME(const CallEvent &Call, CheckerContext &C) const; CHECK_FN(checkFree) CHECK_FN(checkIfNameIndex) @@ -369,12 +371,11 @@ CHECK_FN(checkReallocN) CHECK_FN(checkOwnershipAttr) - void checkRealloc(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State, bool ShouldFreeOnFail) const; + void checkRealloc(const CallEvent &Call, CheckerContext &C, + bool ShouldFreeOnFail) const; - using CheckFn = - std::function; + using CheckFn = std::function; const CallDescriptionMap FreeingMemFnMap{ {{"free", 1}, &MallocChecker::checkFree}, @@ -412,13 +413,13 @@ CallDescriptionMap ReallocatingMemFnMap{ {{"realloc", 2}, - std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)}, + std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)}, {{"reallocf", 2}, - std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, true)}, + std::bind(&MallocChecker::checkRealloc, _1, _2, _3, true)}, {{"g_realloc", 2}, - std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)}, + std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)}, {{"g_try_realloc", 2}, - std::bind(&MallocChecker::checkRealloc, _1, _2, _3, _4, false)}, + std::bind(&MallocChecker::checkRealloc, _1, _2, _3, false)}, {{"g_realloc_n", 3}, &MallocChecker::checkReallocN}, {{"g_try_realloc_n", 3}, &MallocChecker::checkReallocN}, }; @@ -437,8 +438,10 @@ /// Process C++ operator new()'s allocation, which is the part of C++ /// new-expression that goes before the constructor. - void processNewAllocation(const CXXNewExpr *NE, CheckerContext &C, - SVal Target, AllocationFamily Family) const; + LLVM_NODISCARD + ProgramStateRef processNewAllocation(const CXXAllocatorCall &Call, + CheckerContext &C, + AllocationFamily Family) const; /// Perform a zero-allocation check. /// @@ -448,7 +451,8 @@ /// 0. /// \param [in] RetVal Specifies the newly allocated pointer value; /// if unspecified, the value of expression \p E is used. - static ProgramStateRef ProcessZeroAllocCheck(CheckerContext &C, const Expr *E, + LLVM_NODISCARD + static ProgramStateRef ProcessZeroAllocCheck(const CallEvent &Call, const unsigned IndexOfSizeArg, ProgramStateRef State, Optional RetVal = None); @@ -469,9 +473,9 @@ /// \param [in] Att The ownership_returns attribute. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. - ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, - const CallExpr *CE, - const OwnershipAttr* Att, + LLVM_NODISCARD + ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, + const OwnershipAttr *Att, ProgramStateRef State) const; /// Models memory allocation. @@ -483,7 +487,8 @@ /// malloc leaves it undefined. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. - static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, + LLVM_NODISCARD + static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call, const Expr *SizeEx, SVal Init, ProgramStateRef State, AllocationFamily Family); @@ -497,18 +502,21 @@ /// malloc leaves it undefined. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. - static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, + LLVM_NODISCARD + static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call, SVal Size, SVal Init, ProgramStateRef State, AllocationFamily Family); + LLVM_NODISCARD static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE, ProgramStateRef State, SVal Target); // Check if this malloc() for special flags. At present that means M_ZERO or // __GFP_ZERO (in which case, treat it like calloc). + LLVM_NODISCARD llvm::Optional - performKernelMalloc(const CallExpr *CE, CheckerContext &C, + performKernelMalloc(const CallEvent &Call, CheckerContext &C, const ProgramStateRef &State) const; /// Model functions with the ownership_takes and ownership_holds attributes. @@ -528,8 +536,9 @@ /// \param [in] Att The ownership_takes or ownership_holds attribute. /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after deallocation. - ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE, - const OwnershipAttr* Att, + LLVM_NODISCARD + ProgramStateRef FreeMemAttr(CheckerContext &C, const CallEvent &Call, + const OwnershipAttr *Att, ProgramStateRef State) const; /// Models memory deallocation. @@ -551,7 +560,8 @@ /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function /// we're modeling returns with Null on failure. /// \returns The ProgramState right after deallocation. - ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE, + LLVM_NODISCARD + ProgramStateRef FreeMemAux(CheckerContext &C, const CallEvent &Call, ProgramStateRef State, unsigned Num, bool Hold, bool &IsKnownToBeAllocated, AllocationFamily Family, @@ -576,8 +586,9 @@ /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function /// we're modeling returns with Null on failure. /// \returns The ProgramState right after deallocation. + LLVM_NODISCARD ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *ArgExpr, - const Expr *ParentExpr, ProgramStateRef State, + const CallEvent &Call, ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated, AllocationFamily Family, bool ReturnsNullOnFailure = false) const; @@ -595,7 +606,8 @@ /// \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. - ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE, + LLVM_NODISCARD + ProgramStateRef ReallocMemAux(CheckerContext &C, const CallEvent &Call, bool ShouldFreeOnFail, ProgramStateRef State, AllocationFamily Family, bool SuffixWithN = false) const; @@ -605,6 +617,7 @@ /// \param [in] Blocks The amount of blocks that needs to be allocated. /// \param [in] BlockBytes The size of a block. /// \returns The symbolic value of \p Blocks * \p BlockBytes. + LLVM_NODISCARD static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, const Expr *BlockBytes); @@ -613,12 +626,13 @@ /// \param [in] CE The expression that reallocated memory /// \param [in] State The \c ProgramState right before reallocation. /// \returns The ProgramState right after allocation. - static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE, + LLVM_NODISCARD + static ProgramStateRef CallocMem(CheckerContext &C, const CallEvent &Call, ProgramStateRef State); /// See if deallocation happens in a suspicious context. If so, escape the /// pointers that otherwise would have been deallocated and return true. - bool suppressDeallocationsInSuspiciousContexts(const CallExpr *CE, + bool suppressDeallocationsInSuspiciousContexts(const CallEvent &Call, CheckerContext &C) const; /// If in \p S \p Sym is used, check whether \p Sym was already freed. @@ -647,6 +661,7 @@ SymbolRef &EscapingSymbol) const; /// Implementation of the checkPointerEscape callbacks. + LLVM_NODISCARD ProgramStateRef checkPointerEscapeAux(ProgramStateRef State, const InvalidatedSymbols &Escaped, const CallEvent *Call, @@ -909,7 +924,7 @@ } llvm::Optional -MallocChecker::performKernelMalloc(const CallExpr *CE, CheckerContext &C, +MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, const ProgramStateRef &State) const { // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels: // @@ -952,10 +967,10 @@ // We treat the last argument as the flags argument, and callers fall-back to // normal malloc on a None return. This works for the FreeBSD kernel malloc // as well as Linux kmalloc. - if (CE->getNumArgs() < 2) + if (Call.getNumArgs() < 2) return None; - const Expr *FlagsEx = CE->getArg(CE->getNumArgs() - 1); + const Expr *FlagsEx = Call.getArgExpr(Call.getNumArgs() - 1); const SVal V = C.getSVal(FlagsEx); if (!V.getAs()) { // The case where 'V' can be a location can only be due to a bad header, @@ -981,7 +996,8 @@ // 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, AF_Malloc); + return MallocMemAux(C, Call, Call.getArgExpr(0), ZeroVal, TrueState, + AF_Malloc); } return None; @@ -998,110 +1014,128 @@ return TotalSize; } -void MallocChecker::checkBasicAlloc(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { - State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_Malloc); - State = ProcessZeroAllocCheck(C, CE, 0, State); +void MallocChecker::checkBasicAlloc(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State, + AF_Malloc); + State = ProcessZeroAllocCheck(Call, 0, State); C.addTransition(State); } -void MallocChecker::checkKernelMalloc(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { +void MallocChecker::checkKernelMalloc(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); llvm::Optional MaybeState = - performKernelMalloc(CE, C, State); + performKernelMalloc(Call, C, State); if (MaybeState.hasValue()) State = MaybeState.getValue(); else - State = - MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_Malloc); + State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State, + AF_Malloc); C.addTransition(State); } -void MallocChecker::checkRealloc(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State, +void MallocChecker::checkRealloc(const CallEvent &Call, + CheckerContext &C, bool ShouldFreeOnFail) const { - State = ReallocMemAux(C, CE, ShouldFreeOnFail, State, AF_Malloc); - State = ProcessZeroAllocCheck(C, CE, 1, State); + ProgramStateRef State = C.getState(); + State = ReallocMemAux(C, Call, ShouldFreeOnFail, State, AF_Malloc); + State = ProcessZeroAllocCheck(Call, 1, State); C.addTransition(State); } -void MallocChecker::checkCalloc(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { - State = CallocMem(C, CE, State); - State = ProcessZeroAllocCheck(C, CE, 0, State); - State = ProcessZeroAllocCheck(C, CE, 1, State); +void MallocChecker::checkCalloc(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + State = CallocMem(C, Call, State); + State = ProcessZeroAllocCheck(Call, 0, State); + State = ProcessZeroAllocCheck(Call, 1, State); C.addTransition(State); } -void MallocChecker::checkFree(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { +void MallocChecker::checkFree(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); bool IsKnownToBeAllocatedMemory = false; - if (suppressDeallocationsInSuspiciousContexts(CE, C)) + if (suppressDeallocationsInSuspiciousContexts(Call, C)) return; - State = - FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory, AF_Malloc); + State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory, + AF_Malloc); C.addTransition(State); } -void MallocChecker::checkAlloca(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { - State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_Alloca); - State = ProcessZeroAllocCheck(C, CE, 0, State); +void MallocChecker::checkAlloca(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State, + AF_Alloca); + State = ProcessZeroAllocCheck(Call, 0, State); C.addTransition(State); } -void MallocChecker::checkStrdup(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { +void MallocChecker::checkStrdup(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const auto *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) + return; State = MallocUpdateRefState(C, CE, State, AF_Malloc); C.addTransition(State); } -void MallocChecker::checkIfNameIndex(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { +void MallocChecker::checkIfNameIndex(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); // 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); + MallocMemAux(C, Call, UnknownVal(), UnknownVal(), State, AF_IfNameIndex); C.addTransition(State); } -void MallocChecker::checkIfFreeNameIndex(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { +void MallocChecker::checkIfFreeNameIndex(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); bool IsKnownToBeAllocatedMemory = false; - State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory, + State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory, AF_IfNameIndex); C.addTransition(State); } -void MallocChecker::checkCXXNewOrCXXDelete(CheckerContext &C, - const CallExpr *CE, - ProgramStateRef State) const { +void MallocChecker::checkCXXNewOrCXXDelete(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); bool IsKnownToBeAllocatedMemory = false; + const auto *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) + return; + + assert(isStandardNewDelete(Call)); - const FunctionDecl *FD = C.getCalleeDecl(CE); // Process direct calls to operator new/new[]/delete/delete[] functions // as distinct from new/new[]/delete/delete[] expressions that are // processed by the checkPostStmt callbacks for CXXNewExpr and // CXXDeleteExpr. + const FunctionDecl *FD = C.getCalleeDecl(CE); switch (FD->getOverloadedOperator()) { case OO_New: State = - MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_CXXNew); - State = ProcessZeroAllocCheck(C, CE, 0, State); + MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State, AF_CXXNew); + State = ProcessZeroAllocCheck(Call, 0, State); break; case OO_Array_New: - State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, + State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State, AF_CXXNewArray); - State = ProcessZeroAllocCheck(C, CE, 0, State); + State = ProcessZeroAllocCheck(Call, 0, State); break; case OO_Delete: - State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory, + State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory, AF_CXXNew); break; case OO_Array_Delete: - State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory, + State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory, AF_CXXNewArray); break; default: @@ -1111,54 +1145,64 @@ C.addTransition(State); } -void MallocChecker::checkGMalloc0(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { +void MallocChecker::checkGMalloc0(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); SValBuilder &svalBuilder = C.getSValBuilder(); SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); - State = MallocMemAux(C, CE, CE->getArg(0), zeroVal, State, AF_Malloc); - State = ProcessZeroAllocCheck(C, CE, 0, State); + State = MallocMemAux(C, Call, Call.getArgExpr(0), zeroVal, State, AF_Malloc); + State = ProcessZeroAllocCheck(Call, 0, State); C.addTransition(State); } -void MallocChecker::checkGMemdup(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { - State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State, AF_Malloc); - State = ProcessZeroAllocCheck(C, CE, 1, State); +void MallocChecker::checkGMemdup(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + State = MallocMemAux(C, Call, Call.getArgExpr(1), UndefinedVal(), State, + AF_Malloc); + State = ProcessZeroAllocCheck(Call, 1, State); C.addTransition(State); } -void MallocChecker::checkGMallocN(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { +void MallocChecker::checkGMallocN(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); SVal Init = UndefinedVal(); - SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); - State = MallocMemAux(C, CE, TotalSize, Init, State, AF_Malloc); - State = ProcessZeroAllocCheck(C, CE, 0, State); - State = ProcessZeroAllocCheck(C, CE, 1, State); + SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1)); + State = MallocMemAux(C, Call, TotalSize, Init, State, AF_Malloc); + State = ProcessZeroAllocCheck(Call, 0, State); + State = ProcessZeroAllocCheck(Call, 1, State); C.addTransition(State); } -void MallocChecker::checkGMallocN0(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { +void MallocChecker::checkGMallocN0(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); SValBuilder &SB = C.getSValBuilder(); SVal Init = SB.makeZeroVal(SB.getContext().CharTy); - SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); - State = MallocMemAux(C, CE, TotalSize, Init, State, AF_Malloc); - State = ProcessZeroAllocCheck(C, CE, 0, State); - State = ProcessZeroAllocCheck(C, CE, 1, State); + SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1)); + State = MallocMemAux(C, Call, TotalSize, Init, State, AF_Malloc); + State = ProcessZeroAllocCheck(Call, 0, State); + State = ProcessZeroAllocCheck(Call, 1, State); C.addTransition(State); } -void MallocChecker::checkReallocN(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { - State = ReallocMemAux(C, CE, /*ShouldFreeOnFail=*/false, State, AF_Malloc, +void MallocChecker::checkReallocN(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + State = ReallocMemAux(C, Call, /*ShouldFreeOnFail=*/false, State, AF_Malloc, /*SuffixWithN=*/true); - State = ProcessZeroAllocCheck(C, CE, 1, State); - State = ProcessZeroAllocCheck(C, CE, 2, State); + State = ProcessZeroAllocCheck(Call, 1, State); + State = ProcessZeroAllocCheck(Call, 2, State); C.addTransition(State); } -void MallocChecker::checkOwnershipAttr(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { +void MallocChecker::checkOwnershipAttr(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const auto *CE = dyn_cast_or_null(Call.getOriginExpr()); + if (!CE) + return; const FunctionDecl *FD = C.getCalleeDecl(CE); if (ShouldIncludeOwnershipAnnotatedFunctions || ChecksEnabled[CK_MismatchedDeallocatorChecker]) { @@ -1168,11 +1212,11 @@ for (const auto *I : FD->specific_attrs()) { switch (I->getOwnKind()) { case OwnershipAttr::Returns: - State = MallocMemReturnsAttr(C, CE, I, State); + State = MallocMemReturnsAttr(C, Call, I, State); break; case OwnershipAttr::Takes: case OwnershipAttr::Holds: - State = FreeMemAttr(C, CE, I, State); + State = FreeMemAttr(C, Call, I, State); break; } } @@ -1184,74 +1228,69 @@ CheckerContext &C) const { if (C.wasInlined) return; - - const auto *CE = dyn_cast_or_null(Call.getOriginExpr()); - if (!CE) - return; - - const FunctionDecl *FD = C.getCalleeDecl(CE); - if (!FD) + if (!Call.getOriginExpr()) return; ProgramStateRef State = C.getState(); if (const CheckFn *Callback = FreeingMemFnMap.lookup(Call)) { - (*Callback)(this, C, CE, State); + (*Callback)(this, Call, C); return; } if (const CheckFn *Callback = AllocatingMemFnMap.lookup(Call)) { - (*Callback)(this, C, CE, State); + (*Callback)(this, Call, C); return; } if (const CheckFn *Callback = ReallocatingMemFnMap.lookup(Call)) { - (*Callback)(this, C, CE, State); + (*Callback)(this, Call, C); return; } if (isStandardNewDelete(Call)) { - checkCXXNewOrCXXDelete(C, CE, State); + checkCXXNewOrCXXDelete(Call, C); return; } - checkOwnershipAttr(C, CE, State); + checkOwnershipAttr(Call, C); } // Performs a 0-sized allocations check. ProgramStateRef MallocChecker::ProcessZeroAllocCheck( - CheckerContext &C, const Expr *E, const unsigned IndexOfSizeArg, - ProgramStateRef State, Optional RetVal) { + const CallEvent &Call, const unsigned IndexOfSizeArg, ProgramStateRef State, + Optional RetVal) { if (!State) return nullptr; if (!RetVal) - RetVal = C.getSVal(E); + RetVal = Call.getReturnValue(); const Expr *Arg = nullptr; - if (const CallExpr *CE = dyn_cast(E)) { + if (const CallExpr *CE = dyn_cast(Call.getOriginExpr())) { Arg = CE->getArg(IndexOfSizeArg); - } - else if (const CXXNewExpr *NE = dyn_cast(E)) { - if (NE->isArray()) + } else if (const CXXNewExpr *NE = + dyn_cast(Call.getOriginExpr())) { + if (NE->isArray()) { Arg = *NE->getArraySize(); - else + } else { return State; - } - else + } + } else llvm_unreachable("not a CallExpr or CXXNewExpr"); assert(Arg); - Optional DefArgVal = C.getSVal(Arg).getAs(); + auto DefArgVal = + State->getSVal(Arg, Call.getLocationContext()).getAs(); if (!DefArgVal) return State; // Check if the allocation size is 0. ProgramStateRef TrueState, FalseState; - SValBuilder &SvalBuilder = C.getSValBuilder(); + SValBuilder &SvalBuilder = State->getStateManager().getSValBuilder(); DefinedSVal Zero = SvalBuilder.makeZeroVal(Arg->getType()).castAs(); @@ -1322,47 +1361,42 @@ return false; } -void MallocChecker::processNewAllocation(const CXXNewExpr *NE, - CheckerContext &C, SVal Target, - AllocationFamily Family) const { - if (!isStandardNewDelete(NE->getOperatorNew())) - return; +ProgramStateRef +MallocChecker::processNewAllocation(const CXXAllocatorCall &Call, + CheckerContext &C, + AllocationFamily Family) const { + if (!isStandardNewDelete(Call)) + return nullptr; + const CXXNewExpr *NE = Call.getOriginExpr(); const ParentMap &PM = C.getLocationContext()->getParentMap(); + ProgramStateRef State = C.getState(); // Non-trivial constructors have a chance to escape 'this', but marking all // invocations of trivial constructors as escaped would cause too great of // reduction of true positives, so let's just do that for constructors that // have an argument of a pointer-to-record type. if (!PM.isConsumedExpr(NE) && hasNonTrivialConstructorCall(NE)) - return; + return State; - ProgramStateRef State = C.getState(); // The return value from operator new is bound to a specified initialization // value (if any) and we don't want to loose this value. So we call // MallocUpdateRefState() instead of MallocMemAux() which breaks the // existing binding. + SVal Target = Call.getObjectUnderConstruction(); State = MallocUpdateRefState(C, NE, State, Family, Target); State = addExtentSize(C, NE, State, Target); - State = ProcessZeroAllocCheck(C, NE, 0, State, Target); - C.addTransition(State); -} - -void MallocChecker::checkPostStmt(const CXXNewExpr *NE, - CheckerContext &C) const { - if (!C.getAnalysisManager().getAnalyzerOptions().MayInlineCXXAllocator) { - if (NE->isArray()) - processNewAllocation(NE, C, C.getSVal(NE), - (NE->isArray() ? AF_CXXNewArray : AF_CXXNew)); - } + State = ProcessZeroAllocCheck(Call, 0, State, Target); + return State; } void MallocChecker::checkNewAllocator(const CXXAllocatorCall &Call, CheckerContext &C) const { if (!C.wasInlined) { - processNewAllocation( - Call.getOriginExpr(), C, Call.getObjectUnderConstruction(), + ProgramStateRef State = processNewAllocation( + Call, C, (Call.getOriginExpr()->isArray() ? AF_CXXNewArray : AF_CXXNew)); + C.addTransition(State); } } @@ -1452,7 +1486,7 @@ bool IsKnownToBeAllocatedMemory; ProgramStateRef State = - FreeMemAux(C, Call.getArgExpr(0), Call.getOriginExpr(), C.getState(), + FreeMemAux(C, Call.getArgExpr(0), Call, C.getState(), /*Hold=*/true, IsKnownToBeAllocatedMemory, AF_Malloc, /*RetNullOnFailure=*/true); @@ -1460,7 +1494,7 @@ } ProgramStateRef -MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, +MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, const OwnershipAttr *Att, ProgramStateRef State) const { if (!State) @@ -1471,30 +1505,33 @@ OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); if (I != E) { - return MallocMemAux(C, CE, CE->getArg(I->getASTIndex()), UndefinedVal(), - State, AF_Malloc); + return MallocMemAux(C, Call, Call.getArgExpr(I->getASTIndex()), + UndefinedVal(), State, AF_Malloc); } - return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State, AF_Malloc); + return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, AF_Malloc); } ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, - const CallExpr *CE, + const CallEvent &Call, const Expr *SizeEx, SVal Init, ProgramStateRef State, AllocationFamily Family) { if (!State) return nullptr; - return MallocMemAux(C, CE, C.getSVal(SizeEx), Init, State, Family); + assert(SizeEx); + return MallocMemAux(C, Call, C.getSVal(SizeEx), Init, State, Family); } ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, - const CallExpr *CE, SVal Size, + const CallEvent &Call, SVal Size, SVal Init, ProgramStateRef State, AllocationFamily Family) { if (!State) return nullptr; + const Expr *CE = Call.getOriginExpr(); + // We expect the malloc functions to return a pointer. if (!Loc::isLocType(CE->getType())) return nullptr; @@ -1556,7 +1593,7 @@ } ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, - const CallExpr *CE, + const CallEvent &Call, const OwnershipAttr *Att, ProgramStateRef State) const { if (!State) @@ -1569,7 +1606,7 @@ for (const auto &Arg : Att->args()) { ProgramStateRef StateI = - FreeMemAux(C, CE, State, Arg.getASTIndex(), + FreeMemAux(C, Call, State, Arg.getASTIndex(), Att->getOwnKind() == OwnershipAttr::Holds, IsKnownToBeAllocated, AF_Malloc); if (StateI) @@ -1578,7 +1615,8 @@ return State; } -ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, +ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, + const CallEvent &Call, ProgramStateRef State, unsigned Num, bool Hold, bool &IsKnownToBeAllocated, AllocationFamily Family, @@ -1586,11 +1624,11 @@ if (!State) return nullptr; - if (CE->getNumArgs() < (Num + 1)) + if (Call.getNumArgs() < (Num + 1)) return nullptr; - return FreeMemAux(C, CE->getArg(Num), CE, State, Hold, IsKnownToBeAllocated, - Family, ReturnsNullOnFailure); + return FreeMemAux(C, Call.getArgExpr(Num), Call, State, Hold, + IsKnownToBeAllocated, Family, ReturnsNullOnFailure); } /// Checks if the previous call to free on the given symbol failed - if free @@ -1673,7 +1711,7 @@ } ProgramStateRef MallocChecker::FreeMemAux( - CheckerContext &C, const Expr *ArgExpr, const Expr *ParentExpr, + CheckerContext &C, const Expr *ArgExpr, const CallEvent &Call, ProgramStateRef State, bool Hold, bool &IsKnownToBeAllocated, AllocationFamily Family, bool ReturnsNullOnFailure) const { @@ -1701,6 +1739,7 @@ return nullptr; const MemRegion *R = ArgVal.getAsRegion(); + const Expr *ParentExpr = Call.getOriginExpr(); // Nonlocs can't be freed, of course. // Non-region locations (labels and fixed addresses) also shouldn't be freed. @@ -2315,12 +2354,14 @@ } ProgramStateRef -MallocChecker::ReallocMemAux(CheckerContext &C, const CallExpr *CE, +MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call, bool ShouldFreeOnFail, ProgramStateRef State, AllocationFamily Family, bool SuffixWithN) const { if (!State) return nullptr; + const CallExpr *CE = cast(Call.getOriginExpr()); + if (SuffixWithN && CE->getNumArgs() < 3) return nullptr; else if (CE->getNumArgs() < 2) @@ -2364,8 +2405,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, Family); + ProgramStateRef stateMalloc = MallocMemAux( + C, Call, TotalSize, UndefinedVal(), StatePtrIsNull, Family); return stateMalloc; } @@ -2382,16 +2423,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, Family)) + if (ProgramStateRef stateFree = FreeMemAux( + C, Call, StateSizeIsZero, 0, false, IsKnownToBeAllocated, Family)) return stateFree; // Default behavior. if (ProgramStateRef stateFree = - FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocated, Family)) { + FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocated, Family)) { ProgramStateRef stateRealloc = - MallocMemAux(C, CE, TotalSize, UnknownVal(), stateFree, Family); + MallocMemAux(C, Call, TotalSize, UnknownVal(), stateFree, Family); if (!stateRealloc) return nullptr; @@ -2420,19 +2461,21 @@ return nullptr; } -ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE, +ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, + const CallEvent &Call, ProgramStateRef State) { if (!State) return nullptr; - if (CE->getNumArgs() < 2) + if (Call.getNumArgs() < 2) return nullptr; SValBuilder &svalBuilder = C.getSValBuilder(); SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); - SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); + SVal TotalSize = + evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1)); - return MallocMemAux(C, CE, TotalSize, zeroVal, State, AF_Malloc); + return MallocMemAux(C, Call, TotalSize, zeroVal, State, AF_Malloc); } MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N, @@ -2615,7 +2658,7 @@ ProgramStateRef State = C.getState(); bool IsKnownToBeAllocated; - State = FreeMemAux(C, DE->getArgument(), DE, State, + State = FreeMemAux(C, DE->getArgument(), Call, State, /*Hold*/ false, IsKnownToBeAllocated, (DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew)); @@ -2745,8 +2788,8 @@ } bool MallocChecker::suppressDeallocationsInSuspiciousContexts( - const CallExpr *CE, CheckerContext &C) const { - if (CE->getNumArgs() == 0) + const CallEvent &Call, CheckerContext &C) const { + if (Call.getNumArgs() == 0) return false; StringRef FunctionStr = ""; @@ -2764,7 +2807,7 @@ ProgramStateRef State = C.getState(); - for (const Expr *Arg : CE->arguments()) + for (const Expr *Arg : cast(Call.getOriginExpr())->arguments()) if (SymbolRef Sym = C.getSVal(Arg).getAsSymbol()) if (const RefState *RS = State->get(Sym)) State = State->set(Sym, RefState::getEscaped(RS));