diff options
author | Pavel Skripkin <paskripkin@gmail.com> | 2024-07-24 14:15:08 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-24 13:15:08 +0200 |
commit | 893a303962608469ec5bd01fe44e82c935152e9c (patch) | |
tree | a1a25cf6ff45312603794ac65a1d224c7bd2467e /clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | |
parent | 05e95067eeaaf4bac820d10abe146db49925a195 (diff) | |
download | llvm-893a303962608469ec5bd01fe44e82c935152e9c.zip llvm-893a303962608469ec5bd01fe44e82c935152e9c.tar.gz llvm-893a303962608469ec5bd01fe44e82c935152e9c.tar.bz2 |
[clang][analyzer] Support `ownership_{returns,takes}` attributes (#98941)
Add support for checking mismatched ownership_returns/ownership_takes attributes.
Closes #76861
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 364 |
1 files changed, 236 insertions, 128 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index fe202c7..95ec28b 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -103,14 +103,48 @@ using namespace std::placeholders; namespace { // Used to check correspondence between allocators and deallocators. -enum AllocationFamily { +enum AllocationFamilyKind { AF_None, AF_Malloc, AF_CXXNew, AF_CXXNewArray, AF_IfNameIndex, AF_Alloca, - AF_InnerBuffer + AF_InnerBuffer, + AF_Custom, +}; + +struct AllocationFamily { + AllocationFamilyKind Kind; + std::optional<StringRef> CustomName; + + explicit AllocationFamily(AllocationFamilyKind AKind, + std::optional<StringRef> Name = std::nullopt) + : Kind(AKind), CustomName(Name) { + assert((Kind != AF_Custom || CustomName.has_value()) && + "Custom family must specify also the name"); + + // Preseve previous behavior when "malloc" class means AF_Malloc + if (Kind == AF_Custom && CustomName.value() == "malloc") { + Kind = AF_Malloc; + CustomName = std::nullopt; + } + } + + bool operator==(const AllocationFamily &Other) const { + return std::tie(Kind, CustomName) == std::tie(Other.Kind, Other.CustomName); + } + + bool operator!=(const AllocationFamily &Other) const { + return !(*this == Other); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(Kind); + + if (Kind == AF_Custom) + ID.AddString(CustomName.value()); + } }; } // end of anonymous namespace @@ -158,7 +192,7 @@ class RefState { RefState(Kind k, const Stmt *s, AllocationFamily family) : S(s), K(k), Family(family) { - assert(family != AF_None); + assert(family.Kind != AF_None); } public: @@ -194,7 +228,7 @@ public: void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); ID.AddPointer(S); - ID.AddInteger(Family); + Family.Profile(ID); } LLVM_DUMP_METHOD void dump(raw_ostream &OS) const { @@ -899,7 +933,7 @@ public: bool IsReleased = (RSCurr && RSCurr->isReleased()) && (!RSPrev || !RSPrev->isReleased()); assert(!IsReleased || (isa_and_nonnull<CallExpr, CXXDeleteExpr>(Stmt)) || - (!Stmt && RSCurr->getAllocationFamily() == AF_InnerBuffer)); + (!Stmt && RSCurr->getAllocationFamily().Kind == AF_InnerBuffer)); return IsReleased; } @@ -1122,7 +1156,7 @@ MallocChecker::performKernelMalloc(const CallEvent &Call, CheckerContext &C, if (TrueState && !FalseState) { SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy); return MallocMemAux(C, Call, Call.getArgExpr(0), ZeroVal, TrueState, - AF_Malloc); + AllocationFamily(AF_Malloc)); } return std::nullopt; @@ -1143,7 +1177,7 @@ void MallocChecker::checkBasicAlloc(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State, - AF_Malloc); + AllocationFamily(AF_Malloc)); State = ProcessZeroAllocCheck(Call, 0, State); C.addTransition(State); } @@ -1157,7 +1191,7 @@ void MallocChecker::checkKernelMalloc(const CallEvent &Call, State = *MaybeState; else State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State, - AF_Malloc); + AllocationFamily(AF_Malloc)); C.addTransition(State); } @@ -1194,7 +1228,8 @@ void MallocChecker::checkRealloc(const CallEvent &Call, CheckerContext &C, return; ProgramStateRef State = C.getState(); - State = ReallocMemAux(C, Call, ShouldFreeOnFail, State, AF_Malloc); + State = ReallocMemAux(C, Call, ShouldFreeOnFail, State, + AllocationFamily(AF_Malloc)); State = ProcessZeroAllocCheck(Call, 1, State); C.addTransition(State); } @@ -1214,7 +1249,7 @@ void MallocChecker::checkFree(const CallEvent &Call, CheckerContext &C) const { if (suppressDeallocationsInSuspiciousContexts(Call, C)) return; State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory, - AF_Malloc); + AllocationFamily(AF_Malloc)); C.addTransition(State); } @@ -1222,7 +1257,7 @@ void MallocChecker::checkAlloca(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); State = MallocMemAux(C, Call, Call.getArgExpr(0), UndefinedVal(), State, - AF_Alloca); + AllocationFamily(AF_Alloca)); State = ProcessZeroAllocCheck(Call, 0, State); C.addTransition(State); } @@ -1233,7 +1268,7 @@ void MallocChecker::checkStrdup(const CallEvent &Call, const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); if (!CE) return; - State = MallocUpdateRefState(C, CE, State, AF_Malloc); + State = MallocUpdateRefState(C, CE, State, AllocationFamily(AF_Malloc)); C.addTransition(State); } @@ -1243,8 +1278,8 @@ void MallocChecker::checkIfNameIndex(const CallEvent &Call, 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, Call, UnknownVal(), UnknownVal(), State, AF_IfNameIndex); + State = MallocMemAux(C, Call, UnknownVal(), UnknownVal(), State, + AllocationFamily(AF_IfNameIndex)); C.addTransition(State); } @@ -1254,7 +1289,7 @@ void MallocChecker::checkIfFreeNameIndex(const CallEvent &Call, ProgramStateRef State = C.getState(); bool IsKnownToBeAllocatedMemory = false; State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory, - AF_IfNameIndex); + AllocationFamily(AF_IfNameIndex)); C.addTransition(State); } @@ -1275,25 +1310,26 @@ void MallocChecker::checkCXXNewOrCXXDelete(const CallEvent &Call, const FunctionDecl *FD = C.getCalleeDecl(CE); switch (FD->getOverloadedOperator()) { case OO_New: - State = - MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State, AF_CXXNew); + State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State, + AllocationFamily(AF_CXXNew)); State = ProcessZeroAllocCheck(Call, 0, State); break; case OO_Array_New: State = MallocMemAux(C, Call, CE->getArg(0), UndefinedVal(), State, - AF_CXXNewArray); + AllocationFamily(AF_CXXNewArray)); State = ProcessZeroAllocCheck(Call, 0, State); break; case OO_Delete: State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory, - AF_CXXNew); + AllocationFamily(AF_CXXNew)); break; case OO_Array_Delete: State = FreeMemAux(C, Call, State, 0, false, IsKnownToBeAllocatedMemory, - AF_CXXNewArray); + AllocationFamily(AF_CXXNewArray)); break; default: - llvm_unreachable("not a new/delete operator"); + assert(false && "not a new/delete operator"); + return; } C.addTransition(State); @@ -1304,7 +1340,8 @@ void MallocChecker::checkGMalloc0(const CallEvent &Call, ProgramStateRef State = C.getState(); SValBuilder &svalBuilder = C.getSValBuilder(); SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); - State = MallocMemAux(C, Call, Call.getArgExpr(0), zeroVal, State, AF_Malloc); + State = MallocMemAux(C, Call, Call.getArgExpr(0), zeroVal, State, + AllocationFamily(AF_Malloc)); State = ProcessZeroAllocCheck(Call, 0, State); C.addTransition(State); } @@ -1312,8 +1349,8 @@ void MallocChecker::checkGMalloc0(const CallEvent &Call, 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 = MallocMemAux(C, Call, Call.getArgExpr(1), UnknownVal(), State, + AllocationFamily(AF_Malloc)); State = ProcessZeroAllocCheck(Call, 1, State); C.addTransition(State); } @@ -1323,7 +1360,8 @@ void MallocChecker::checkGMallocN(const CallEvent &Call, ProgramStateRef State = C.getState(); SVal Init = UndefinedVal(); SVal TotalSize = evalMulForBufferSize(C, Call.getArgExpr(0), Call.getArgExpr(1)); - State = MallocMemAux(C, Call, TotalSize, Init, State, AF_Malloc); + State = MallocMemAux(C, Call, TotalSize, Init, State, + AllocationFamily(AF_Malloc)); State = ProcessZeroAllocCheck(Call, 0, State); State = ProcessZeroAllocCheck(Call, 1, State); C.addTransition(State); @@ -1335,7 +1373,8 @@ void MallocChecker::checkGMallocN0(const CallEvent &Call, 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 = MallocMemAux(C, Call, TotalSize, Init, State, + AllocationFamily(AF_Malloc)); State = ProcessZeroAllocCheck(Call, 0, State); State = ProcessZeroAllocCheck(Call, 1, State); C.addTransition(State); @@ -1365,7 +1404,8 @@ void MallocChecker::preGetdelim(const CallEvent &Call, // of reporting any violation of the preconditions. bool IsKnownToBeAllocated = false; State = FreeMemAux(C, Call.getArgExpr(0), Call, State, false, - IsKnownToBeAllocated, AF_Malloc, false, LinePtr); + IsKnownToBeAllocated, AllocationFamily(AF_Malloc), false, + LinePtr); if (State) C.addTransition(State); } @@ -1394,13 +1434,15 @@ void MallocChecker::checkGetdelim(const CallEvent &Call, return; State = setDynamicExtent(State, LinePtr->getAsRegion(), *Size, SVB); - C.addTransition(MallocUpdateRefState(C, CE, State, AF_Malloc, *LinePtr)); + C.addTransition(MallocUpdateRefState(C, CE, State, + AllocationFamily(AF_Malloc), *LinePtr)); } void MallocChecker::checkReallocN(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); - State = ReallocMemAux(C, Call, /*ShouldFreeOnFail=*/false, State, AF_Malloc, + State = ReallocMemAux(C, Call, /*ShouldFreeOnFail=*/false, State, + AllocationFamily(AF_Malloc), /*SuffixWithN=*/true); State = ProcessZeroAllocCheck(Call, 1, State); State = ProcessZeroAllocCheck(Call, 2, State); @@ -1489,8 +1531,10 @@ ProgramStateRef MallocChecker::ProcessZeroAllocCheck( } else { return State; } - } else - llvm_unreachable("not a CallExpr or CXXNewExpr"); + } else { + assert(false && "not a CallExpr or CXXNewExpr"); + return nullptr; + } assert(Arg); @@ -1598,7 +1642,8 @@ MallocChecker::processNewAllocation(const CXXAllocatorCall &Call, SVal Target = Call.getObjectUnderConstruction(); if (Call.getOriginExpr()->isArray()) { if (auto SizeEx = NE->getArraySize()) - checkTaintedness(C, Call, C.getSVal(*SizeEx), State, AF_CXXNewArray); + checkTaintedness(C, Call, C.getSVal(*SizeEx), State, + AllocationFamily(AF_CXXNewArray)); } State = MallocUpdateRefState(C, NE, State, Family, Target); @@ -1611,7 +1656,8 @@ void MallocChecker::checkNewAllocator(const CXXAllocatorCall &Call, if (!C.wasInlined) { ProgramStateRef State = processNewAllocation( Call, C, - (Call.getOriginExpr()->isArray() ? AF_CXXNewArray : AF_CXXNew)); + AllocationFamily(Call.getOriginExpr()->isArray() ? AF_CXXNewArray + : AF_CXXNew)); C.addTransition(State); } } @@ -1655,10 +1701,10 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, return; bool IsKnownToBeAllocatedMemory; - ProgramStateRef State = - FreeMemAux(C, Call.getArgExpr(0), Call, C.getState(), - /*Hold=*/true, IsKnownToBeAllocatedMemory, AF_Malloc, - /*ReturnsNullOnFailure=*/true); + ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0), Call, C.getState(), + /*Hold=*/true, IsKnownToBeAllocatedMemory, + AllocationFamily(AF_Malloc), + /*ReturnsNullOnFailure=*/true); C.addTransition(State); } @@ -1670,15 +1716,15 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallEvent &Call, if (!State) return nullptr; - if (Att->getModule()->getName() != "malloc") - return nullptr; + auto attrClassName = Att->getModule()->getName(); + auto Family = AllocationFamily(AF_Custom, attrClassName); if (!Att->args().empty()) { return MallocMemAux(C, Call, Call.getArgExpr(Att->args_begin()->getASTIndex()), - UndefinedVal(), State, AF_Malloc); + UndefinedVal(), State, Family); } - return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, AF_Malloc); + return MallocMemAux(C, Call, UnknownVal(), UndefinedVal(), State, Family); } ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, @@ -1768,10 +1814,10 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, unsigned Count = C.blockCount(); SValBuilder &SVB = C.getSValBuilder(); const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); - DefinedSVal RetVal = - ((Family == AF_Alloca) ? SVB.getAllocaRegionVal(CE, LCtx, Count) - : SVB.getConjuredHeapSymbolVal(CE, LCtx, Count) - .castAs<DefinedSVal>()); + DefinedSVal RetVal = ((Family.Kind == AF_Alloca) + ? SVB.getAllocaRegionVal(CE, LCtx, Count) + : SVB.getConjuredHeapSymbolVal(CE, LCtx, Count) + .castAs<DefinedSVal>()); State = State->BindExpr(CE, C.getLocationContext(), RetVal); // Fill the region with the initialization value. @@ -1781,7 +1827,7 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, if (Size.isUndef()) Size = UnknownVal(); - checkTaintedness(C, Call, Size, State, AF_Malloc); + checkTaintedness(C, Call, Size, State, AllocationFamily(AF_Malloc)); // Set the region's extent. State = setDynamicExtent(State, RetVal.getAsRegion(), @@ -1830,8 +1876,8 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, if (!State) return nullptr; - if (Att->getModule()->getName() != "malloc") - return nullptr; + auto attrClassName = Att->getModule()->getName(); + auto Family = AllocationFamily(AF_Custom, attrClassName); bool IsKnownToBeAllocated = false; @@ -1839,7 +1885,7 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, ProgramStateRef StateI = FreeMemAux(C, Call, State, Arg.getASTIndex(), Att->getOwnKind() == OwnershipAttr::Holds, - IsKnownToBeAllocated, AF_Malloc); + IsKnownToBeAllocated, Family); if (StateI) State = StateI; } @@ -1877,6 +1923,27 @@ static bool didPreviousFreeFail(ProgramStateRef State, return false; } +static void printOwnershipTakesList(raw_ostream &os, CheckerContext &C, + const Expr *E) { + const CallExpr *CE = dyn_cast<CallExpr>(E); + + if (!CE) + return; + + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return; + + // Only one ownership_takes attribute is allowed. + for (const auto *I : FD->specific_attrs<OwnershipAttr>()) { + if (I->getOwnKind() != OwnershipAttr::Takes) + continue; + + os << ", which takes ownership of '" << I->getModule()->getName() << '\''; + break; + } +} + static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E) { if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { // FIXME: This doesn't handle indirect calls. @@ -1884,9 +1951,12 @@ static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E) { if (!FD) return false; - os << *FD; + os << '\'' << *FD; + if (!FD->isOverloadedOperator()) os << "()"; + + os << '\''; return true; } @@ -1918,26 +1988,55 @@ static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E) { static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family) { - switch(Family) { - case AF_Malloc: os << "malloc()"; return; - case AF_CXXNew: os << "'new'"; return; - case AF_CXXNewArray: os << "'new[]'"; return; - case AF_IfNameIndex: os << "'if_nameindex()'"; return; - case AF_InnerBuffer: os << "container-specific allocator"; return; - case AF_Alloca: - case AF_None: llvm_unreachable("not a deallocation expression"); + switch (Family.Kind) { + case AF_Malloc: + os << "'malloc()'"; + return; + case AF_CXXNew: + os << "'new'"; + return; + case AF_CXXNewArray: + os << "'new[]'"; + return; + case AF_IfNameIndex: + os << "'if_nameindex()'"; + return; + case AF_InnerBuffer: + os << "container-specific allocator"; + return; + case AF_Custom: + os << Family.CustomName.value(); + return; + case AF_Alloca: + case AF_None: + assert(false && "not a deallocation expression"); } } static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) { - switch(Family) { - case AF_Malloc: os << "free()"; return; - case AF_CXXNew: os << "'delete'"; return; - case AF_CXXNewArray: os << "'delete[]'"; return; - case AF_IfNameIndex: os << "'if_freenameindex()'"; return; - case AF_InnerBuffer: os << "container-specific deallocator"; return; - case AF_Alloca: - case AF_None: llvm_unreachable("suspicious argument"); + switch (Family.Kind) { + case AF_Malloc: + os << "'free()'"; + return; + case AF_CXXNew: + os << "'delete'"; + return; + case AF_CXXNewArray: + os << "'delete[]'"; + return; + case AF_IfNameIndex: + os << "'if_freenameindex()'"; + return; + case AF_InnerBuffer: + os << "container-specific deallocator"; + return; + case AF_Custom: + os << "function that takes ownership of '" << Family.CustomName.value() + << "\'"; + return; + case AF_Alloca: + case AF_None: + assert(false && "not a deallocation expression"); } } @@ -1991,7 +2090,7 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr, // code. In that case, the ZERO_SIZE_PTR defines a special value used for a // zero-sized memory block which is allowed to be freed, despite not being a // null pointer. - if (Family != AF_Malloc || !isArgZERO_SIZE_PTR(State, C, ArgVal)) + if (Family.Kind != AF_Malloc || !isArgZERO_SIZE_PTR(State, C, ArgVal)) HandleNonHeapDealloc(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, Family); return nullptr; @@ -2041,7 +2140,7 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr, if (RsBase) { // Memory returned by alloca() shouldn't be freed. - if (RsBase->getAllocationFamily() == AF_Alloca) { + if (RsBase->getAllocationFamily().Kind == AF_Alloca) { HandleFreeAlloca(C, ArgVal, ArgExpr->getSourceRange()); return nullptr; } @@ -2119,9 +2218,10 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr, std::optional<MallocChecker::CheckKind> MallocChecker::getCheckIfTracked(AllocationFamily Family, bool IsALeakCheck) const { - switch (Family) { + switch (Family.Kind) { case AF_Malloc: case AF_Alloca: + case AF_Custom: case AF_IfNameIndex: { if (ChecksEnabled[CK_MallocChecker]) return CK_MallocChecker; @@ -2145,10 +2245,12 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family, return std::nullopt; } case AF_None: { - llvm_unreachable("no family"); + assert(false && "no family"); + return std::nullopt; } } - llvm_unreachable("unhandled family"); + assert(false && "unhandled family"); + return std::nullopt; } std::optional<MallocChecker::CheckKind> @@ -2316,11 +2418,11 @@ void MallocChecker::HandleFreeAlloca(CheckerContext &C, SVal ArgVal, if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_FreeAlloca[*CheckKind]) BT_FreeAlloca[*CheckKind].reset(new BugType( - CheckNames[*CheckKind], "Free alloca()", categories::MemoryError)); + CheckNames[*CheckKind], "Free 'alloca()'", categories::MemoryError)); auto R = std::make_unique<PathSensitiveBugReport>( *BT_FreeAlloca[*CheckKind], - "Memory allocated by alloca() should not be deallocated", N); + "Memory allocated by 'alloca()' should not be deallocated", N); R->markInteresting(ArgVal.getAsRegion()); R->addRange(Range); C.emitReport(std::move(R)); @@ -2373,6 +2475,8 @@ void MallocChecker::HandleMismatchedDealloc(CheckerContext &C, if (printMemFnName(DeallocOs, C, DeallocExpr)) os << ", not " << DeallocOs.str(); + + printOwnershipTakesList(os, C, DeallocExpr); } auto R = std::make_unique<PathSensitiveBugReport>(*BT_MismatchedDealloc, @@ -2465,7 +2569,7 @@ void MallocChecker::HandleUseAfterFree(CheckerContext &C, SourceRange Range, auto R = std::make_unique<PathSensitiveBugReport>( *BT_UseFree[*CheckKind], - AF == AF_InnerBuffer + AF.Kind == AF_InnerBuffer ? "Inner pointer of container used after re/deallocation" : "Use of memory after it is freed", N); @@ -2474,7 +2578,7 @@ void MallocChecker::HandleUseAfterFree(CheckerContext &C, SourceRange Range, R->addRange(Range); R->addVisitor<MallocBugVisitor>(Sym); - if (AF == AF_InnerBuffer) + if (AF.Kind == AF_InnerBuffer) R->addVisitor(allocation_state::getInnerPointerBRVisitor(Sym)); C.emitReport(std::move(R)); @@ -2733,7 +2837,8 @@ ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, 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, + AllocationFamily(AF_Malloc)); } MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N, @@ -2788,7 +2893,7 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N, assert(RS && "cannot leak an untracked symbol"); AllocationFamily Family = RS->getAllocationFamily(); - if (Family == AF_Alloca) + if (Family.Kind == AF_Alloca) return; std::optional<MallocChecker::CheckKind> CheckKind = @@ -2915,9 +3020,10 @@ void MallocChecker::checkPreCall(const CallEvent &Call, ProgramStateRef State = C.getState(); bool IsKnownToBeAllocated; - State = FreeMemAux(C, DE->getArgument(), Call, State, - /*Hold*/ false, IsKnownToBeAllocated, - (DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew)); + State = FreeMemAux( + C, DE->getArgument(), Call, State, + /*Hold*/ false, IsKnownToBeAllocated, + AllocationFamily(DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew)); C.addTransition(State); return; @@ -3349,8 +3455,8 @@ ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State, } static bool checkIfNewOrNewArrayFamily(const RefState *RS) { - return (RS->getAllocationFamily() == AF_CXXNewArray || - RS->getAllocationFamily() == AF_CXXNew); + return (RS->getAllocationFamily().Kind == AF_CXXNewArray || + RS->getAllocationFamily().Kind == AF_CXXNew); } ProgramStateRef MallocChecker::checkPointerEscapeAux( @@ -3431,7 +3537,7 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N, const Stmt *S = N->getStmtForDiagnostics(); // When dealing with containers, we sometimes want to give a note // even if the statement is missing. - if (!S && (!RSCurr || RSCurr->getAllocationFamily() != AF_InnerBuffer)) + if (!S && (!RSCurr || RSCurr->getAllocationFamily().Kind != AF_InnerBuffer)) return nullptr; const LocationContext *CurrentLC = N->getLocationContext(); @@ -3483,53 +3589,55 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N, Sym, "Returned allocated memory"); } else if (isReleased(RSCurr, RSPrev, S)) { const auto Family = RSCurr->getAllocationFamily(); - switch (Family) { - case AF_Alloca: - case AF_Malloc: - case AF_CXXNew: - case AF_CXXNewArray: - case AF_IfNameIndex: - Msg = "Memory is released"; + switch (Family.Kind) { + case AF_Alloca: + case AF_Malloc: + case AF_Custom: + case AF_CXXNew: + case AF_CXXNewArray: + case AF_IfNameIndex: + Msg = "Memory is released"; + StackHint = std::make_unique<StackHintGeneratorForSymbol>( + Sym, "Returning; memory was released"); + break; + case AF_InnerBuffer: { + const MemRegion *ObjRegion = + allocation_state::getContainerObjRegion(statePrev, Sym); + const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion); + QualType ObjTy = TypedRegion->getValueType(); + OS << "Inner buffer of '" << ObjTy << "' "; + + if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) { + OS << "deallocated by call to destructor"; StackHint = std::make_unique<StackHintGeneratorForSymbol>( - Sym, "Returning; memory was released"); - break; - case AF_InnerBuffer: { - const MemRegion *ObjRegion = - allocation_state::getContainerObjRegion(statePrev, Sym); - const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion); - QualType ObjTy = TypedRegion->getValueType(); - OS << "Inner buffer of '" << ObjTy << "' "; - - if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) { - OS << "deallocated by call to destructor"; - StackHint = std::make_unique<StackHintGeneratorForSymbol>( - Sym, "Returning; inner buffer was deallocated"); - } else { - OS << "reallocated by call to '"; - const Stmt *S = RSCurr->getStmt(); - if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) { - OS << MemCallE->getMethodDecl()->getDeclName(); - } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) { - OS << OpCallE->getDirectCallee()->getDeclName(); - } else if (const auto *CallE = dyn_cast<CallExpr>(S)) { - auto &CEMgr = BRC.getStateManager().getCallEventManager(); - CallEventRef<> Call = - CEMgr.getSimpleCall(CallE, state, CurrentLC, {nullptr, 0}); - if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl())) - OS << D->getDeclName(); - else - OS << "unknown"; - } - OS << "'"; - StackHint = std::make_unique<StackHintGeneratorForSymbol>( - Sym, "Returning; inner buffer was reallocated"); + Sym, "Returning; inner buffer was deallocated"); + } else { + OS << "reallocated by call to '"; + const Stmt *S = RSCurr->getStmt(); + if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) { + OS << MemCallE->getMethodDecl()->getDeclName(); + } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) { + OS << OpCallE->getDirectCallee()->getDeclName(); + } else if (const auto *CallE = dyn_cast<CallExpr>(S)) { + auto &CEMgr = BRC.getStateManager().getCallEventManager(); + CallEventRef<> Call = + CEMgr.getSimpleCall(CallE, state, CurrentLC, {nullptr, 0}); + if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl())) + OS << D->getDeclName(); + else + OS << "unknown"; } - Msg = OS.str(); - break; + OS << "'"; + StackHint = std::make_unique<StackHintGeneratorForSymbol>( + Sym, "Returning; inner buffer was reallocated"); + } + Msg = OS.str(); + break; } case AF_None: - llvm_unreachable("Unhandled allocation family!"); - } + assert(false && "Unhandled allocation family!"); + return nullptr; + } // See if we're releasing memory while inlining a destructor // (or one of its callees). This turns on various common @@ -3603,7 +3711,7 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N, // Generate the extra diagnostic. PathDiagnosticLocation Pos; if (!S) { - assert(RSCurr->getAllocationFamily() == AF_InnerBuffer); + assert(RSCurr->getAllocationFamily().Kind == AF_InnerBuffer); auto PostImplCall = N->getLocation().getAs<PostImplicitCall>(); if (!PostImplCall) return nullptr; @@ -3650,7 +3758,7 @@ namespace allocation_state { ProgramStateRef markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) { - AllocationFamily Family = AF_InnerBuffer; + AllocationFamily Family(AF_InnerBuffer); return State->set<RegionState>(Sym, RefState::getReleased(Family, Origin)); } |