Author: Kristóf Umann Date: 2020-05-20T02:03:31+02:00 New Revision: 392222dd72657244f13d7f99cc6a497cc78eba2e
URL: https://github.com/llvm/llvm-project/commit/392222dd72657244f13d7f99cc6a497cc78eba2e DIFF: https://github.com/llvm/llvm-project/commit/392222dd72657244f13d7f99cc6a497cc78eba2e.diff LOG: [analyzer][NFC][MallocChecker] Convert many parameters into CallEvent Exactly what it says on the tin! This is clearly not the end of the road in this direction, the parameters could be merged far more with the use of CallEvent or a better value type in the CallDescriptionMap, but this was shockingly difficult enough on its own. I expect that simplifying the file further will be far easier moving forward. The end goal is to research how we could create a more mature checker interaction infrastructure for more complicated C++ modeling, and I'm pretty sure that being able successfully split up our giants is the first step in this direction. Differential Revision: https://reviews.llvm.org/D75432 Added: Modified: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp Removed: ################################################################################ diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index edcee6e2d052..f5f4dd0eaea5 100644 --- 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 <climits> #include <functional> @@ -268,7 +273,7 @@ REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair) /// 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<FunctionDecl>(Call.getDecl())) return false; return isStandardNewDelete(cast<FunctionDecl>(Call.getDecl())); } @@ -283,9 +288,8 @@ class MallocChecker : public Checker<check::DeadSymbols, check::PointerEscape, check::ConstPointerEscape, check::PreStmt<ReturnStmt>, check::EndFunction, check::PreCall, check::PostCall, - check::PostStmt<CXXNewExpr>, check::NewAllocator, - check::PostStmt<BlockExpr>, check::PostObjCMessage, - check::Location, eval::Assume> { + check::NewAllocator, check::PostStmt<BlockExpr>, + 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 @@ class MallocChecker 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 @@ class MallocChecker mutable std::unique_ptr<BugType> 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 @@ class MallocChecker 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<void(const MallocChecker *, CheckerContext &C, - const CallExpr *CE, ProgramStateRef State)>; + using CheckFn = std::function<void(const MallocChecker *, + const CallEvent &Call, CheckerContext &C)>; const CallDescriptionMap<CheckFn> FreeingMemFnMap{ {{"free", 1}, &MallocChecker::checkFree}, @@ -412,13 +413,13 @@ class MallocChecker CallDescriptionMap<CheckFn> 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 @@ class MallocChecker /// 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 @@ class MallocChecker /// 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<SVal> RetVal = None); @@ -469,9 +473,9 @@ class MallocChecker /// \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 @@ class MallocChecker /// 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 @@ class MallocChecker /// 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<ProgramStateRef> - 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 @@ class MallocChecker /// \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 @@ class MallocChecker /// \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 @@ class MallocChecker /// \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 @@ class MallocChecker /// \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 @@ class MallocChecker /// \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 @@ class MallocChecker /// \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 @@ class MallocChecker SymbolRef &EscapingSymbol) const; /// Implementation of the checkPointerEscape callbacks. + LLVM_NODISCARD ProgramStateRef checkPointerEscapeAux(ProgramStateRef State, const InvalidatedSymbols &Escaped, const CallEvent *Call, @@ -909,7 +924,7 @@ bool MallocChecker::isMemCall(const CallEvent &Call) const { } llvm::Optional<ProgramStateRef> -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 @@ MallocChecker::performKernelMalloc(const CallExpr *CE, CheckerContext &C, // 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<NonLoc>()) { // The case where 'V' can be a location can only be due to a bad header, @@ -981,7 +996,8 @@ MallocChecker::performKernelMalloc(const CallExpr *CE, CheckerContext &C, // 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 @@ SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, 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<ProgramStateRef> 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<CallExpr>(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 diff erently? 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<CallExpr>(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 @@ void MallocChecker::checkCXXNewOrCXXDelete(CheckerContext &C, 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<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; const FunctionDecl *FD = C.getCalleeDecl(CE); if (ShouldIncludeOwnershipAnnotatedFunctions || ChecksEnabled[CK_MismatchedDeallocatorChecker]) { @@ -1168,11 +1212,11 @@ void MallocChecker::checkOwnershipAttr(CheckerContext &C, const CallExpr *CE, for (const auto *I : FD->specific_attrs<OwnershipAttr>()) { 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 @@ void MallocChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { if (C.wasInlined) return; - - const auto *CE = dyn_cast_or_null<CallExpr>(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<SVal> RetVal) { + const CallEvent &Call, const unsigned IndexOfSizeArg, ProgramStateRef State, + Optional<SVal> RetVal) { if (!State) return nullptr; if (!RetVal) - RetVal = C.getSVal(E); + RetVal = Call.getReturnValue(); const Expr *Arg = nullptr; - if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { + if (const CallExpr *CE = dyn_cast<CallExpr>(Call.getOriginExpr())) { Arg = CE->getArg(IndexOfSizeArg); - } - else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) { - if (NE->isArray()) + } else if (const CXXNewExpr *NE = + dyn_cast<CXXNewExpr>(Call.getOriginExpr())) { + if (NE->isArray()) { Arg = *NE->getArraySize(); - else + } else { return State; - } - else + } + } else llvm_unreachable("not a CallExpr or CXXNewExpr"); assert(Arg); - Optional<DefinedSVal> DefArgVal = C.getSVal(Arg).getAs<DefinedSVal>(); + auto DefArgVal = + State->getSVal(Arg, Call.getLocationContext()).getAs<DefinedSVal>(); 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<DefinedSVal>(); @@ -1322,47 +1361,42 @@ static bool hasNonTrivialConstructorCall(const CXXNewExpr *NE) { 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 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, 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 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, } 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 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, 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 @@ static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, } ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, - const CallExpr *CE, + const CallEvent &Call, const OwnershipAttr *Att, ProgramStateRef State) const { if (!State) @@ -1569,7 +1606,7 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, 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 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, 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 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, 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 @@ static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) { } 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 @@ ProgramStateRef MallocChecker::FreeMemAux( 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 @@ void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal, } 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<CallExpr>(Call.getOriginExpr()); + if (SuffixWithN && CE->getNumArgs() < 3) return nullptr; else if (CE->getNumArgs() < 2) @@ -2364,8 +2405,8 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallExpr *CE, // 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 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallExpr *CE, // 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 @@ MallocChecker::ReallocMemAux(CheckerContext &C, const CallExpr *CE, 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 @@ void MallocChecker::checkPreCall(const CallEvent &Call, 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 @@ static bool isReleased(SymbolRef Sym, CheckerContext &C) { } 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 @@ bool MallocChecker::suppressDeallocationsInSuspiciousContexts( ProgramStateRef State = C.getState(); - for (const Expr *Arg : CE->arguments()) + for (const Expr *Arg : cast<CallExpr>(Call.getOriginExpr())->arguments()) if (SymbolRef Sym = C.getSVal(Arg).getAsSymbol()) if (const RefState *RS = State->get<RegionState>(Sym)) State = State->set<RegionState>(Sym, RefState::getEscaped(RS)); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits