aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
diff options
context:
space:
mode:
authorPavel Skripkin <paskripkin@gmail.com>2024-07-24 14:15:08 +0300
committerGitHub <noreply@github.com>2024-07-24 13:15:08 +0200
commit893a303962608469ec5bd01fe44e82c935152e9c (patch)
treea1a25cf6ff45312603794ac65a1d224c7bd2467e /clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
parent05e95067eeaaf4bac820d10abe146db49925a195 (diff)
downloadllvm-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.cpp364
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));
}