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// UnsafeBufferReachableAnalysisResult is a flat set of EntityPointerLevels
14// reachable from unsafe buffer usage.
15//===----------------------------------------------------------------------===//
16
18#include "SSAFAnalysesCommon.h"
29#include "llvm/ADT/STLExtras.h"
30#include "llvm/ADT/iterator_range.h"
31#include "llvm/Support/Error.h"
32#include "llvm/Support/JSON.h"
33#include <memory>
34
35using namespace clang::ssaf;
36using namespace llvm;
37
38namespace {
39
40json::Object serializeUnsafeBufferUsageAnalysisResult(
43 json::Object Result;
44
46 entityPointerLevelMapToJSON(R.UnsafeBuffers, IdToJSON);
47 return Result;
48}
49
51deserializeUnsafeBufferUsageAnalysisResult(
52 const json::Object &Obj, JSONFormat::EntityIdFromJSONFn IdFromJSON) {
53 const json::Array *Content =
55
56 if (!Content)
57 return makeSawButExpectedError(Obj, "an object with a key %s",
59
60 auto UnsafeBuffers = entityPointerLevelMapFromJSON(*Content, IdFromJSON);
61
62 if (!UnsafeBuffers)
63 return UnsafeBuffers.takeError();
64
65 auto Ret = std::make_unique<UnsafeBufferUsageAnalysisResult>();
66
67 Ret->UnsafeBuffers = std::move(*UnsafeBuffers);
68 return std::move(Ret);
69}
70
71JSONFormat::AnalysisResultRegistry::Add<UnsafeBufferUsageAnalysisResult>
72 RegisterUnsafeBufferUsageResultForJSON(
73 serializeUnsafeBufferUsageAnalysisResult,
74 deserializeUnsafeBufferUsageAnalysisResult);
75
76class UnsafeBufferUsageAnalysis final
77 : public SummaryAnalysis<UnsafeBufferUsageAnalysisResult,
78 UnsafeBufferUsageEntitySummary> {
79public:
80 llvm::Error add(EntityId Id,
81 const UnsafeBufferUsageEntitySummary &Summary) override {
82 auto UnsafeBuffersOfEntity = getUnsafeBuffers(Summary);
83
84 getResult().UnsafeBuffers[Id] = EntityPointerLevelSet(
85 UnsafeBuffersOfEntity.begin(), UnsafeBuffersOfEntity.end());
86 return llvm::Error::success();
87 }
88};
89
90AnalysisRegistry::Add<UnsafeBufferUsageAnalysis>
91 RegisterUnsafeBufferUsageAnalysis(
92 "Whole-program unsafe buffer usage analysis");
93
94//===----------------------------------------------------------------------===//
95// UnsafeBufferReachableAnalysis---computes reachable unsafe buffer nodes
96//===----------------------------------------------------------------------===//
97
98json::Object serializeUnsafeBufferReachableAnalysisResult(
101 json::Object Result;
102
104 entityPointerLevelSetToJSON(R.Reachables, IdToJSON);
105 return Result;
106}
107
109deserializeUnsafeBufferReachableAnalysisResult(
110 const json::Object &Obj, JSONFormat::EntityIdFromJSONFn IdFromJSON) {
111 const json::Array *Content =
113
114 if (!Content)
116 Obj, "an object with a key %s",
118
119 auto Reachables = entityPointerLevelSetFromJSON(*Content, IdFromJSON);
120
121 if (!Reachables)
122 return Reachables.takeError();
123
124 auto Ret = std::make_unique<UnsafeBufferReachableAnalysisResult>();
125
126 Ret->Reachables = std::move(*Reachables);
127 return std::move(Ret);
128}
129
130JSONFormat::AnalysisResultRegistry::Add<UnsafeBufferReachableAnalysisResult>
131 RegisterUnsafeBufferReachableResultForJSON(
132 serializeUnsafeBufferReachableAnalysisResult,
133 deserializeUnsafeBufferReachableAnalysisResult);
134
135/// \brief Computes pointers (EPLs) that satisfy a specific set of constraints.
136///
137/// The pointers must satisfy all of the following constraints:
138///
139/// 1. **C1 (Unsafe):** Any pointer in `UnsafeBufferUsageAnalysisResult`
140/// is considered unsafe.
141/// 2. **C2 (Reachable):** If a pointer is reachable from an unsafe pointer in
142/// the pointer flow graph (provided by `PointerFlowAnalysisResult`), it is
143/// also unsafe.
144/// 3. **C3 (Constrained):** Type-constrained entities are NOT unsafe.
145class UnsafeBufferReachableAnalysis
146 : public DerivedAnalysis<UnsafeBufferReachableAnalysisResult,
147 PointerFlowAnalysisResult,
148 TypeConstrainedPointersAnalysisResult,
149 UnsafeBufferUsageAnalysisResult> {
150
151 struct BoundsPropagationGraph {
152 EdgeSet PointerFlows;
153
154 /// Returns the EntityPointerLevelSet that are reachable from \p Src by
155 /// one edge in the BoundsPropagationGraph.
156 EntityPointerLevelSet getDestNodes(const EntityPointerLevel &Src) const {
157 auto I = PointerFlows.find(Src);
158 if (I == PointerFlows.end())
159 return {};
160 return I->second;
161 }
162 };
163
164 std::map<EntityId, BoundsPropagationGraph> BPG;
165
166 // Use pointers for efficiency. EPLs are in tree-based containers that only
167 // grow. So pointers to them are stable.
168 using EPLPtr = const EntityPointerLevel *;
169
170 // Find all outgoing edges from `EPL` in the `Graph`, insert their
171 // destination nodes into `Reachables`, and add newly discovered nodes to
172 // `Worklist`:
173 void updateReachablesWithOutgoings(EPLPtr EPL,
174 std::vector<EPLPtr> &WorkList) {
175 for (auto &[Id, SubGraph] : BPG) {
176 auto R = SubGraph.getDestNodes(*EPL);
177
178 for (const auto &Dst : R) {
179 auto [It, Inserted] = getResult().Reachables.insert(Dst);
180 if (Inserted)
181 WorkList.push_back(&*It);
182 }
183 }
184 }
185
186 // Expand the initial set of C1 pointers in `getResult().Reachables` by
187 // computing and appending all reachable pointers, satisfying both C1 and C2.
188 void computeReachableUnsafePointers() {
189 auto &Reachables = getResult().Reachables;
190 // Simple DFS:
191 std::vector<EPLPtr> Worklist;
192
193 for (auto &EPL : Reachables)
194 Worklist.push_back(&EPL);
195
196 while (!Worklist.empty()) {
197 EPLPtr Node = Worklist.back();
198 Worklist.pop_back();
199
200 updateReachablesWithOutgoings(Node, Worklist);
201 }
202 }
203
204public:
205 llvm::Error
206 initialize(const PointerFlowAnalysisResult &PtrFlowGraph,
207 const TypeConstrainedPointersAnalysisResult &TypeConstraints,
208 const UnsafeBufferUsageAnalysisResult &UnsafePtrs) override {
209 auto HasNoTypeConstraint =
210 [&TypeConstraints](const EntityPointerLevel &EPL) {
211 return !TypeConstraints.contains(EPL.getEntity());
212 };
213
214 // Filter out edges involving type-constrained pointers from `PtrFlowGraph`:
215 for (auto &[Id, SubGraph] : PtrFlowGraph.Edges) {
216 EdgeSet FilteredSubGraph;
217
218 for (const auto &[Src, Dsts] : SubGraph) {
219 if (TypeConstraints.contains(Src.getEntity()))
220 continue;
221
222 auto FilteredDstRange =
223 llvm::make_filter_range(Dsts, HasNoTypeConstraint);
224
225 if (!FilteredDstRange.empty())
226 FilteredSubGraph[Src].insert(FilteredDstRange.begin(),
227 FilteredDstRange.end());
228 }
229 if (!FilteredSubGraph.empty())
230 BPG.try_emplace(Id,
231 BoundsPropagationGraph{std::move(FilteredSubGraph)});
232 }
233
234 // Filter out type-constrained pointers from `UnsafePtrs`:
235 for (auto &[Contributor, EPLs] : UnsafePtrs) {
236 auto FilteredRange = llvm::make_filter_range(EPLs, HasNoTypeConstraint);
237
238 getResult().Reachables.insert(FilteredRange.begin(), FilteredRange.end());
239 }
240 return llvm::Error::success();
241 }
242
243 llvm::Expected<bool> step() override {
244 // Compute the reachable EPLs from the C1 unsafe pointers over the
245 // pointer-flow graph; both are already C3-filtered, so the result
246 // satisfies C1, C2, and C3.
247 computeReachableUnsafePointers();
248 // This is not an iterative algorithm so stop iteration by retruning false:
249 return false;
250 }
251};
252
253AnalysisRegistry::Add<UnsafeBufferReachableAnalysis>
254 RegisterUnsafeBufferReachableAnalysis(
255 "Reachable pointers from unsafe buffer usage in pointer flow graph");
256
257} // namespace
258
259namespace clang::ssaf {
260// NOLINTNEXTLINE(misc-use-internal-linkage)
262} // 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:285
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.
llvm::json::Array entityPointerLevelSetToJSON(llvm::iterator_range< EntityPointerLevelSet::const_iterator > EPLs, JSONFormat::EntityIdToJSONFn EntityId2JSON)
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
Expected< EntityPointerLevelSet > entityPointerLevelSetFromJSON(const llvm::json::Array &EPLsData, JSONFormat::EntityIdFromJSONFn EntityIdFromJSON)
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