Skip to content

Commit cf3653d

Browse files
eupharinaresistor
authored andcommitted
[CHERI_CSA] AllocationChecker: rework
1 parent 9c4e78b commit cf3653d

File tree

5 files changed

+242
-81
lines changed

5 files changed

+242
-81
lines changed

clang/lib/StaticAnalyzer/Checkers/CHERI/AllocationChecker.cpp

Lines changed: 185 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -18,61 +18,82 @@
1818
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
1919
#include "clang/StaticAnalyzer/Core/Checker.h"
2020
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
21+
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
2122
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2223
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
2324

2425
using namespace clang;
2526
using namespace ento;
2627
using namespace cheri;
2728

28-
namespace {
29+
struct EscapeInfo {
30+
PointerEscapeKind Kind;
31+
EscapeInfo(PointerEscapeKind K) : Kind(K) {};
32+
bool operator==(const EscapeInfo &X) const { return Kind == X.Kind; }
33+
void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Kind); }
34+
};
35+
using EscapePair = std::pair<const MemRegion *, EscapeInfo>;
2936

30-
class AllocationChecker : public Checker<check::PostStmt<CastExpr>> {
37+
namespace {
38+
class AllocationChecker
39+
: public Checker<check::PostStmt<CastExpr>, check::PreCall, check::Bind,
40+
check::EndFunction> {
3141
BugType BT_Default{this, "Allocation partitioning", "CHERI portability"};
3242
BugType BT_KnownReg{this, "Heap or static allocation partitioning",
3343
"CHERI portability"};
3444

3545
class AllocPartitionBugVisitor : public BugReporterVisitor {
3646
public:
37-
AllocPartitionBugVisitor(const MemRegion *R) : Reg(R) {}
47+
AllocPartitionBugVisitor(const MemRegion *P, const MemRegion *A)
48+
: PrevAlloc(P), SubAlloc(A) {}
3849

3950
void Profile(llvm::FoldingSetNodeID &ID) const override {
4051
static int X = 0;
4152
ID.AddPointer(&X);
42-
ID.AddPointer(Reg);
53+
ID.AddPointer(PrevAlloc);
54+
ID.AddPointer(SubAlloc);
4355
}
4456

4557
PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
4658
BugReporterContext &BRC,
4759
PathSensitiveBugReport &BR) override;
4860

4961
private:
50-
const MemRegion *Reg;
62+
const MemRegion *PrevAlloc;
63+
const MemRegion *SubAlloc;
64+
bool PrevReported = false;
5165
};
5266

5367
public:
5468
void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
69+
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
70+
void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
71+
void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
5572

5673
private:
57-
ExplodedNode *emitAllocationPartitionWarning(const CastExpr *CE,
58-
CheckerContext &C,
59-
const MemRegion *R) const;
74+
ExplodedNode *emitAllocationPartitionWarning(CheckerContext &C,
75+
const MemRegion *MR,
76+
SourceRange SR,
77+
StringRef Msg) const;
6078
};
6179

6280
} // namespace
6381

6482
REGISTER_MAP_WITH_PROGRAMSTATE(AllocMap, const MemRegion *, QualType)
6583
REGISTER_MAP_WITH_PROGRAMSTATE(ShiftMap, const MemRegion *, const MemRegion *)
84+
REGISTER_SET_WITH_PROGRAMSTATE(SuballocationSet, const MemRegion *)
6685

