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 @@ -214,16 +214,24 @@ REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState) +namespace { +struct StateAndPred { + ProgramStateRef State; + ExplodedNode *Pred; +}; +} // namespace + /// Check if the memory associated with this symbol was released. static bool isReleased(SymbolRef Sym, CheckerContext &C); /// Update the RefState to reflect the new memory allocation. /// The optional \p RetVal parameter specifies the newly allocated pointer /// value; if unspecified, the value of expression \p E is used. -static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, - ProgramStateRef State, - AllocationFamily Family, - Optional RetVal = None); +static StateAndPred MallocUpdateRefState(CheckerContext &C, const Expr *E, + ProgramStateRef State, + ExplodedNode *Pred, + AllocationFamily Family, + Optional RetVal = None); //===----------------------------------------------------------------------===// // The modeling of memory reallocation. @@ -455,9 +463,9 @@ /// Process C++ operator new()'s allocation, which is the part of C++ /// new-expression that goes before the constructor. LLVM_NODISCARD - ProgramStateRef processNewAllocation(const CXXAllocatorCall &Call, - CheckerContext &C, - AllocationFamily Family) const; + StateAndPred processNewAllocation(const CXXAllocatorCall &Call, + CheckerContext &C, + AllocationFamily Family) const; /// Perform a zero-allocation check. /// @@ -490,9 +498,10 @@ /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. LLVM_NODISCARD - ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, - const OwnershipAttr *Att, - ProgramStateRef State) const; + StateAndPred MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, + const OwnershipAttr *Att, + ProgramStateRef State, + ExplodedNode *Pred) const; /// Models memory allocation. /// @@ -504,10 +513,10 @@ /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. LLVM_NODISCARD - static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call, - const Expr *SizeEx, SVal Init, - ProgramStateRef State, - AllocationFamily Family); + static StateAndPred MallocMemAux(CheckerContext &C, const CallEvent &Call, + const Expr *SizeEx, SVal Init, + ProgramStateRef State, ExplodedNode *Pred, + AllocationFamily Family); /// Models memory allocation. /// @@ -519,17 +528,17 @@ /// \param [in] State The \c ProgramState right before allocation. /// \returns The ProgramState right after allocation. LLVM_NODISCARD - static ProgramStateRef MallocMemAux(CheckerContext &C, const CallEvent &Call, - SVal Size, SVal Init, - ProgramStateRef State, - AllocationFamily Family); + static StateAndPred MallocMemAux(CheckerContext &C, const CallEvent &Call, + SVal Size, SVal Init, ProgramStateRef State, + ExplodedNode *Pred, AllocationFamily Family); // 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 CallEvent &Call, CheckerContext &C, - const ProgramStateRef &State) const; + llvm::Optional performKernelMalloc(const CallEvent &Call, + CheckerContext &C, + ProgramStateRef State, + ExplodedNode *Pred) const; /// Model functions with the ownership_takes and ownership_holds attributes. /// @@ -619,10 +628,10 @@ /// has an '_n' suffix, such as g_realloc_n. /// \returns The ProgramState right after reallocation. LLVM_NODISCARD - ProgramStateRef ReallocMemAux(CheckerContext &C, const CallEvent &Call, - bool ShouldFreeOnFail, ProgramStateRef State, - AllocationFamily Family, - bool SuffixWithN = false) const; + StateAndPred ReallocMemAux(CheckerContext &C, const CallEvent &Call, + bool ShouldFreeOnFail, ProgramStateRef State, + ExplodedNode *Pred, AllocationFamily Family, + bool SuffixWithN = false) const; /// Evaluates the buffer size that needs to be allocated. /// @@ -639,8 +648,8 @@ /// \param [in] State The \c ProgramState right before reallocation. /// \returns The ProgramState right after allocation. LLVM_NODISCARD - static ProgramStateRef CallocMem(CheckerContext &C, const CallEvent &Call, - ProgramStateRef State); + static StateAndPred CallocMem(CheckerContext &C, const CallEvent &Call, + ProgramStateRef State, ExplodedNode *Pred); /// See if deallocation happens in a suspicious context. If so, escape the /// pointers that otherwise would have been deallocated and return true. @@ -1138,9 +1147,10 @@ return Func && Func->hasAttr(); } -llvm::Optional +llvm::Optional MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, - const ProgramStateRef &State) const { + ProgramStateRef State, + ExplodedNode *Pred) const { // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels: // // void *malloc(unsigned long size, struct malloc_type *mtp, int flags); @@ -1211,7 +1221,7 @@ // If M_ZERO is set, treat this like calloc (initialized). if (TrueState && !FalseState) { SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy); - return MallocMemAux(C, Call, Call.getArgExpr(0), ZeroVal, TrueState, + return MallocMemAux(C, Call, Call.getArgExpr(0), ZeroVal, TrueState, Pred, AF_Malloc); } @@ -1231,24 +1241,24 @@ 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); + StateAndPred Pair = {C.getState(), C.getPredecessor()}; + Pair = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), Pair.State, + Pair.Pred, AF_Malloc); + Pair.State = ProcessZeroAllocCheck(Call, 0, Pair.State); + C.addTransition(Pair.State, Pair.Pred); } void MallocChecker::checkKernelMalloc(const CallEvent &Call, CheckerContext &C) const { - ProgramStateRef State = C.getState(); - llvm::Optional MaybeState = - performKernelMalloc(Call, C, State); - if (MaybeState.hasValue()) - State = MaybeState.getValue(); + StateAndPred Pair = {C.getState(), C.getPredecessor()}; + llvm::Optional MaybePair = + performKernelMalloc(Call, C, Pair.State, Pair.Pred); + if (MaybePair.hasValue()) + Pair = MaybePair.getValue(); else - State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State, - AF_Malloc); - C.addTransition(State); + Pair = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), Pair.State, + Pair.Pred, AF_Malloc); + C.addTransition(Pair.State, Pair.Pred); } static bool isStandardRealloc(const CallEvent &Call) { @@ -1289,19 +1299,18 @@ // https://bugs.llvm.org/show_bug.cgi?id=46253 if (!isStandardRealloc(Call) && !isGRealloc(Call)) return; - ProgramStateRef State = C.getState(); - State = ReallocMemAux(C, Call, ShouldFreeOnFail, State, AF_Malloc); - State = ProcessZeroAllocCheck(Call, 1, State); - C.addTransition(State); + StateAndPred Pair = ReallocMemAux(C, Call, ShouldFreeOnFail, C.getState(), + C.getPredecessor(), AF_Malloc); + Pair.State = ProcessZeroAllocCheck(Call, 1, Pair.State); + C.addTransition(Pair.State, Pair.Pred); } 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); + StateAndPred Pair = CallocMem(C, Call, C.getState(), C.getPredecessor()); + Pair.State = ProcessZeroAllocCheck(Call, 0, Pair.State); + Pair.State = ProcessZeroAllocCheck(Call, 1, Pair.State); + C.addTransition(Pair.State, Pair.Pred); } void MallocChecker::checkFree(const CallEvent &Call, CheckerContext &C) const { @@ -1316,33 +1325,32 @@ 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); + StateAndPred Pair = {C.getState(), C.getPredecessor()}; + Pair = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), Pair.State, + Pair.Pred, AF_Alloca); + Pair.State = ProcessZeroAllocCheck(Call, 0, Pair.State); + C.addTransition(Pair.State, Pair.Pred); } 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); + StateAndPred Pair = + MallocUpdateRefState(C, CE, C.getState(), C.getPredecessor(), AF_Malloc); + C.addTransition(Pair.State, Pair.Pred); } void MallocChecker::checkIfNameIndex(const CallEvent &Call, CheckerContext &C) const { - ProgramStateRef State = C.getState(); + StateAndPred Pair = {C.getState(), C.getPredecessor()}; // Should we model this differently? We can allocate a fixed number of // elements with zeros in the last one. - State = - MallocMemAux(C, Call, UnknownVal(), UnknownVal(), State, AF_IfNameIndex); - - C.addTransition(State); + Pair = MallocMemAux(C, Call, UnknownVal(), UnknownVal(), Pair.State, + Pair.Pred, AF_IfNameIndex); + C.addTransition(Pair.State, Pair.Pred); } void MallocChecker::checkIfFreeNameIndex(const CallEvent &Call, @@ -1356,7 +1364,7 @@ void MallocChecker::checkCXXNewOrCXXDelete(const CallEvent &Call, CheckerContext &C) const { - ProgramStateRef State = C.getState(); + StateAndPred Pair = {C.getState(), C.getPredecessor()}; bool IsKnownToBeAllocatedMemory = false; const auto *CE = dyn_cast_or_null(Call.getOriginExpr()); if (!CE) @@ -1371,85 +1379,88 @@ const FunctionDecl *FD = C.getCalleeDecl(CE); switch (FD->getOverloadedOperator()) { case OO_New: - State = - MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State, AF_CXXNew); - State = ProcessZeroAllocCheck(Call, 0, State); + Pair = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), Pair.State, + Pair.Pred, AF_CXXNew); + Pair.State = ProcessZeroAllocCheck(Call, 0, Pair.State); break; case OO_Array_New: - State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State, - AF_CXXNewArray); - State = ProcessZeroAllocCheck(Call, 0, State); + Pair = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), Pair.State, + Pair.Pred, AF_CXXNewArray); + Pair.State = ProcessZeroAllocCheck(Call, 0, Pair.State); break; case OO_Delete: - State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory, - AF_CXXNew); + Pair.State = FreeMemAux(C, Call, Pair.State, 0, false, + IsKnownToBeAllocatedMemory, AF_CXXNew); break; case OO_Array_Delete: - State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory, - AF_CXXNewArray); + Pair.State = FreeMemAux(C, Call, Pair.State, 0, false, + IsKnownToBeAllocatedMemory, AF_CXXNewArray); break; default: llvm_unreachable("not a new/delete operator"); } - C.addTransition(State); + C.addTransition(Pair.State, Pair.Pred); } void MallocChecker::checkGMalloc0(const CallEvent &Call, CheckerContext &C) const { - ProgramStateRef State = C.getState(); + StateAndPred Pair = {C.getState(), C.getPredecessor()}; SValBuilder &svalBuilder = C.getSValBuilder(); SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); - State = MallocMemAux(C, Call, Call.getArgExpr(0), zeroVal, State, AF_Malloc); - State = ProcessZeroAllocCheck(Call, 0, State); - C.addTransition(State); + Pair = MallocMemAux(C, Call, Call.getArgExpr(0), zeroVal, Pair.State, + Pair.Pred, AF_Malloc); + Pair.State = ProcessZeroAllocCheck(Call, 0, Pair.State); + C.addTransition(Pair.State, Pair.Pred); } void MallocChecker::checkGMemdup(const CallEvent &Call, CheckerContext &C) const { - ProgramStateRef State = C.getState(); - State = - MallocMemAux(C, Call, Call.getArgExpr(1), UnknownVal(), State, AF_Malloc); - State = ProcessZeroAllocCheck(Call, 1, State); - C.addTransition(State); + StateAndPred Pair = {C.getState(), C.getPredecessor()}; + Pair = MallocMemAux(C, Call, Call.getArgExpr(1), UnknownVal(), Pair.State, + Pair.Pred, AF_Malloc); + Pair.State = ProcessZeroAllocCheck(Call, 1, Pair.State); + C.addTransition(Pair.State, Pair.Pred); } void MallocChecker::checkGMallocN(const CallEvent &Call, CheckerContext &C) const { - ProgramStateRef State = C.getState(); + StateAndPred Pair = {C.getState(), C.getPredecessor()}; SVal Init = UndefinedVal(); 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); + Pair = + MallocMemAux(C, Call, TotalSize, Init, Pair.State, Pair.Pred, AF_Malloc); + Pair.State = ProcessZeroAllocCheck(Call, 0, Pair.State); + Pair.State = ProcessZeroAllocCheck(Call, 1, Pair.State); + C.addTransition(Pair.State, Pair.Pred); } void MallocChecker::checkGMallocN0(const CallEvent &Call, CheckerContext &C) const { - ProgramStateRef State = C.getState(); + StateAndPred Pair = {C.getState(), C.getPredecessor()}; SValBuilder &SB = C.getSValBuilder(); SVal Init = SB.makeZeroVal(SB.getContext().CharTy); 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); + Pair = + MallocMemAux(C, Call, TotalSize, Init, Pair.State, Pair.Pred, AF_Malloc); + Pair.State = ProcessZeroAllocCheck(Call, 0, Pair.State); + Pair.State = ProcessZeroAllocCheck(Call, 1, Pair.State); + C.addTransition(Pair.State, Pair.Pred); } 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(Call, 1, State); - State = ProcessZeroAllocCheck(Call, 2, State); - C.addTransition(State); + StateAndPred Pair = ReallocMemAux(C, Call, /*ShouldFreeOnFail=*/false, + C.getState(), C.getPredecessor(), AF_Malloc, + /*SuffixWithN=*/true); + Pair.State = ProcessZeroAllocCheck(Call, 1, Pair.State); + Pair.State = ProcessZeroAllocCheck(Call, 2, Pair.State); + C.addTransition(Pair.State, Pair.Pred); } void MallocChecker::checkOwnershipAttr(const CallEvent &Call, CheckerContext &C) const { - ProgramStateRef State = C.getState(); + StateAndPred Pair = {C.getState(), C.getPredecessor()}; const auto *CE = dyn_cast_or_null(Call.getOriginExpr()); if (!CE) return; @@ -1464,16 +1475,16 @@ for (const auto *I : FD->specific_attrs()) { switch (I->getOwnKind()) { case OwnershipAttr::Returns: - State = MallocMemReturnsAttr(C, Call, I, State); + Pair = MallocMemReturnsAttr(C, Call, I, Pair.State, Pair.Pred); break; case OwnershipAttr::Takes: case OwnershipAttr::Holds: - State = FreeMemAttr(C, Call, I, State); + Pair.State = FreeMemAttr(C, Call, I, Pair.State); break; } } } - C.addTransition(State); + C.addTransition(Pair.State, Pair.Pred); } void MallocChecker::checkPostCall(const CallEvent &Call, @@ -1613,41 +1624,42 @@ return false; } -ProgramStateRef +StateAndPred MallocChecker::processNewAllocation(const CXXAllocatorCall &Call, CheckerContext &C, AllocationFamily Family) const { if (!isStandardNewDelete(Call)) - return nullptr; + return {nullptr, C.getPredecessor()}; const CXXNewExpr *NE = Call.getOriginExpr(); const ParentMap &PM = C.getLocationContext()->getParentMap(); - ProgramStateRef State = C.getState(); + StateAndPred Pair = {C.getState(), C.getPredecessor()}; // 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 State; + return Pair; // 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 = ProcessZeroAllocCheck(Call, 0, State, Target); - return State; + + Pair = MallocUpdateRefState(C, NE, Pair.State, Pair.Pred, Family, Target); + Pair.State = ProcessZeroAllocCheck(Call, 0, Pair.State, Target); + return Pair; } void MallocChecker::checkNewAllocator(const CXXAllocatorCall &Call, CheckerContext &C) const { if (!C.wasInlined) { - ProgramStateRef State = processNewAllocation( + StateAndPred Pair = processNewAllocation( Call, C, (Call.getOriginExpr()->isArray() ? AF_CXXNewArray : AF_CXXNew)); - C.addTransition(State); + C.addTransition(Pair.State, Pair.Pred); } } @@ -1698,48 +1710,49 @@ C.addTransition(State); } -ProgramStateRef -MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, - const OwnershipAttr *Att, - ProgramStateRef State) const { +StateAndPred MallocChecker::MallocMemReturnsAttr(CheckerContext &C, + const CallEvent &Call, + const OwnershipAttr *Att, + ProgramStateRef State, + ExplodedNode *Pred) const { if (!State) - return nullptr; + return {nullptr, Pred}; if (Att->getModule()->getName() != "malloc") - return nullptr; + return {nullptr, Pred}; OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); if (I != E) { return MallocMemAux(C, Call, Call.getArgExpr(I->getASTIndex()), - UndefinedVal(), State, AF_Malloc); + UndefinedVal(), State, Pred, AF_Malloc); } - return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, AF_Malloc); + return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, Pred, + AF_Malloc); } -ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, - const CallEvent &Call, - const Expr *SizeEx, SVal Init, - ProgramStateRef State, - AllocationFamily Family) { +StateAndPred MallocChecker::MallocMemAux( + CheckerContext &C, const CallEvent &Call, const Expr *SizeEx, SVal Init, + ProgramStateRef State, ExplodedNode *Pred, AllocationFamily Family) { if (!State) - return nullptr; + return {nullptr, Pred}; assert(SizeEx); - return MallocMemAux(C, Call, C.getSVal(SizeEx), Init, State, Family); + return MallocMemAux(C, Call, C.getSVal(SizeEx), Init, State, Pred, Family); } -ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, - const CallEvent &Call, SVal Size, - SVal Init, ProgramStateRef State, - AllocationFamily Family) { +StateAndPred MallocChecker::MallocMemAux(CheckerContext &C, + const CallEvent &Call, SVal Size, + SVal Init, ProgramStateRef State, + ExplodedNode *Pred, + AllocationFamily Family) { if (!State) - return nullptr; + return {nullptr, Pred}; const Expr *CE = Call.getOriginExpr(); // We expect the malloc functions to return a pointer. if (!Loc::isLocType(CE->getType())) - return nullptr; + return {nullptr, Pred}; // Bind the return value to the symbolic value from the heap region. // TODO: We could rewrite post visit to eval call; 'malloc' does not have @@ -1758,15 +1771,16 @@ State = setDynamicExtent(State, RetVal.getAsRegion(), Size.castAs(), svalBuilder); - return MallocUpdateRefState(C, CE, State, Family); + return MallocUpdateRefState(C, CE, State, Pred, Family); } -static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, - ProgramStateRef State, - AllocationFamily Family, - Optional RetVal) { +static StateAndPred MallocUpdateRefState(CheckerContext &C, const Expr *E, + ProgramStateRef State, + ExplodedNode *Pred, + AllocationFamily Family, + Optional RetVal) { if (!State) - return nullptr; + return {nullptr, Pred}; // Get the return value. if (!RetVal) @@ -1774,7 +1788,7 @@ // We expect the malloc functions to return a pointer. if (!RetVal->getAs()) - return nullptr; + return {nullptr, Pred}; SymbolRef Sym = RetVal->getAsLocSymbol(); // This is a return value of a function that was not inlined, such as malloc() @@ -1782,7 +1796,8 @@ assert(Sym); // Set the symbol's state to Allocated. - return State->set(Sym, RefState::getAllocated(Family, E)); + State = State->set(Sym, RefState::getAllocated(Family, E)); + return {State, Pred}; } ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, @@ -2571,24 +2586,25 @@ } } -ProgramStateRef +StateAndPred MallocChecker::ReallocMemAux(CheckerContext &C, const CallEvent &Call, bool ShouldFreeOnFail, ProgramStateRef State, - AllocationFamily Family, bool SuffixWithN) const { + ExplodedNode *Pred, AllocationFamily Family, + bool SuffixWithN) const { if (!State) - return nullptr; + return {nullptr, Pred}; const CallExpr *CE = cast(Call.getOriginExpr()); if (SuffixWithN && CE->getNumArgs() < 3) - return nullptr; + return {nullptr, Pred}; else if (CE->getNumArgs() < 2) - return nullptr; + return {nullptr, Pred}; const Expr *arg0Expr = CE->getArg(0); SVal Arg0Val = C.getSVal(arg0Expr); if (!Arg0Val.getAs()) - return nullptr; + return {nullptr, Pred}; DefinedOrUnknownSVal arg0Val = Arg0Val.castAs(); SValBuilder &svalBuilder = C.getSValBuilder(); @@ -2604,7 +2620,7 @@ if (SuffixWithN) TotalSize = evalMulForBufferSize(C, Arg1, CE->getArg(2)); if (!TotalSize.getAs()) - return nullptr; + return {nullptr, Pred}; // Compare the size argument to 0. DefinedOrUnknownSVal SizeZero = @@ -2624,13 +2640,12 @@ // 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, Call, TotalSize, UndefinedVal(), StatePtrIsNull, Family); - return stateMalloc; + return MallocMemAux(C, Call, TotalSize, UndefinedVal(), StatePtrIsNull, + Pred, Family); } if (PrtIsNull && SizeIsZero) - return State; + return {State, Pred}; assert(!PrtIsNull); @@ -2644,16 +2659,16 @@ // any constrains on the output pointer. if (ProgramStateRef stateFree = FreeMemAux( C, Call, StateSizeIsZero, 0, false, IsKnownToBeAllocated, Family)) - return stateFree; + return {stateFree, Pred}; // Default behavior. if (ProgramStateRef stateFree = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocated, Family)) { - ProgramStateRef stateRealloc = - MallocMemAux(C, Call, TotalSize, UnknownVal(), stateFree, Family); - if (!stateRealloc) - return nullptr; + StateAndPred ReallocState = + MallocMemAux(C, Call, TotalSize, UnknownVal(), stateFree, Pred, Family); + if (!ReallocState.State) + return ReallocState; OwnershipAfterReallocKind Kind = OAR_ToBeFreedAfterFailure; if (ShouldFreeOnFail) @@ -2671,30 +2686,30 @@ // Record the info about the reallocated symbol so that we could properly // process failed reallocation. - stateRealloc = stateRealloc->set(ToPtr, - ReallocPair(FromPtr, Kind)); + ReallocState.State = ReallocState.State->set( + ToPtr, ReallocPair(FromPtr, Kind)); // The reallocated symbol should stay alive for as long as the new symbol. C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr); - return stateRealloc; + return ReallocState; } - return nullptr; + return {nullptr, Pred}; } -ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, - const CallEvent &Call, - ProgramStateRef State) { +StateAndPred MallocChecker::CallocMem(CheckerContext &C, const CallEvent &Call, + ProgramStateRef State, + ExplodedNode *Pred) { if (!State) - return nullptr; + return {nullptr, Pred}; if (Call.getNumArgs() < 2) - return nullptr; + return {nullptr, Pred}; SValBuilder &svalBuilder = C.getSValBuilder(); SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1)); - return MallocMemAux(C, Call, TotalSize, zeroVal, State, AF_Malloc); + return MallocMemAux(C, Call, TotalSize, zeroVal, State, Pred, AF_Malloc); } MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N,