clang 24.0.0git
UnsafeBufferUsageAnalysis.cpp
Go to the documentation of this file.
1//===- UnsafeBufferUsageAnalysis.cpp - WPA for UnsafeBufferUsage ----------===//
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// UnsafeBufferUsageAnalysis is a noop analysis.
9//
10// UnsafeBufferUsageAnalysisResult is a map from EntityIds to
11// EntityPointerLevelSets
12//===----------------------------------------------------------------------===//
13
15#include "SSAFAnalysesCommon.h"
26#include "llvm/ADT/STLExtras.h"
27#include "llvm/ADT/iterator_range.h"
28#include "llvm/Support/Error.h"
29#include "llvm/Support/JSON.h"
30#include <memory>
31
32using namespace clang::ssaf;
33using namespace llvm;
34
35namespace {
36
37json::Object serializeUnsafeBufferUsageAnalysisResult(
40 json::Object Result;
41
43 entityPointerLevelMapToJSON(R.UnsafeBuffers, IdToJSON);
44 return Result;
45}
46
48deserializeUnsafeBufferUsageAnalysisResult(
49 const json::Object &Obj, JSONFormat::EntityIdFromJSONFn IdFromJSON) {
50 const json::Array *Content =
52
53 if (!Content)
54 return makeSawButExpectedError(Obj, "an object with a key %s",
56
57 auto UnsafeBuffers = entityPointerLevelMapFromJSON(*Content, IdFromJSON);
58
59 if (!UnsafeBuffers)
60 return UnsafeBuffers.takeError();
61
62 auto Ret = std::make_unique<UnsafeBufferUsageAnalysisResult>();
63
64 Ret->UnsafeBuffers = std::move(*UnsafeBuffers);
65 return std::move(Ret);
66}
67
68JSONFormat::AnalysisResultRegistry::Add<UnsafeBufferUsageAnalysisResult>
69 RegisterUnsafeBufferUsageResultForJSON(
70 serializeUnsafeBufferUsageAnalysisResult,
71 deserializeUnsafeBufferUsageAnalysisResult);
72
73class UnsafeBufferUsageAnalysis final
74 : public SummaryAnalysis<UnsafeBufferUsageAnalysisResult,
75 UnsafeBufferUsageEntitySummary> {
76public:
77 llvm::Error add(EntityId Id,
78 const UnsafeBufferUsageEntitySummary &Summary) override {
79 auto UnsafeBuffersOfEntity = getUnsafeBuffers(Summary);
80
81 getResult().UnsafeBuffers[Id] = EntityPointerLevelSet(
82 UnsafeBuffersOfEntity.begin(), UnsafeBuffersOfEntity.end());
83 return llvm::Error::success();
84 }
85};
86
87AnalysisRegistry::Add<UnsafeBufferUsageAnalysis>
88 RegisterUnsafeBufferUsageAnalysis(
89 "Whole-program unsafe buffer usage analysis");
90
91//===----------------------------------------------------------------------===//
92// UnsafeBufferReachableAnalysis---computes reachable unsafe buffer nodes
93//===----------------------------------------------------------------------===//
94
95json::Object serializeUnsafeBufferReachableAnalysisResult(
98 json::Object Result;
99
101 entityPointerLevelMapToJSON(R.Reachables, IdToJSON);
102 return Result;
103}
104
106deserializeUnsafeBufferReachableAnalysisResult(
107 const json::Object &Obj, JSONFormat::EntityIdFromJSONFn IdFromJSON) {
108 const json::Array *Content =
110
111 if (!Content)
113 Obj, "an object with a key %s",
115
116 auto Reachables = entityPointerLevelMapFromJSON(*Content, IdFromJSON);
117
118 if (!Reachables)
119 return Reachables.takeError();
120
121 auto Ret = std::make_unique<UnsafeBufferReachableAnalysisResult>();
122
123 Ret->Reachables = std::move(*Reachables);
124 return std::move(Ret);
125}
126
127JSONFormat::AnalysisResultRegistry::Add<UnsafeBufferReachableAnalysisResult>
128 RegisterUnsafeBufferReachableResultForJSON(
129 serializeUnsafeBufferReachableAnalysisResult,
130 deserializeUnsafeBufferReachableAnalysisResult);
131
132/// \brief Computes pointers (EPLs) that satisfy a specific set of constraints.
133///
134/// The pointers must satisfy all of the following constraints:
135///
136/// 1. **C1 (Unsafe):** Any pointer in `UnsafeBufferUsageAnalysisResult`
137/// is considered unsafe.
138/// 2. **C2 (Reachable):** If a pointer is reachable from an unsafe pointer in
139/// the pointer flow graph (provided by `PointerFlowAnalysisResult`), it is
140/// also unsafe.
141/// 3. **C3 (Constrained):** Type-constrained entities are NOT unsafe.
142class UnsafeBufferReachableAnalysis
143 : public DerivedAnalysis<UnsafeBufferReachableAnalysisResult,
144 PointerFlowAnalysisResult,
145 TypeConstrainedPointersAnalysisResult,
146 UnsafeBufferUsageAnalysisResult> {
147
148 /// BoundsPropagationGraph adds bounds propagation semantics to the
149 /// pointer-flow graph, which represents the set of static pointer assignment
150 /// sites collected from the source code. Consider the following example:
151 ///
152 /// void f(int ***p, int **q) {
153 /// *p = q;
154 /// (**p)[5] = 0;
155 /// }
156 ///
157 /// There is one static pointer assignment thus one pointer-flow edge: (p, 2)
158 /// -> (q, 1). In terms of bounds propagation, this assignment implies that if
159 /// 'p' at pointer level 2 requires bounds, 'q' at pointer level 1 must also
160 /// have them. Furthermore, this relationship propagates to deeper indirection
161 /// levels: if 'p' at level 3 requires bounds, so does 'q' at level 2.
162 ///
163 /// In the example above, `(**p)` requires bounds (due to the array index),
164 /// and therefore `*q` must require bounds as well.
165 ///
166 /// To generalize the idea, the BoundsPropagationGraph is defined as a super
167 /// graph of the input pointer-flow graph by:
168 ///
169 /// For each edge (src, i) -> (dest, j) in the pointer-flow graph, the
170 /// BoundsPropagationGraph has a finite set of edges
171 /// {(src, i + d) -> (dest, j + d) | 0 <= d < UB}, where UB is an upper
172 /// bound based on the maximum pointer level the pointer type can have.
173 struct BoundsPropagationGraph {
174 private:
175 EdgeSet PointerFlows;
176
177 public:
178 BoundsPropagationGraph(EdgeSet PointerFlows)
179 : PointerFlows(std::move(PointerFlows)) {}
180
181 /// Returns the EntityPointerLevelSet that are reachable from \p Src by
182 /// one edge in the BoundsPropagationGraph.
183 EntityPointerLevelSet getDestNodes(const EntityPointerLevel &Src) const {
184 unsigned SrcPtrLv = Src.getPointerLevel();
185 EntityPointerLevelSet Result;
186
187 for (unsigned P = 1; P <= SrcPtrLv; ++P) {
188 auto I = PointerFlows.find(buildEntityPointerLevel(Src.getEntity(), P));
189
190 if (I != PointerFlows.end()) {
191 unsigned Delta = SrcPtrLv - P;
192 for (const auto &EPL : I->second)
193 Result.insert(buildEntityPointerLevel(
194 EPL.getEntity(), EPL.getPointerLevel() + Delta));
195 }
196 }
197 return Result;
198 }
199 };
200
201 std::map<EntityId, BoundsPropagationGraph> BPG;
202
203 // Use pointers for efficiency. EPLs are in tree-based containers that only
204 // grow. So pointers to them are stable.
205 using EPLPtr = const EntityPointerLevel *;
206
207 // Find all outgoing edges from `EPL` in the `Graph`, insert their
208 // destination nodes into `Reachables`, and add newly discovered nodes to
209 // `Worklist`:
210 void updateReachablesWithOutgoings(EPLPtr EPL,
211 std::vector<EPLPtr> &WorkList) {
212 for (auto &[Id, SubGraph] : BPG) {
213 auto R = SubGraph.getDestNodes(*EPL);
214
215 for (const auto &Dst : R) {
216 auto [It, Inserted] = getResult().Reachables[Id].insert(Dst);
217 if (Inserted)
218 WorkList.push_back(&*It);
219 }
220 }
221 }
222
223 // Expand the initial set of C1 pointers in `getResult().Reachables` by
224 // computing and appending all reachable pointers, satisfying both C1 and C2.
225 void computeReachableUnsafePointers() {
226 auto &Reachables = getResult().Reachables;
227 // Simple DFS:
228 std::vector<EPLPtr> Worklist;
229
230 for (auto &[Id, EPLs] : Reachables)
231 for (auto &EPL : EPLs)
232 Worklist.push_back(&EPL);
233
234 while (!Worklist.empty()) {
235 EPLPtr Node = Worklist.back();
236 Worklist.pop_back();
237
238 updateReachablesWithOutgoings(Node, Worklist);
239 }
240 }
241
242public:
243 llvm::Error
244 initialize(const PointerFlowAnalysisResult &PtrFlowGraph,
245 const TypeConstrainedPointersAnalysisResult &TypeConstraints,
246 const UnsafeBufferUsageAnalysisResult &UnsafePtrs) override {
247 auto HasNoTypeConstraint =
248 [&TypeConstraints](const EntityPointerLevel &EPL) {
249 return !TypeConstraints.contains(EPL.getEntity());
250 };
251
252 // Filter out edges involving type-constrained pointers from `PtrFlowGraph`:
253 for (auto &[Id, SubGraph] : PtrFlowGraph.Edges) {
254 EdgeSet FilteredSubGraph;
255
256 for (const auto &[Src, Dsts] : SubGraph) {
257 if (TypeConstraints.contains(Src.getEntity()))
258 continue;
259
260 auto FilteredDstRange =
261 llvm::make_filter_range(Dsts, HasNoTypeConstraint);
262
263 if (!FilteredDstRange.empty())
264 FilteredSubGraph[Src].insert(FilteredDstRange.begin(),
265 FilteredDstRange.end());
266 }
267 if (!FilteredSubGraph.empty())
268 BPG.try_emplace(Id, std::move(FilteredSubGraph));
269 }
270
271 // Filter out type-constrained pointers from `UnsafePtrs`:
272 for (auto &[Contributor, EPLs] : UnsafePtrs) {
273 auto FilteredRange = llvm::make_filter_range(EPLs, HasNoTypeConstraint);
274
275 if (!FilteredRange.empty())
276 getResult().Reachables[Contributor].insert(FilteredRange.begin(),
277 FilteredRange.end());
278 }
279 return llvm::Error::success();
280 }
281
282 llvm::Expected<bool> step() override {
283 // Compute the reachable EPLs from the C1 unsafe pointers over the
284 // pointer-flow graph; both are already C3-filtered, so the result
285 // satisfies C1, C2, and C3.
286 computeReachableUnsafePointers();
287 // This is not an iterative algorithm so stop iteration by retruning false:
288 return false;
289 }
290};
291
292AnalysisRegistry::Add<UnsafeBufferReachableAnalysis>
293 RegisterUnsafeBufferReachableAnalysis(
294 "Reachable pointers from unsafe buffer usage in pointer flow graph");
295
296} // namespace
297
298namespace clang::ssaf {
299// NOLINTNEXTLINE(misc-use-internal-linkage)
301} // namespace clang::ssaf
Result
Implement __builtin_bit_cast and related operations.
Typed intermediate that concrete derived analyses inherit from.
llvm::function_ref< llvm::Expected< EntityId >(const Object &)> EntityIdFromJSONFn
Definition JSONFormat.h:99
llvm::function_ref< Object(EntityId)> EntityIdToJSONFn
Definition JSONFormat.h:98
Typed intermediate that concrete summary analyses inherit from.
PRESERVE_NONE bool Ret(InterpState &S)
Definition Interp.h:289
Expected< std::map< EntityId, EntityPointerLevelSet > > entityPointerLevelMapFromJSON(const llvm::json::Array &Content, JSONFormat::EntityIdFromJSONFn IdFromJSON)
Deserialize a flat array of alternating [EntityId, EntityPointerLevelSet, ...] pairs into a map.
std::map< EntityPointerLevel, EntityPointerLevelSet > EdgeSet
Maps each LHS pointer (source / assignee) to the set of RHS pointers (destinations / assigned values)...
Definition PointerFlow.h:24
volatile int UnsafeBufferUsageAnalysisAnchorSource
llvm::Error makeSawButExpectedError(const JSONTy &Saw, llvm::StringRef Expected, const Ts &...ExpectedArgs)
constexpr llvm::StringLiteral UnsafeBufferUsageAnalysisResultName
constexpr llvm::StringLiteral UnsafeBufferReachableAnalysisResultName
llvm::json::Array entityPointerLevelMapToJSON(const std::map< EntityId, EntityPointerLevelSet > &Map, JSONFormat::EntityIdToJSONFn IdToJSON)
Serialize a map<EntityId, EntityPointerLevelSet> as a flat array of alternating [EntityId,...
Diagnostic wrappers for TextAPI types for error reporting.
Definition Dominators.h:30
float __ovld __cnfn step(float, float)
Returns 0.0 if x < edge, otherwise it returns 1.0.
std::map< EntityId, EdgeSet > Edges