Skip to content

Commit ed1e296

Browse files
eupharinaresistor
authored andcommitted
[CHERI_CSA] AllocationChecker: rework
1 parent 045bab7 commit ed1e296

File tree

5 files changed

+245
-84
lines changed

5 files changed

+245
-84
lines changed

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

Lines changed: 188 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,66 +14,94 @@
1414
//
1515
//===----------------------------------------------------------------------===//
1616
#include "CHERIUtils.h"
17-
#include "clang/StaticAnalyzer/Core/Checker.h"
18-
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19-
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
2017
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18+
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19+
#include "clang/StaticAnalyzer/Core/Checker.h"
2120
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
21+
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
2222
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
23+
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
2324

2425

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

29-
namespace {
3030

31-
class AllocationChecker : public Checker<check::PostStmt<CastExpr>> {
31+
struct EscapeInfo {
32+
PointerEscapeKind Kind;
33+
EscapeInfo(PointerEscapeKind K) : Kind(K) {};
34+
bool operator==(const EscapeInfo &X) const {
35+
return Kind == X.Kind;
36+
}
37+
void Profile(llvm::FoldingSetNodeID &ID) const {
38+
ID.AddInteger(Kind);
39+
}
40+
};
41+
using EscapePair = std::pair<const MemRegion*, EscapeInfo>;
42+
43+
namespace {
44+
class AllocationChecker : public Checker<check::PostStmt<CastExpr>,
45+
check::PreCall,
46+
check::Bind,
47+
check::EndFunction> {
3248
BugType BT_Default{this, "Allocation partitioning", "CHERI portability"};
3349
BugType BT_KnownReg{this, "Heap or static allocation partitioning",
3450
"CHERI portability"};
3551

3652
class AllocPartitionBugVisitor : public BugReporterVisitor {
3753
public:
38-
AllocPartitionBugVisitor(const MemRegion *R) : Reg(R) {}
54+
AllocPartitionBugVisitor(const MemRegion *P, const MemRegion *A)
55+
: PrevAlloc(P), SubAlloc(A) {}
3956

4057
void Profile(llvm::FoldingSetNodeID &ID) const override {
4158
static int X = 0;
4259
ID.AddPointer(&X);
43-
ID.AddPointer(Reg);
60+
ID.AddPointer(PrevAlloc);
61+
ID.AddPointer(SubAlloc);
62+
4463
}
4564

4665
PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
4766
BugReporterContext &BRC,
4867
PathSensitiveBugReport &BR) override;
4968

5069
private:
51-
const MemRegion *Reg;
70+
const MemRegion *PrevAlloc;
71+
const MemRegion *SubAlloc;
72+
bool PrevReported = false;
5273
};
5374

5475
public:
5576
void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
77+
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
78+
void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
79+
void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
5680

5781
private:
58-
ExplodedNode *emitAllocationPartitionWarning(const CastExpr *CE,
59-
CheckerContext &C,
60-
const MemRegion *R) const;
82+
ExplodedNode *emitAllocationPartitionWarning(CheckerContext &C,
83+
const MemRegion *MR,
84+
SourceRange SR,
85+
StringRef Msg) const;
6186
};
6287

6388
} // namespace
6489

6590
REGISTER_MAP_WITH_PROGRAMSTATE(AllocMap, const MemRegion *, QualType)
6691
REGISTER_MAP_WITH_PROGRAMSTATE(ShiftMap, const MemRegion *, const MemRegion *)
92+
REGISTER_SET_WITH_PROGRAMSTATE(SuballocationSet, const MemRegion *)
6793

6894
namespace {
69-
std::pair<const MemRegion *, bool> getAllocationStart(const MemRegion *R,
70-
CheckerContext &C,
95+
std::pair<const MemRegion *, bool> getAllocationStart(const ASTContext &ASTCtx,
96+
const MemRegion *R,
97+
ProgramStateRef State,
7198
bool ZeroShift = true) {
7299
if (const ElementRegion *ER = R->getAs<ElementRegion>()) {
73100
const MemRegion *Base = ER->getSuperRegion();
74-
return getAllocationStart(Base, C, ER->getIndex().isZeroConstant());
101+
return getAllocationStart(ASTCtx, Base, State,
102+
ZeroShift && ER->getIndex().isZeroConstant());
75103
}
76-
if (auto OrigR = C.getState()->get<ShiftMap>(R)) {
104+
if (const auto *OrigR = State->get<ShiftMap>(R)) {
77105
return std::make_pair(*OrigR, false);
78106
}
79107
return std::make_pair(R, ZeroShift);
@@ -88,17 +116,22 @@ bool isAllocation(const MemRegion *R) {
88116
return false;
89117
}
90118

91-
bool relatedTypes(const Type *Ty1, const Type *Ty2) {
119+
bool relatedTypes(const ASTContext &ASTCtx, const Type *Ty1, const Type *Ty2) {
92120
if (Ty1 == Ty2)
93121
return true;
122+
if (Ty1->isIntegerType()) {
123+
if (Ty2->isIntegerType())
124+
return ASTCtx.getTypeSize(Ty1) == ASTCtx.getTypeSize(Ty2);
125+
return false;
126+
}
94127
if (Ty1->isArrayType())
95-
return relatedTypes(Ty1->getArrayElementTypeNoTypeQual(), Ty2);
128+
return relatedTypes(ASTCtx, Ty1->getArrayElementTypeNoTypeQual(), Ty2);
96129
if (Ty1->isRecordType()) {
97130
if (RecordDecl *RD = Ty1->getAs<RecordType>()->getAsRecordDecl()) {
98131
const RecordDecl::field_iterator &FirstField = RD->field_begin();
99132
if (FirstField != RD->field_end()) {
100133
const Type *FFTy = FirstField->getType()->getUnqualifiedDesugaredType();
101-
return relatedTypes(FFTy, Ty2);
134+
return relatedTypes(ASTCtx, FFTy, Ty2);
102135
}
103136
}
104137
}
@@ -110,8 +143,11 @@ bool reportForType(QualType Ty) {
110143
return false;
111144
if (Ty->isPointerType() || Ty->isArrayType()) {
112145
const Type *PTy = Ty->getPointeeOrArrayElementType();
146+
PTy = PTy->getUnqualifiedDesugaredType();
113147
if (PTy->isCharType())
114148
return false;
149+
if (PTy->isPointerType())
150+
return false;
115151
if (PTy->isStructureTypeWithFlexibleArrayMember())
116152
return false;
117153
return true;
@@ -133,17 +169,24 @@ std::optional<QualType> getPrevType(ProgramStateRef State, const MemRegion *R) {
133169
} // namespace
134170

135171
ExplodedNode *AllocationChecker::emitAllocationPartitionWarning(
136-
const CastExpr *CE, CheckerContext &C, const MemRegion *MR) const {
172+
CheckerContext &C, const MemRegion *MR, SourceRange SR,
173+
StringRef Msg = "") const {
137174
if (ExplodedNode *ErrNode = C.generateNonFatalErrorNode()) {
138-
SmallString<256> Buf;
139-
llvm::raw_svector_ostream OS(Buf);
140-
OS << "Allocation partition: ";
141-
describeCast(OS, CE, C.getASTContext().getLangOpts());
142-
const MemSpaceRegion *MemSpace = MR->getMemorySpace();
143-
bool KnownReg = isa<HeapSpaceRegion, GlobalsSpaceRegion>(MemSpace);
144-
auto R = std::make_unique<PathSensitiveBugReport>(
145-
KnownReg ? BT_KnownReg : BT_Default, OS.str(), ErrNode);
146-
R->addVisitor(std::make_unique<AllocPartitionBugVisitor>(MR));
175+
auto R = std::make_unique<PathSensitiveBugReport>(BT_Default, Msg, ErrNode);
176+
R->addRange(SR);
177+
R->markInteresting(MR);
178+
179+
const MemRegion *PrevAlloc =
180+
getAllocationStart(C.getASTContext(), MR, C.getState()).first;
181+
R->addVisitor(std::make_unique<AllocPartitionBugVisitor>(
182+
PrevAlloc == MR ? nullptr : PrevAlloc, MR));
183+
184+
if (const DeclRegion *PrevDecl = getAllocationDecl(PrevAlloc)) {
185+
auto DeclLoc = PathDiagnosticLocation::create(PrevDecl->getDecl(),
186+
C.getSourceManager());
187+
R->addNote("Original allocation", DeclLoc);
188+
}
189+
147190
C.emitReport(std::move(R));
148191
return ErrNode;
149192
}
@@ -158,13 +201,16 @@ void AllocationChecker::checkPostStmt(const CastExpr *CE,
158201
const MemRegion *MR = SrcVal.getAsRegion();
159202
if (!MR)
160203
return;
161-
if (MR->getMemorySpace()->getKind() == MemSpaceRegion::CodeSpaceRegionKind)
204+
const MemSpaceRegion *MemSpace = MR->getMemorySpace();
205+
if (!isa<HeapSpaceRegion, GlobalsSpaceRegion, StackSpaceRegion>(MemSpace))
162206
return;
163207

208+
const ASTContext &ASTCtx = C.getASTContext();
164209
ProgramStateRef State = C.getState();
165210
bool Updated = false;
166211

167-
std::pair<const MemRegion *, bool> StartPair = getAllocationStart(MR, C);
212+
std::pair<const MemRegion *, bool> StartPair =
213+
getAllocationStart(ASTCtx, MR, State);
168214
const MemRegion *SR = StartPair.first;
169215
if (!isAllocation(SR))
170216
return;
@@ -197,9 +243,11 @@ void AllocationChecker::checkPostStmt(const CastExpr *CE,
197243
->getPointeeOrArrayElementType()
198244
->getUnqualifiedDesugaredType();
199245
const Type *Ty2 = DstTy->getPointeeType()->getUnqualifiedDesugaredType();
200-
if (!relatedTypes(Ty1, Ty2)) {
201-
emitAllocationPartitionWarning(CE, C, SR);
202-
return;
246+
if (!relatedTypes(ASTCtx, Ty1, Ty2)) {
247+
State = State->add<SuballocationSet>(SR);
248+
if (DMR)
249+
State = State->add<SuballocationSet>(DMR);
250+
Updated = true;
203251
} // else OK
204252
} // else ??? (ignore for now)
205253
} else {
@@ -211,29 +259,122 @@ void AllocationChecker::checkPostStmt(const CastExpr *CE,
211259
C.addTransition(State);
212260
}
213261

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

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

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

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

239380
//===----------------------------------------------------------------------===//

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

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

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

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

79+
const DeclRegion *getAllocationDecl(const MemRegion *MR) {
80+
if (const DeclRegion *DR = MR->getAs<DeclRegion>())
81+
return DR;
82+
if (const ElementRegion *ER = MR->getAs<ElementRegion>())
83+
return getAllocationDecl(ER->getSuperRegion());
84+
return nullptr;
85+
}
86+
8087

8188
} // end of namespace: cheri
8289
} // end of namespace: ento

0 commit comments

Comments
 (0)