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