Index: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ 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" @@ -62,10 +65,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 @@ -265,7 +270,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())); } @@ -280,9 +285,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. @@ -311,7 +315,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 checkNewAllocator(const CXXAllocatorCall &Call, CheckerContext &C) const; void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const; void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; @@ -347,11 +350,10 @@ 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; template - void checkRealloc(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const; + void checkRealloc(const CallEvent &Call, CheckerContext &C) const; CHECK_FN(checkBasicAlloc) CHECK_FN(checkKernelMalloc) @@ -369,8 +371,8 @@ CHECK_FN(checkReallocN) CHECK_FN(checkOwnershipAttr) - using CheckFn = void (MallocChecker::*)(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const; + using CheckFn = void (MallocChecker::*)(const CallEvent &Call, + CheckerContext &C) const; CallDescriptionMap FreeingMemFnMap{ {{"realloc", 2}, &MallocChecker::checkRealloc}, @@ -419,8 +421,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, ProgramStateRef State, + AllocationFamily Family) const; /// Perform a zero-allocation check. /// @@ -430,7 +434,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); @@ -451,9 +456,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. @@ -465,7 +470,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); @@ -479,18 +485,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. @@ -510,8 +519,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. @@ -533,7 +543,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, @@ -558,8 +569,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; @@ -577,7 +589,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; @@ -587,6 +600,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); @@ -595,12 +609,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. @@ -629,6 +644,7 @@ SymbolRef &EscapingSymbol) const; /// Implementation of the checkPointerEscape callbacks. + LLVM_NODISCARD ProgramStateRef checkPointerEscapeAux(ProgramStateRef State, const InvalidatedSymbols &Escaped, const CallEvent *Call, @@ -885,8 +901,9 @@ return Func && Func->hasAttr(); } -llvm::Optional MallocChecker::performKernelMalloc( - const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const { +llvm::Optional +MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, + const ProgramStateRef &State) const { // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels: // // void *malloc(unsigned long size, struct malloc_type *mtp, int flags); @@ -928,10 +945,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, @@ -957,7 +974,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; @@ -974,110 +992,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); } template -void MallocChecker::checkRealloc(CheckerContext &C, const CallExpr *CE, - ProgramStateRef State) const { - State = ReallocMemAux(C, CE, ShouldFreeOnFail, State, AF_Malloc); - State = ProcessZeroAllocCheck(C, CE, 1, State); +void MallocChecker::checkRealloc(const CallEvent &Call, + CheckerContext &C) const { + 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: @@ -1087,54 +1123,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]) { @@ -1144,11 +1190,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; } } @@ -1160,63 +1206,59 @@ 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)) - (this->**Callback)(C, CE, State); + (this->**Callback)(Call, C); if (const CheckFn *Callback = NonFreeingMemFnMap.lookup(Call)) - (this->**Callback)(C, CE, State); + (this->**Callback)(Call, C); - if (isStandardNewDelete(Call)) - checkCXXNewOrCXXDelete(C, CE, State); + if (isStandardNewDelete(Call)) { + checkCXXNewOrCXXDelete(Call, C); + } - 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 = Call.getSValBuilder(); DefinedSVal Zero = SvalBuilder.makeZeroVal(Arg->getType()).castAs(); @@ -1287,12 +1329,14 @@ 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, ProgramStateRef State, + AllocationFamily Family) const { + if (!isStandardNewDelete(Call)) + return nullptr; + const CXXNewExpr *NE = Call.getOriginExpr(); const ParentMap &PM = C.getLocationContext()->getParentMap(); // Non-trivial constructors have a chance to escape 'this', but marking all @@ -1300,34 +1344,26 @@ // 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(C.getState()); 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(C.getState()), + ProgramStateRef State = processNewAllocation( + Call, C, C.getState(), (Call.getOriginExpr()->isArray() ? AF_CXXNewArray : AF_CXXNew)); + C.addTransition(State); } } @@ -1417,7 +1453,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); @@ -1425,7 +1461,7 @@ } ProgramStateRef -MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, +MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, const OwnershipAttr *Att, ProgramStateRef State) const { if (!State) @@ -1436,30 +1472,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; @@ -1521,7 +1560,7 @@ } ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, - const CallExpr *CE, + const CallEvent &Call, const OwnershipAttr *Att, ProgramStateRef State) const { if (!State) @@ -1534,7 +1573,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) @@ -1543,7 +1582,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, @@ -1551,11 +1591,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 @@ -1638,7 +1678,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 { @@ -1666,6 +1706,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. @@ -2274,12 +2315,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) @@ -2323,8 +2366,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; } @@ -2347,16 +2390,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; @@ -2377,19 +2420,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, @@ -2572,7 +2617,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)); @@ -2702,8 +2747,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 = ""; @@ -2721,7 +2766,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));