1+ // ===-- AllocationChecker.cpp - Allocation Checker -*- C++ -*--------------===//
2+ //
3+ // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+ // See https://llvm.org/LICENSE.txt for license information.
5+ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+ //
7+ // ===----------------------------------------------------------------------===//
8+ //
9+ // This file defines checker that detects unrelated objects being allocated as
10+ // adjacent suballocations of the bigger memory allocation. It reports
11+ // situations when an address of such object escapes the function and
12+ // suggests narrowing the bounds of the escaping capability, so it covers only
13+ // the current suballocation, following the principle of least privilege.
14+ //
15+ // ===----------------------------------------------------------------------===//
16+ #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"
20+ #include " clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
21+ #include " clang/StaticAnalyzer/Core/CheckerManager.h"
22+ #include " clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
23+
24+
25+ using namespace clang ;
26+ using namespace ento ;
27+ using namespace cheri ;
28+
29+ namespace {
30+
31+ class AllocationChecker : public Checker <check::PostStmt<CastExpr>> {
32+ BugType BT_1{this , " Allocation partitioning" , " CHERI portability" };
33+
34+ class AllocPartitionBugVisitor : public BugReporterVisitor {
35+ public:
36+ AllocPartitionBugVisitor (const MemRegion *R) : Reg(R) {}
37+
38+ void Profile (llvm::FoldingSetNodeID &ID) const override {
39+ static int X = 0 ;
40+ ID.AddPointer (&X);
41+ ID.AddPointer (Reg);
42+ }
43+
44+ PathDiagnosticPieceRef VisitNode (const ExplodedNode *N,
45+ BugReporterContext &BRC,
46+ PathSensitiveBugReport &BR) override ;
47+
48+ private:
49+ const MemRegion *Reg;
50+ };
51+
52+ public:
53+ void checkPostStmt (const CastExpr *CE, CheckerContext &C) const ;
54+
55+ private:
56+ ExplodedNode *emitAllocationPartitionWarning (const CastExpr *CE,
57+ CheckerContext &C,
58+ const MemRegion *R) const ;
59+ };
60+
61+ } // namespace
62+
63+ REGISTER_MAP_WITH_PROGRAMSTATE (AllocMap, const MemRegion *, QualType)
64+ REGISTER_MAP_WITH_PROGRAMSTATE(ShiftMap, const MemRegion *, const MemRegion *)
65+
66+ namespace {
67+ std::pair<const MemRegion *, bool > getAllocationStart (const MemRegion *R,
68+ CheckerContext &C,
69+ bool ZeroShift = true ) {
70+ if (const ElementRegion *ER = R->getAs <ElementRegion>()) {
71+ const MemRegion *Base = ER->getSuperRegion ();
72+ return getAllocationStart (Base, C, ER->getIndex ().isZeroConstant ());
73+ }
74+ if (auto OrigR = C.getState ()->get <ShiftMap>(R)) {
75+ return std::make_pair (*OrigR, false );
76+ }
77+ return std::make_pair (R, ZeroShift);
78+ }
79+
80+ bool isAllocation (const MemRegion *R) {
81+ if (R->getAs <SymbolicRegion>())
82+ return true ;
83+ if (const TypedValueRegion *TR = R->getAs <TypedValueRegion>()) {
84+ return TR->getValueType ()->isArrayType ();
85+ }
86+ return false ;
87+ }
88+
89+ bool relatedTypes (const Type *Ty1, const Type *Ty2) {
90+ if (Ty1 == Ty2)
91+ return true ;
92+ if (Ty1->isArrayType ())
93+ return relatedTypes (Ty1->getArrayElementTypeNoTypeQual (), Ty2);
94+ return false ;
95+ }
96+
97+ bool isGenPtrType (QualType Ty) {
98+ return Ty->isVoidPointerType () ||
99+ ((Ty->isPointerType () || Ty->isArrayType ()) &&
100+ Ty->getPointeeOrArrayElementType ()->isCharType ());
101+ }
102+
103+ std::optional<QualType> getPrevType (ProgramStateRef State, const MemRegion *R) {
104+ if (const QualType *PrevTy = State->get <AllocMap>(R))
105+ return *PrevTy;
106+ if (const TypedValueRegion *TR = R->getAs <TypedValueRegion>()) {
107+ QualType Ty = TR->getValueType ();
108+ if ((Ty->isPointerType () || Ty->isArrayType ()) && !isGenPtrType (Ty))
109+ return Ty;
110+ }
111+ return std::nullopt ;
112+ }
113+
114+ } // namespace
115+
116+ ExplodedNode *AllocationChecker::emitAllocationPartitionWarning (
117+ const CastExpr *CE, CheckerContext &C, const MemRegion *MR) const {
118+ if (ExplodedNode *ErrNode = C.generateNonFatalErrorNode ()) {
119+ SmallString<256 > Buf;
120+ llvm::raw_svector_ostream OS (Buf);
121+ OS << " Allocation partition: " ;
122+ describeCast (OS, CE, C.getASTContext ().getLangOpts ());
123+ auto R = std::make_unique<PathSensitiveBugReport>(
124+ BT_1, OS.str (), ErrNode);
125+ R->addVisitor (std::make_unique<AllocPartitionBugVisitor>(MR));
126+ C.emitReport (std::move (R));
127+ return ErrNode;
128+ }
129+ return nullptr ;
130+ }
131+
132+ void AllocationChecker::checkPostStmt (const CastExpr *CE,
133+ CheckerContext &C) const {
134+ if (CE->getCastKind () != CK_BitCast)
135+ return ;
136+ SVal SrcVal = C.getSVal (CE->getSubExpr ());
137+ const MemRegion *MR = SrcVal.getAsRegion ();
138+ if (!MR)
139+ return ;
140+ if (MR->getMemorySpace ()->getKind () == MemSpaceRegion::CodeSpaceRegionKind)
141+ return ;
142+
143+ ProgramStateRef State = C.getState ();
144+ bool Updated = false ;
145+
146+ std::pair<const MemRegion *, bool > StartPair = getAllocationStart (MR, C);
147+ const MemRegion *SR = StartPair.first ;
148+ if (!isAllocation (SR))
149+ return ;
150+ bool ZeroShift = StartPair.second ;
151+
152+ SVal DstVal = C.getSVal (CE);
153+ const MemRegion *DMR = DstVal.getAsRegion ();
154+ if (MR->getAs <ElementRegion>() && (!DMR || !DMR->getAs <ElementRegion>())) {
155+ if (DstVal.isUnknown ()) {
156+ const LocationContext *LCtx = C.getLocationContext ();
157+ DstVal = C.getSValBuilder ().conjureSymbolVal (
158+ nullptr , CE, LCtx, CE->getType (), C.blockCount ());
159+ State = State->BindExpr (CE, LCtx, DstVal);
160+ DMR = DstVal.getAsRegion ();
161+ }
162+ if (DMR) {
163+ State = State->set <ShiftMap>(DMR, SR);
164+ Updated = true ;
165+ }
166+ }
167+
168+ QualType DstTy = CE->getType ().getUnqualifiedType ();
169+ if (!DstTy->isPointerType ())
170+ return ;
171+ if (DstTy->isVoidPointerType () || DstTy->getPointeeType ()->isCharType ())
172+ return ;
173+
174+ std::optional<QualType> PrevTy = getPrevType (State, SR);
175+ if (PrevTy) {
176+ if (SR != MR && !ZeroShift) {
177+ const Type *Ty1 = PrevTy->getTypePtr ()
178+ ->getPointeeOrArrayElementType ()
179+ ->getUnqualifiedDesugaredType ();
180+ const Type *Ty2 = DstTy->getPointeeType ()->getUnqualifiedDesugaredType ();
181+ if (!relatedTypes (Ty1, Ty2)) {
182+ emitAllocationPartitionWarning (CE, C, SR);
183+ return ;
184+ } // else OK
185+ } // else ??? (ignore for now)
186+ } else {
187+ State = State->set <AllocMap>(SR, DstTy);
188+ Updated = true ;
189+ }
190+
191+ if (Updated)
192+ C.addTransition (State);
193+ }
194+
195+ PathDiagnosticPieceRef AllocationChecker::AllocPartitionBugVisitor::VisitNode (
196+ const ExplodedNode *N, BugReporterContext &BRC,
197+ PathSensitiveBugReport &BR) {
198+ const Stmt *S = N->getStmtForDiagnostics ();
199+ if (!S)
200+ return nullptr ;
201+
202+ const CastExpr *CE = dyn_cast<CastExpr>(S);
203+ if (!CE || CE->getCastKind () != CK_BitCast)
204+ return nullptr ;
205+
206+ if (Reg != N->getSVal (CE->getSubExpr ()).getAsRegion ())
207+ return nullptr ;
208+
209+ SmallString<256 > Buf;
210+ llvm::raw_svector_ostream OS (Buf);
211+ OS << " Previous partition: " ;
212+ describeCast (OS, CE, BRC.getASTContext ().getLangOpts ());
213+
214+ // Generate the extra diagnostic.
215+ PathDiagnosticLocation const Pos (S, BRC.getSourceManager (),
216+ N->getLocationContext ());
217+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str (), true );
218+ }
219+
220+ // ===----------------------------------------------------------------------===//
221+ // Checker registration.
222+ // ===----------------------------------------------------------------------===//
223+
224+ void ento::registerAllocationChecker (CheckerManager &Mgr) {
225+ Mgr.registerChecker <AllocationChecker>();
226+ }
227+
228+ bool ento::shouldRegisterAllocationChecker (const CheckerManager &Mgr) {
229+ return true ;
230+ }
0 commit comments