6786
namespace {
68-
std::pair<const MemRegion *, bool> getAllocationStart(const MemRegion *R,
69-
CheckerContext &C,
87+
std::pair<const MemRegion *, bool> getAllocationStart(const ASTContext &ASTCtx,
88+
const MemRegion *R,
89+
ProgramStateRef State,
7090
bool ZeroShift = true) {
7191
if (const ElementRegion *ER = R->getAs<ElementRegion>()) {
7292
const MemRegion *Base = ER->getSuperRegion();
73-
return getAllocationStart(Base, C, ER->getIndex().isZeroConstant());
93+
return getAllocationStart(ASTCtx, Base, State,
94+
ZeroShift && ER->getIndex().isZeroConstant());
7495
}
75-
if (auto OrigR = C.getState()->get<ShiftMap>(R)) {
96+
if (const auto *OrigR = State->get<ShiftMap>(R)) {
7697
return std::make_pair(*OrigR, false);
7798
}
7899
return std::make_pair(R, ZeroShift);
@@ -87,17 +108,22 @@ bool isAllocation(const MemRegion *R) {
87108
return false;
88109
}
89110

90-
bool relatedTypes(const Type *Ty1, const Type *Ty2) {
111+
bool relatedTypes(const ASTContext &ASTCtx, const Type *Ty1, const Type *Ty2) {
91112
if (Ty1 == Ty2)
92113
return true;
114+
if (Ty1->isIntegerType()) {
115+
if (Ty2->isIntegerType())
116+
return ASTCtx.getTypeSize(Ty1) == ASTCtx.getTypeSize(Ty2);
117+
return false;
118+
}
93119
if (Ty1->isArrayType())
94-
return relatedTypes(Ty1->getArrayElementTypeNoTypeQual(), Ty2);
120+
return relatedTypes(ASTCtx, Ty1->getArrayElementTypeNoTypeQual(), Ty2);
95121
if (Ty1->isRecordType()) {
96122
if (RecordDecl *RD = Ty1->getAs<RecordType>()->getAsRecordDecl()) {
97123
const RecordDecl::field_iterator &FirstField = RD->field_begin();
98124
if (FirstField != RD->field_end()) {
99125
const Type *FFTy = FirstField->getType()->getUnqualifiedDesugaredType();
100-
return relatedTypes(FFTy, Ty2);
126+
return relatedTypes(ASTCtx, FFTy, Ty2);
101127
}
102128
}
103129
}
@@ -109,8 +135,11 @@ bool reportForType(QualType Ty) {
109135
return false;
110136
if (Ty->isPointerType() || Ty->isArrayType()) {
111137
const Type *PTy = Ty->getPointeeOrArrayElementType();
138+
PTy = PTy->getUnqualifiedDesugaredType();
112139
if (PTy->isCharType())
113140
return false;
141+
if (PTy->isPointerType())
142+
return false;
114143
if (PTy->isStructureTypeWithFlexibleArrayMember())
115144
return false;
116145
return true;
@@ -132,17 +161,24 @@ std::optional<QualType> getPrevType(ProgramStateRef State, const MemRegion *R) {
132161
} // namespace
133162

134163
ExplodedNode *AllocationChecker::emitAllocationPartitionWarning(
135-
const CastExpr *CE, CheckerContext &C, const MemRegion *MR) const {
164+
CheckerContext &C, const MemRegion *MR, SourceRange SR,
165+
StringRef Msg = "") const {
136166
if (ExplodedNode *ErrNode = C.generateNonFatalErrorNode()) {
137-
SmallString<256> Buf;
138-
llvm::raw_svector_ostream OS(Buf);
139-
OS << "Allocation partition: ";
140-
describeCast(OS, CE, C.getASTContext().getLangOpts());
141-
const MemSpaceRegion *MemSpace = MR->getMemorySpace();
142-
bool KnownReg = isa<HeapSpaceRegion, GlobalsSpaceRegion>(MemSpace);
143-
auto R = std::make_unique<PathSensitiveBugReport>(
144-
KnownReg ? BT_KnownReg : BT_Default, OS.str(), ErrNode);
145-
R->addVisitor(std::make_unique<AllocPartitionBugVisitor>(MR));
167+
auto R = std::make_unique<PathSensitiveBugReport>(BT_Default, Msg, ErrNode);
168+
R->addRange(SR);
169+
R->markInteresting(MR);
170+
171+
const MemRegion *PrevAlloc =
172+
getAllocationStart(C.getASTContext(), MR, C.getState()).first;
173+
R->addVisitor(std::make_unique<AllocPartitionBugVisitor>(
174+
PrevAlloc == MR ? nullptr : PrevAlloc, MR));
175+
176+
if (const DeclRegion *PrevDecl = getAllocationDecl(PrevAlloc)) {
177+
auto DeclLoc = PathDiagnosticLocation::create(PrevDecl->getDecl(),
178+
C.getSourceManager());
179+
R->addNote("Original allocation", DeclLoc);
180+
}
181+
146182
C.emitReport(std::move(R));
147183
return ErrNode;
148184
}
@@ -157,13 +193,16 @@ void AllocationChecker::checkPostStmt(const CastExpr *CE,
157193
const MemRegion *MR = SrcVal.getAsRegion();
158194
if (!MR)
159195
return;
160-
if (MR->getMemorySpace()->getKind() == MemSpaceRegion::CodeSpaceRegionKind)
196+
const MemSpaceRegion *MemSpace = MR->getMemorySpace();
197+
if (!isa<HeapSpaceRegion, GlobalsSpaceRegion, StackSpaceRegion>(MemSpace))
161198
return;
162199

200+
const ASTContext &ASTCtx = C.getASTContext();
163201
ProgramStateRef State = C.getState();
164202
bool Updated = false;
165203

166-
std::pair<const MemRegion *, bool> StartPair = getAllocationStart(MR, C);
204+
std::pair<const MemRegion *, bool> StartPair =
205+
getAllocationStart(ASTCtx, MR, State);
167206
const MemRegion *SR = StartPair.first;
168207
if (!isAllocation(SR))
169208
return;
@@ -196,9 +235,11 @@ void AllocationChecker::checkPostStmt(const CastExpr *CE,
196235
->getPointeeOrArrayElementType()
197236
->getUnqualifiedDesugaredType();
198237
const Type *Ty2 = DstTy->getPointeeType()->getUnqualifiedDesugaredType();
199-
if (!relatedTypes(Ty1, Ty2)) {
200-
emitAllocationPartitionWarning(CE, C, SR);
201-
return;
238+
if (!relatedTypes(ASTCtx, Ty1, Ty2)) {
239+
State = State->add<SuballocationSet>(SR);
240+
if (DMR)
241+
State = State->add<SuballocationSet>(DMR);
242+
Updated = true;
202243
} // else OK
203244
} // else ??? (ignore for now)
204245
} else {
@@ -210,29 +251,129 @@ void AllocationChecker::checkPostStmt(const CastExpr *CE,
210251
C.addTransition(State);
211252
}
212253

254+
void AllocationChecker::checkPreCall(const CallEvent &Call,
255+
CheckerContext &C) const {
256+
ProgramStateRef State = C.getState();
257+
ExplodedNode *N = nullptr;
258+
bool Updated = false;
259+
for (unsigned Arg = 0; Arg < Call.getNumArgs(); ++Arg) {
260+
const Expr *ArgExpr = Call.getArgExpr(Arg);
261+
if (const MemRegion *MR = C.getSVal(ArgExpr).getAsRegion()) {
262+
if (State->contains<SuballocationSet>(MR)) {
263+
SmallString<256> Buf;
264+
llvm::raw_svector_ostream OS(Buf);
265+
OS << "Pointer to suballocation passed to function as " << (Arg + 1);
266+
if (Arg + 1 < 11) {
267+
switch (Arg + 1) {
268+
case 1:
269+
OS << "st";
270+
break;
271+
case 2:
272+
OS << "nd";
273+
break;
274+
case 3:
275+
OS << "rd";
276+
break;
277+
default:
278+
OS << "th";
279+
break;
280+
}
281+
}
282+
283+
OS << " argument";
284+
OS << " (consider narrowing the bounds for suballocation)";
285+
N = emitAllocationPartitionWarning(C, MR, ArgExpr->getSourceRange(),
286+
OS.str());
287+
if (N) {
288+
State = State->remove<SuballocationSet>(MR);
289+
Updated = true;
290+
}
291+
}
292+
}
293+
}
294+
if (Updated)
295+
C.addTransition(State, N ? N : C.getPredecessor());
296+
}
297+
298+
void AllocationChecker::checkBind(SVal L, SVal V, const Stmt *S,
299+
CheckerContext &C) const {
300+
const MemRegion *Dst = L.getAsRegion();
301+
if (!Dst || !isa<FieldRegion>(Dst))
302+
return;
303+
304+
ProgramStateRef State = C.getState();
305+
const MemRegion *Val = V.getAsRegion();
306+
if (Val && State->contains<SuballocationSet>(Val)) {
307+
ExplodedNode *N = emitAllocationPartitionWarning(
308+
C, Val, S->getSourceRange(),
309+
"Pointer to suballocation escaped on assign"
310+
" (consider narrowing the bounds for suballocation)");
311+
if (N) {
312+
State = State->remove<SuballocationSet>(Val);
313+
C.addTransition(State, N);
314+
}
315+
return;
316+
}
317+
}
318+
319+
void AllocationChecker::checkEndFunction(const ReturnStmt *RS,
320+
CheckerContext &C) const {
321+
if (!RS)
322+
return;
323+
const Expr *RV = RS->getRetValue();
324+
if (!RV)
325+
return;
326+
327+
llvm::SmallVector<EscapePair, 2> Escaped;
328+
if (const MemRegion *MR = C.getSVal(RV).getAsRegion()) {
329+
if (C.getState()->contains<SuballocationSet>(MR)) {
330+
ExplodedNode *N = emitAllocationPartitionWarning(
331+
C, MR, RS->getSourceRange(),
332+
"Pointer to suballocation returned from function"
333+
" (consider narrowing the bounds for suballocation)");
334+
if (N) {
335+
ProgramStateRef State = N->getState()->remove<SuballocationSet>(MR);
336+
C.addTransition(State, N);
337+
}
338+
return;
339+
}
340+
}
341+
}
342+
213343
PathDiagnosticPieceRef AllocationChecker::AllocPartitionBugVisitor::VisitNode(
214344
const ExplodedNode *N, BugReporterContext &BRC,
215345
PathSensitiveBugReport &BR) {
216346
const Stmt *S = N->getStmtForDiagnostics();
217347
if (!S)
218348
return nullptr;
219349

220-
const CastExpr *CE = dyn_cast<CastExpr>(S);
221-
if (!CE || CE->getCastKind() != CK_BitCast)
222-
return nullptr;
223-
224-
if (Reg != N->getSVal(CE->getSubExpr()).getAsRegion())
225-
return nullptr;
350+
if (const CastExpr *CE = dyn_cast<CastExpr>(S)) {
351+
if (CE->getCastKind() != CK_BitCast)
352+
return nullptr;
226353

227-
SmallString<256> Buf;
228-
llvm::raw_svector_ostream OS(Buf);
229-
OS << "Previous partition: ";
230-
describeCast(OS, CE, BRC.getASTContext().getLangOpts());
354+
SmallString<256> Buf;
355+
llvm::raw_svector_ostream OS(Buf);
231356

232-
// Generate the extra diagnostic.
233-
PathDiagnosticLocation const Pos(S, BRC.getSourceManager(),
234-
N->getLocationContext());
235-
return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
357+
if (!PrevReported && PrevAlloc &&
358+
PrevAlloc == N->getSVal(CE->getSubExpr()).getAsRegion()) {
359+
OS << "Previous partition: ";
360+
PrevReported = true;
361+
} else if (SubAlloc == N->getSVal(CE).getAsRegion() &&
362+
N->getState()->contains<SuballocationSet>(SubAlloc) &&
363+
!N->getFirstPred()->getState()->contains<SuballocationSet>(
364+
SubAlloc)) {
365+
OS << "Allocation partition: ";
366+
} else
367+
return nullptr;
368+
369+
describeCast(OS, CE, BRC.getASTContext().getLangOpts());
370+
PathDiagnosticLocation const Pos(S, BRC.getSourceManager(),
371+
N->getLocationContext());
372+
auto Ev = std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
373+
Ev->setPrunable(false);
374+
return Ev;
375+
}
376+
return nullptr;
236377
}
237378

238379
//===----------------------------------------------------------------------===//

clang/lib/StaticAnalyzer/Checkers/CHERI/CHERIUtils.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,17 +53,16 @@ bool hasCapability(const QualType OrigTy, ASTContext &Ctx) {
5353
}
5454

5555
namespace {
56+
5657
void printType(raw_ostream &OS, const QualType &Ty, const PrintingPolicy &PP) {
57-
OS << "'";
58-
Ty.print(OS, PP);
59-
OS << "'";
60-
const QualType &CanTy = Ty.getCanonicalType();
61-
if (CanTy != Ty) {
62-
OS << " (aka '";
63-
CanTy.print(OS, PP);
64-
OS << "')";
58+
std::string TyStr = Ty.getAsString(PP);
59+
OS << "'" << TyStr << "'";
60+
std::string CanTyStr = Ty.getCanonicalType().getAsString(PP);
61+
if (CanTyStr != TyStr) {
62+
OS << " (aka '" << CanTyStr << "')";
6563
}
6664
}
65+
6766
} // namespace
6867

6968
void describeCast(raw_ostream &OS, const CastExpr *CE,
@@ -76,6 +75,14 @@ void describeCast(raw_ostream &OS, const CastExpr *CE,
7675
printType(OS, CE->getType(), PP);
7776
}
7877

78+
const DeclRegion *getAllocationDecl(const MemRegion *MR) {
79+
if (const DeclRegion *DR = MR->getAs<DeclRegion>())
80+
return DR;
81+
if (const ElementRegion *ER = MR->getAs<ElementRegion>())
82+
return getAllocationDecl(ER->getSuperRegion());
83+
return nullptr;
84+
}
85+
7986
} // namespace cheri
8087
} // namespace ento
8188
} // namespace clang

clang/lib/StaticAnalyzer/Checkers/CHERI/CHERIUtils.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ bool hasCapability(const QualType OrigTy, ASTContext &Ctx);
3030
void describeCast(raw_ostream &OS, const CastExpr *CE,
3131
const LangOptions &LangOpts);
3232

33+
const DeclRegion *getAllocationDecl(const MemRegion *MR);
34+
3335
} // namespace cheri
3436
} // namespace ento
3537
} // namespace clang

0 commit comments

Comments
 (0)