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
2425using namespace clang ;
2526using namespace ento ;
2627using 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
5367public:
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
5673private:
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
6482REGISTER_MAP_WITH_PROGRAMSTATE (AllocMap, const MemRegion *, QualType)
6583REGISTER_MAP_WITH_PROGRAMSTATE(ShiftMap, const MemRegion *, const MemRegion *)
84+ REGISTER_SET_WITH_PROGRAMSTATE(SuballocationSet, const MemRegion *)
6685
6786namespace {
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
134163ExplodedNode *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+
213343PathDiagnosticPieceRef 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// ===----------------------------------------------------------------------===//
0 commit comments