clang 23.0.0git
UnsafeBufferUsageExtractor.cpp
Go to the documentation of this file.
1//===- UnsafeBufferUsageExtractor.cpp -------------------------------------===//
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
12#include "clang/AST/Decl.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/ADT/iterator_range.h"
20#include "llvm/Support/Error.h"
21#include <memory>
22
23namespace {
24using namespace clang;
25using namespace ssaf;
26
27static bool hasPointerType(const Expr *E) {
28 auto Ty = E->getType();
29 return !Ty.isNull() && !Ty->isFunctionPointerType() &&
30 (Ty->isPointerType() || Ty->isArrayType());
31}
32
33constexpr inline auto buildEntityPointerLevel =
35
36// Translate a pointer type expression 'E' to a (set of) EntityPointerLevel(s)
37// associated with the declared type of the base address of `E`. If the base
38// address of `E` is not associated with an entity, the translation result is an
39// empty set.
40//
41// The translation is a process of traversing into the pointer 'E' until its
42// base address can be represented by an entity, with the number of dereferences
43// tracked by incrementing the pointer level. Naturally, taking address of, as
44// the inverse operation of dereference, is tracked by decrementing the pointer
45// level.
46//
47// For example, suppose there are pointers and arrays declared as
48// int *ptr, **p1, **p2;
49// int arr[10][10];
50// , the translation of expressions involving these base addresses will be:
51// Translate(ptr + 5) -> {(ptr, 1)}
52// Translate(arr[5]) -> {(arr, 2)}
53// Translate(cond ? p1[5] : p2) -> {(p1, 2), (p2, 1)}
54// Translate(&arr[5]) -> {(arr, 1)}
55class EntityPointerLevelTranslator
56 : ConstStmtVisitor<EntityPointerLevelTranslator,
57 Expected<EntityPointerLevelSet>> {
58 friend class StmtVisitorBase;
59
60 // Fallback method for all unsupported expression kind:
61 llvm::Error fallback(const Stmt *E) {
62 return llvm::createStringError(
63 "unsupported expression kind for translation to "
64 "EntityPointerLevel: %s",
65 E->getStmtClassName());
66 }
67
68 static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) {
69 return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() + 1);
70 }
71
72 static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) {
73 assert(E.getPointerLevel() > 0);
74 return buildEntityPointerLevel(E.getEntity(), E.getPointerLevel() - 1);
75 }
76
77 EntityPointerLevel createEntityPointerLevelFor(const EntityName &Name) {
78 return buildEntityPointerLevel(Extractor.addEntity(Name), 1);
79 }
80
81 // The common helper function for Translate(*base):
82 // Translate(*base) -> Translate(base) with .pointerLevel + 1
83 Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) {
84 assert(hasPointerType(Ptr));
85
86 Expected<EntityPointerLevelSet> SubResult = Visit(Ptr);
87 if (!SubResult)
88 return SubResult.takeError();
89
90 auto Incremented = llvm::map_range(*SubResult, incrementPointerLevel);
91 return EntityPointerLevelSet{Incremented.begin(), Incremented.end()};
92 }
93
94 UnsafeBufferUsageTUSummaryExtractor &Extractor;
95
96public:
97 EntityPointerLevelTranslator(UnsafeBufferUsageTUSummaryExtractor &Extractor)
98 : Extractor(Extractor) {}
99
100 Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); }
101
102private:
103 Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) {
104 return fallback(E);
105 }
106
107 // Translate(base + x) -> Translate(base)
108 // Translate(x + base) -> Translate(base)
109 // Translate(base - x) -> Translate(base)
110 // Translate(base {+=, -=, =} x) -> Translate(base)
111 // Translate(x, base) -> Translate(base)
112 Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) {
113 switch (E->getOpcode()) {
114 case clang::BO_Add:
115 if (hasPointerType(E->getLHS()))
116 return Visit(E->getLHS());
117 return Visit(E->getRHS());
118 case clang::BO_Sub:
119 case clang::BO_AddAssign:
120 case clang::BO_SubAssign:
121 case clang::BO_Assign:
122 return Visit(E->getLHS());
123 case clang::BO_Comma:
124 return Visit(E->getRHS());
125 default:
126 return fallback(E);
127 }
128 }
129
130 // Translate({++, --}base) -> Translate(base)
131 // Translate(base{++, --}) -> Translate(base)
132 // Translate(*base) -> Translate(base) with .pointerLevel += 1
133 // Translate(&base) -> {}, if Translate(base) is {}
134 // -> Translate(base) with .pointerLevel -= 1
135 Expected<EntityPointerLevelSet> VisitUnaryOperator(const UnaryOperator *E) {
136 switch (E->getOpcode()) {
137 case clang::UO_PostInc:
138 case clang::UO_PostDec:
139 case clang::UO_PreInc:
140 case clang::UO_PreDec:
141 return Visit(E->getSubExpr());
142 case clang::UO_AddrOf: {
143 Expected<EntityPointerLevelSet> SubResult = Visit(E->getSubExpr());
144 if (!SubResult)
145 return SubResult.takeError();
146
147 auto Decremented = llvm::map_range(*SubResult, decrementPointerLevel);
148 return EntityPointerLevelSet{Decremented.begin(), Decremented.end()};
149 }
150 case clang::UO_Deref:
151 return translateDereferencePointer(E->getSubExpr());
152 default:
153 return fallback(E);
154 }
155 }
156
157 // Translate((T*)base) -> Translate(p) if p has pointer type
158 // -> {} otherwise
159 Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) {
160 if (hasPointerType(E->getSubExpr()))
161 return Visit(E->getSubExpr());
162 return EntityPointerLevelSet{};
163 }
164
165 // Translate(f(...)) -> {} if it is an indirect call
166 // -> {(f_return, 1)}, otherwise
167 Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) {
168 if (auto *FD = E->getDirectCallee())
169 if (auto FDEntityName = getEntityNameForReturn(FD))
171 createEntityPointerLevelFor(*FDEntityName)};
172 return EntityPointerLevelSet{};
173 }
174
175 // Translate(base[x]) -> Translate(*base)
176 Expected<EntityPointerLevelSet>
177 VisitArraySubscriptExpr(const ArraySubscriptExpr *E) {
178 return translateDereferencePointer(E->getBase());
179 }
180
181 // Translate(cond ? base1 : base2) := Translate(base1) U Translate(base2)
182 Expected<EntityPointerLevelSet>
183 VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) {
184 Expected<EntityPointerLevelSet> ReT = Visit(E->getTrueExpr());
185 Expected<EntityPointerLevelSet> ReF = Visit(E->getFalseExpr());
186
187 if (ReT && ReF) {
188 ReT->insert(ReF->begin(), ReF->end());
189 return ReT;
190 }
191 if (!ReF && !ReT)
192 return llvm::joinErrors(ReT.takeError(), ReF.takeError());
193 if (!ReF)
194 return ReF.takeError();
195 return ReT.takeError();
196 }
197
198 Expected<EntityPointerLevelSet> VisitParenExpr(const ParenExpr *E) {
199 return Visit(E->getSubExpr());
200 }
201
202 // Translate("string-literal") -> {}
203 // Buffer accesses on string literals are unsafe, but string literals are not
204 // entities so there is no EntityPointerLevel associated with it.
205 Expected<EntityPointerLevelSet> VisitStringLiteral(const StringLiteral *E) {
206 return EntityPointerLevelSet{};
207 }
208
209 // Translate(DRE) -> {(Decl, 1)}
210 Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) {
211 if (auto EntityName = getEntityName(E->getDecl()))
212 return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
213 return llvm::createStringError(
214 "failed to create entity name from the Decl of " +
215 E->getNameInfo().getAsString());
216 }
217
218 // Translate({., ->}f) -> {(MemberDecl, 1)}
219 Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) {
220 if (auto EntityName = getEntityName(E->getMemberDecl()))
221 return EntityPointerLevelSet{createEntityPointerLevelFor(*EntityName)};
222 return llvm::createStringError(
223 "failed to create entity name from the MemberDecl of " +
225 }
226
227 Expected<EntityPointerLevelSet>
228 VisitOpaqueValueExpr(const OpaqueValueExpr *S) {
229 return Visit(S->getSourceExpr());
230 }
231};
232
234buildEntityPointerLevels(std::set<const Expr *> &&UnsafePointers,
236 EntityPointerLevelSet Result{};
237 EntityPointerLevelTranslator Translator{Extractor};
238 llvm::Error AllErrors = llvm::ErrorSuccess();
239
240 for (const Expr *Ptr : UnsafePointers) {
241 Expected<EntityPointerLevelSet> Translation = Translator.translate(Ptr);
242
243 if (Translation) {
244 // Filter out those temporary invalid EntityPointerLevels associated with
245 // `&E` pointers:
246 auto FilteredTranslation = llvm::make_filter_range(
247 *Translation, [](const EntityPointerLevel &E) -> bool {
248 return E.getPointerLevel() > 0;
249 });
250 Result.insert(FilteredTranslation.begin(), FilteredTranslation.end());
251 continue;
252 }
253 AllErrors = llvm::joinErrors(std::move(AllErrors), Translation.takeError());
254 }
255 if (AllErrors)
256 return AllErrors;
257 return Result;
258}
259} // namespace
260
261std::unique_ptr<UnsafeBufferUsageEntitySummary>
263 const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error) {
264 // FIXME: findUnsafePointers should accept more kinds of `Decl`s than just
265 // `FunctionDecl`:
266 if (const auto *FD = dyn_cast<FunctionDecl>(Contributor)) {
268 buildEntityPointerLevels(findUnsafePointers(FD), *this);
269
270 if (EPLs)
271 return std::make_unique<UnsafeBufferUsageEntitySummary>(
272 UnsafeBufferUsageEntitySummary(std::move(*EPLs)));
273 Error = EPLs.takeError();
274 }
275 return nullptr;
276}
277
279 ASTContext &Ctx) {
280 // FIXME: impl me!
281}
Defines the clang::ASTContext interface.
std::set< EntityPointerLevel, EntityPointerLevel::Comparator > EntityPointerLevelSet
static bool hasPointerType(const Expr &E)
An UnsafeBufferUsageEntitySummary is an immutable set of unsafe buffers, in the form of EntityPointer...
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:226
Expr * getTrueExpr() const
getTrueExpr - Return the subexpression representing the value of the expression if the condition eval...
Definition Expr.h:4540
Expr * getFalseExpr() const
getFalseExpr - Return the subexpression representing the value of the expression if the condition eva...
Definition Expr.h:4546
Expr * getLHS() const
Definition Expr.h:4091
Expr * getRHS() const
Definition Expr.h:4093
Opcode getOpcode() const
Definition Expr.h:4086
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition Expr.h:3129
Expr * getSubExpr()
Definition Expr.h:3729
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
DeclarationNameInfo getNameInfo() const
Definition Expr.h:1345
ValueDecl * getDecl()
Definition Expr.h:1341
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
This represents one expression.
Definition Expr.h:112
QualType getType() const
Definition Expr.h:144
ValueDecl * getMemberDecl() const
Retrieve the member declaration to which this expression refers.
Definition Expr.h:3450
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
Definition Decl.h:317
Expr * getSourceExpr() const
The source expression of an opaque value expression is the expression which originally generated the ...
Definition Expr.h:1231
const Expr * getSubExpr() const
Definition Expr.h:2202
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition TypeBase.h:1004
const char * getStmtClassName() const
Definition Stmt.cpp:86
Expr * getSubExpr() const
Definition Expr.h:2288
Opcode getOpcode() const
Definition Expr.h:2283
static EntityPointerLevel buildEntityPointerLevel(EntityId Entity, unsigned PointerLevel)
void HandleTranslationUnit(ASTContext &Ctx) override
HandleTranslationUnit - This method is called when the ASTs for entire translation unit have been par...
std::unique_ptr< UnsafeBufferUsageEntitySummary > extractEntitySummary(const Decl *Contributor, ASTContext &Ctx, llvm::Error &Error)
std::optional< EntityName > getEntityNameForReturn(const FunctionDecl *FD)
Maps return entity of a function to an EntityName.
std::optional< EntityName > getEntityName(const Decl *D)
Maps a declaration to an EntityName.
The JSON file list parser is used to communicate input to InstallAPI.
std::set< const Expr * > findUnsafePointers(const FunctionDecl *FD)
std::string getAsString() const
getAsString - Retrieve the human-readable string for this name.