Skip to content

Commit e08dd79

Browse files
committed
csa/MallocChecker: add support for custom allocation classes
Add support for custom allocation classes that could be specified by ownership_returns attribute. This patch adds basic support, so new class is not contructed anywhere and handled as an error everywhere.
1 parent aee553e commit e08dd79

File tree

1 file changed

+122
-62
lines changed

1 file changed

+122
-62
lines changed

clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp

+122-62
Original file line numberDiff line numberDiff line change
@@ -103,14 +103,46 @@ using namespace std::placeholders;
103103
namespace {
104104

105105
// Used to check correspondence between allocators and deallocators.
106-
enum AllocationFamily {
106+
enum AllocationFamilyKind {
107107
AF_None,
108108
AF_Malloc,
109109
AF_CXXNew,
110110
AF_CXXNewArray,
111111
AF_IfNameIndex,
112112
AF_Alloca,
113-
AF_InnerBuffer
113+
AF_InnerBuffer,
114+
AF_Custom,
115+
};
116+
117+
class AllocationFamily {
118+
public:
119+
AllocationFamily(AllocationFamilyKind kind,
120+
std::optional<StringRef> name = std::nullopt)
121+
: _kind(kind), _customName(name) {
122+
assert(kind != AF_Custom || name != std::nullopt);
123+
}
124+
125+
bool operator==(const AllocationFamily &Other) const {
126+
if (Other.kind() == this->kind()) {
127+
return this->kind() == AF_Custom ? this->_customName == Other._customName
128+
: true;
129+
} else {
130+
return false;
131+
}
132+
}
133+
134+
bool operator==(const AllocationFamilyKind &kind) {
135+
return this->kind() == kind;
136+
}
137+
138+
bool operator!=(const AllocationFamilyKind &kind) { return !(*this == kind); }
139+
140+
std::optional<StringRef> name() const { return _customName; }
141+
AllocationFamilyKind kind() const { return _kind; }
142+
143+
private:
144+
AllocationFamilyKind _kind;
145+
std::optional<StringRef> _customName;
114146
};
115147

116148
} // end of anonymous namespace
@@ -194,7 +226,7 @@ class RefState {
194226
void Profile(llvm::FoldingSetNodeID &ID) const {
195227
ID.AddInteger(K);
196228
ID.AddPointer(S);
197-
ID.AddInteger(Family);
229+
ID.AddInteger(Family.kind());
198230
}
199231

200232
LLVM_DUMP_METHOD void dump(raw_ostream &OS) const {
@@ -1918,26 +1950,52 @@ static bool printMemFnName(raw_ostream &os, CheckerContext &C, const Expr *E) {
19181950

19191951
static void printExpectedAllocName(raw_ostream &os, AllocationFamily Family) {
19201952

1921-
switch(Family) {
1922-
case AF_Malloc: os << "malloc()"; return;
1923-
case AF_CXXNew: os << "'new'"; return;
1924-
case AF_CXXNewArray: os << "'new[]'"; return;
1925-
case AF_IfNameIndex: os << "'if_nameindex()'"; return;
1926-
case AF_InnerBuffer: os << "container-specific allocator"; return;
1927-
case AF_Alloca:
1928-
case AF_None: llvm_unreachable("not a deallocation expression");
1953+
switch (Family.kind()) {
1954+
case AF_Malloc:
1955+
os << "malloc()";
1956+
return;
1957+
case AF_CXXNew:
1958+
os << "'new'";
1959+
return;
1960+
case AF_CXXNewArray:
1961+
os << "'new[]'";
1962+
return;
1963+
case AF_IfNameIndex:
1964+
os << "'if_nameindex()'";
1965+
return;
1966+
case AF_InnerBuffer:
1967+
os << "container-specific allocator";
1968+
return;
1969+
case AF_Alloca:
1970+
case AF_None:
1971+
llvm_unreachable("not a deallocation expression");
1972+
case AF_Custom:
1973+
llvm_unreachable("not a deallocation expression");
19291974
}
19301975
}
19311976

19321977
static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) {
1933-
switch(Family) {
1934-
case AF_Malloc: os << "free()"; return;
1935-
case AF_CXXNew: os << "'delete'"; return;
1936-
case AF_CXXNewArray: os << "'delete[]'"; return;
1937-
case AF_IfNameIndex: os << "'if_freenameindex()'"; return;
1938-
case AF_InnerBuffer: os << "container-specific deallocator"; return;
1939-
case AF_Alloca:
1940-
case AF_None: llvm_unreachable("suspicious argument");
1978+
switch (Family.kind()) {
1979+
case AF_Malloc:
1980+
os << "free()";
1981+
return;
1982+
case AF_CXXNew:
1983+
os << "'delete'";
1984+
return;
1985+
case AF_CXXNewArray:
1986+
os << "'delete[]'";
1987+
return;
1988+
case AF_IfNameIndex:
1989+
os << "'if_freenameindex()'";
1990+
return;
1991+
case AF_InnerBuffer:
1992+
os << "container-specific deallocator";
1993+
return;
1994+
case AF_Alloca:
1995+
case AF_None:
1996+
llvm_unreachable("suspicious argument");
1997+
case AF_Custom:
1998+
llvm_unreachable("suspicious argument");
19411999
}
19422000
}
19432001

@@ -2119,7 +2177,7 @@ MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr,
21192177
std::optional<MallocChecker::CheckKind>
21202178
MallocChecker::getCheckIfTracked(AllocationFamily Family,
21212179
bool IsALeakCheck) const {
2122-
switch (Family) {
2180+
switch (Family.kind()) {
21232181
case AF_Malloc:
21242182
case AF_Alloca:
21252183
case AF_IfNameIndex: {
@@ -2144,6 +2202,7 @@ MallocChecker::getCheckIfTracked(AllocationFamily Family,
21442202
return CK_InnerPointerChecker;
21452203
return std::nullopt;
21462204
}
2205+
case AF_Custom:
21472206
case AF_None: {
21482207
llvm_unreachable("no family");
21492208
}
@@ -3483,53 +3542,54 @@ PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N,
34833542
Sym, "Returned allocated memory");
34843543
} else if (isReleased(RSCurr, RSPrev, S)) {
34853544
const auto Family = RSCurr->getAllocationFamily();
3486-
switch (Family) {
3487-
case AF_Alloca:
3488-
case AF_Malloc:
3489-
case AF_CXXNew:
3490-
case AF_CXXNewArray:
3491-
case AF_IfNameIndex:
3492-
Msg = "Memory is released";
3545+
switch (Family.kind()) {
3546+
case AF_Alloca:
3547+
case AF_Malloc:
3548+
case AF_CXXNew:
3549+
case AF_CXXNewArray:
3550+
case AF_IfNameIndex:
3551+
Msg = "Memory is released";
3552+
StackHint = std::make_unique<StackHintGeneratorForSymbol>(
3553+
Sym, "Returning; memory was released");
3554+
break;
3555+
case AF_InnerBuffer: {
3556+
const MemRegion *ObjRegion =
3557+
allocation_state::getContainerObjRegion(statePrev, Sym);
3558+
const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);
3559+
QualType ObjTy = TypedRegion->getValueType();
3560+
OS << "Inner buffer of '" << ObjTy << "' ";
3561+
3562+
if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) {
3563+
OS << "deallocated by call to destructor";
34933564
StackHint = std::make_unique<StackHintGeneratorForSymbol>(
3494-
Sym, "Returning; memory was released");
3495-
break;
3496-
case AF_InnerBuffer: {
3497-
const MemRegion *ObjRegion =
3498-
allocation_state::getContainerObjRegion(statePrev, Sym);
3499-
const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);
3500-
QualType ObjTy = TypedRegion->getValueType();
3501-
OS << "Inner buffer of '" << ObjTy << "' ";
3502-
3503-
if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) {
3504-
OS << "deallocated by call to destructor";
3505-
StackHint = std::make_unique<StackHintGeneratorForSymbol>(
3506-
Sym, "Returning; inner buffer was deallocated");
3507-
} else {
3508-
OS << "reallocated by call to '";
3509-
const Stmt *S = RSCurr->getStmt();
3510-
if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) {
3511-
OS << MemCallE->getMethodDecl()->getDeclName();
3512-
} else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) {
3513-
OS << OpCallE->getDirectCallee()->getDeclName();
3514-
} else if (const auto *CallE = dyn_cast<CallExpr>(S)) {
3515-
auto &CEMgr = BRC.getStateManager().getCallEventManager();
3516-
CallEventRef<> Call =
3517-
CEMgr.getSimpleCall(CallE, state, CurrentLC, {nullptr, 0});
3518-
if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()))
3519-
OS << D->getDeclName();
3520-
else
3521-
OS << "unknown";
3522-
}
3523-
OS << "'";
3524-
StackHint = std::make_unique<StackHintGeneratorForSymbol>(
3525-
Sym, "Returning; inner buffer was reallocated");
3565+
Sym, "Returning; inner buffer was deallocated");
3566+
} else {
3567+
OS << "reallocated by call to '";
3568+
const Stmt *S = RSCurr->getStmt();
3569+
if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) {
3570+
OS << MemCallE->getMethodDecl()->getDeclName();
3571+
} else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) {
3572+
OS << OpCallE->getDirectCallee()->getDeclName();
3573+
} else if (const auto *CallE = dyn_cast<CallExpr>(S)) {
3574+
auto &CEMgr = BRC.getStateManager().getCallEventManager();
3575+
CallEventRef<> Call =
3576+
CEMgr.getSimpleCall(CallE, state, CurrentLC, {nullptr, 0});
3577+
if (const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()))
3578+
OS << D->getDeclName();
3579+
else
3580+
OS << "unknown";
35263581
}
3527-
Msg = OS.str();
3528-
break;
3582+
OS << "'";
3583+
StackHint = std::make_unique<StackHintGeneratorForSymbol>(
3584+
Sym, "Returning; inner buffer was reallocated");
3585+
}
3586+
Msg = OS.str();
3587+
break;
35293588
}
3589+
case AF_Custom:
35303590
case AF_None:
35313591
llvm_unreachable("Unhandled allocation family!");
3532-
}
3592+
}
35333593

35343594
// See if we're releasing memory while inlining a destructor
35353595
// (or one of its callees). This turns on various common

0 commit comments

Comments
 (0)