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
2526using namespace clang ;
2627using namespace ento ;
2728using 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
5475public:
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
5781private:
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
6590REGISTER_MAP_WITH_PROGRAMSTATE (AllocMap, const MemRegion *, QualType)
6691REGISTER_MAP_WITH_PROGRAMSTATE(ShiftMap, const MemRegion *, const MemRegion *)
92+ REGISTER_SET_WITH_PROGRAMSTATE(SuballocationSet, const MemRegion *)
6793
6894namespace {
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
135171ExplodedNode *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+
214344PathDiagnosticPieceRef 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// ===----------------------------------------------------------------------===//
0 commit comments