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"
30#include "llvm/ADT/DenseMap.h"
31#include "llvm/ADT/STLExtras.h"
32#include "llvm/ADT/SmallVector.h"
33#include "llvm/ADT/iterator_range.h"
34#include "llvm/Support/Error.h"
35#include "llvm/Support/JSON.h"
36#include <memory>
37
38using namespace clang::ssaf;
39using namespace llvm;
40
41namespace {
42
43json::Object serializeUnsafeBufferUsageAnalysisResult(
46 json::Object Result;
47
49 entityPointerLevelMapToJSON(R.UnsafeBuffers, IdToJSON);
50 return Result;
51}
52
54deserializeUnsafeBufferUsageAnalysisResult(
55 const json::Object &Obj, JSONFormat::EntityIdFromJSONFn IdFromJSON) {
56 const json::Array *Content =
58
59 if (!Content)
60 return makeSawButExpectedError(Obj, "an object with a key %s",
62
63 auto UnsafeBuffers = entityPointerLevelMapFromJSON(*Content, IdFromJSON);
64
65 if (!UnsafeBuffers)
66 return UnsafeBuffers.takeError();
67
68 auto Ret = std::make_unique<UnsafeBufferUsageAnalysisResult>();
69
70 Ret->UnsafeBuffers = std::move(*UnsafeBuffers);
71 return std::move(Ret);
72}
73
74JSONFormat::AnalysisResultRegistry::Add<UnsafeBufferUsageAnalysisResult>
75 RegisterUnsafeBufferUsageResultForJSON(
76 serializeUnsafeBufferUsageAnalysisResult,
77 deserializeUnsafeBufferUsageAnalysisResult);
78
79class UnsafeBufferUsageAnalysis final
80 : public SummaryAnalysis<UnsafeBufferUsageAnalysisResult,
81 UnsafeBufferUsageEntitySummary> {
82public:
83 llvm::Error add(EntityId Id,
84 const UnsafeBufferUsageEntitySummary &Summary) override {
85 auto UnsafeBuffersOfEntity = getUnsafeBuffers(Summary);
86
87 getResult().UnsafeBuffers[Id] = EntityPointerLevelSet(
88 UnsafeBuffersOfEntity.begin(), UnsafeBuffersOfEntity.end());
89 return llvm::Error::success();
90 }
91};
92
93AnalysisRegistry::Add<UnsafeBufferUsageAnalysis>
94 RegisterUnsafeBufferUsageAnalysis(
95 "Whole-program unsafe buffer usage analysis");
96
97//===----------------------------------------------------------------------===//
98// UnsafeBufferReachableAnalysis---computes reachable unsafe buffer nodes
99//===----------------------------------------------------------------------===//
100
101json::Object serializeUnsafeBufferReachableAnalysisResult(
104 json::Object Result;
105
107 entityPointerLevelSetToJSON(R.Reachables, IdToJSON);
108 return Result;
109}
110
112deserializeUnsafeBufferReachableAnalysisResult(
113 const json::Object &Obj, JSONFormat::EntityIdFromJSONFn IdFromJSON) {
114 const json::Array *Content =
116
117 if (!Content)
119 Obj, "an object with a key %s",
121
122 auto Reachables = entityPointerLevelSetFromJSON(*Content, IdFromJSON);
123
124 if (!Reachables)
125 return Reachables.takeError();
126
127 auto Ret = std::make_unique<UnsafeBufferReachableAnalysisResult>();
128
129 Ret->Reachables = std::move(*Reachables);
130 return std::move(Ret);
131}
132
133JSONFormat::AnalysisResultRegistry::Add<UnsafeBufferReachableAnalysisResult>
134 RegisterUnsafeBufferReachableResultForJSON(
135 serializeUnsafeBufferReachableAnalysisResult,
136 deserializeUnsafeBufferReachableAnalysisResult);
137
138/// \brief Computes pointers (EPLs) that satisfy a specific set of constraints.
139///
140/// The pointers must satisfy all of the following constraints:
141///
142/// 1. **C1 (Unsafe):** Any pointer in `UnsafeBufferUsageAnalysisResult`
143/// is considered unsafe.
144/// 2. **C2 (Reachable):** If a pointer is reachable from an unsafe pointer in
145/// the pointer flow graph (provided by `PointerFlowAnalysisResult`), it is
146/// also unsafe.
147/// 3. **C3 (Constrained):** Type-constrained entities are NOT unsafe.
148/// 4. **C4 (Family):** If a parameter or return slot of a virtual method is
149/// unsafe at some pointer level, so is every slot of its override family
150/// (provided by `VirtualMethodFamilyAnalysisResult`) at that level, because
151/// a virtual call can dispatch to any of the overrides.
152class UnsafeBufferReachableAnalysis
153 : public DerivedAnalysis<
154 UnsafeBufferReachableAnalysisResult, PointerFlowAnalysisResult,
155 TypeConstrainedPointersAnalysisResult,
156 UnsafeBufferUsageAnalysisResult, VirtualMethodFamilyAnalysisResult> {
157
158 struct BoundsPropagationGraph {
159 EdgeSet PointerFlows;
160
161 /// Returns the EntityPointerLevelSet that are reachable from \p Src by
162 /// one edge in the BoundsPropagationGraph.
163 EntityPointerLevelSet getDestNodes(const EntityPointerLevel &Src) const {
164 auto I = PointerFlows.find(Src);
165 if (I == PointerFlows.end())
166 return {};
167 return I->second;
168 }
169 };
170
171 std::map<EntityId, BoundsPropagationGraph> BPG;
172
173 /// Maps each virtual method slot to the ID of its override family.
174 const llvm::DenseMap<EntityId, EntityId> *FamilyOf = nullptr;
175
176 /// The slots of each override family, excluding type-constrained ones.
177 llvm::DenseMap<EntityId, llvm::SmallVector<EntityId, 2>> FamilyMembers;
178
179 // Use pointers for efficiency. EPLs are in tree-based containers that only
180 // grow. So pointers to them are stable.
181 using EPLPtr = const EntityPointerLevel *;
182
183 // Insert `EPL` into `Reachables`, and add it to `Worklist` if it is new:
184 void insertReachable(const EntityPointerLevel &EPL,
185 std::vector<EPLPtr> &WorkList) {
186 auto [It, Inserted] = getResult().Reachables.insert(EPL);
187 if (Inserted)
188 WorkList.push_back(&*It);
189 }
190
191 // Find all outgoing edges from `EPL` in the `Graph`, insert their
192 // destination nodes into `Reachables`, and add newly discovered nodes to
193 // `Worklist`:
194 void updateReachablesWithOutgoings(EPLPtr EPL,
195 std::vector<EPLPtr> &WorkList) {
196 for (auto &[Id, SubGraph] : BPG) {
197 auto R = SubGraph.getDestNodes(*EPL);
198
199 for (const auto &Dst : R)
200 insertReachable(Dst, WorkList);
201 }
202 }
203
204 // Insert the slots of the override family of `EPL` at the pointer level of
205 // `EPL` into `Reachables`, and add newly discovered nodes to `Worklist`:
206 void updateReachablesWithFamily(EPLPtr EPL, std::vector<EPLPtr> &WorkList) {
207 auto FamilyIt = FamilyOf->find(EPL->getEntity());
208 if (FamilyIt == FamilyOf->end())
209 return;
210 auto MembersIt = FamilyMembers.find(FamilyIt->second);
211 if (MembersIt == FamilyMembers.end())
212 return;
213 for (EntityId Member : MembersIt->second)
214 insertReachable(buildEntityPointerLevel(Member, EPL->getPointerLevel()),
215 WorkList);
216 }
217
218 // Expand the initial set of C1 pointers in `getResult().Reachables` by
219 // computing and appending all reachable pointers, satisfying C1, C2 and C4.
220 void computeReachableUnsafePointers() {
221 auto &Reachables = getResult().Reachables;
222 // Simple DFS:
223 std::vector<EPLPtr> Worklist;
224
225 for (auto &EPL : Reachables)
226 Worklist.push_back(&EPL);
227
228 while (!Worklist.empty()) {
229 EPLPtr Node = Worklist.back();
230 Worklist.pop_back();
231
232 updateReachablesWithOutgoings(Node, Worklist);
233 updateReachablesWithFamily(Node, Worklist);
234 }
235 }
236
237public:
238 llvm::Error
239 initialize(const PointerFlowAnalysisResult &PtrFlowGraph,
240 const TypeConstrainedPointersAnalysisResult &TypeConstraints,
241 const UnsafeBufferUsageAnalysisResult &UnsafePtrs,
242 const VirtualMethodFamilyAnalysisResult &Families) override {
243 auto HasNoTypeConstraint =
244 [&TypeConstraints](const EntityPointerLevel &EPL) {
245 return !TypeConstraints.contains(EPL.getEntity());
246 };
247
248 // Filter out edges involving type-constrained pointers from `PtrFlowGraph`:
249 for (auto &[Id, SubGraph] : PtrFlowGraph.Edges) {
250 EdgeSet FilteredSubGraph;
251
252 for (const auto &[Src, Dsts] : SubGraph) {
253 if (TypeConstraints.contains(Src.getEntity()))
254 continue;
255
256 auto FilteredDstRange =
257 llvm::make_filter_range(Dsts, HasNoTypeConstraint);
258
259 if (!FilteredDstRange.empty())
260 FilteredSubGraph[Src].insert(FilteredDstRange.begin(),
261 FilteredDstRange.end());
262 }
263 if (!FilteredSubGraph.empty())
264 BPG.try_emplace(Id,
265 BoundsPropagationGraph{std::move(FilteredSubGraph)});
266 }
267
268 // Filter out type-constrained pointers from `UnsafePtrs`:
269 for (auto &[Contributor, EPLs] : UnsafePtrs) {
270 auto FilteredRange = llvm::make_filter_range(EPLs, HasNoTypeConstraint);
271
272 getResult().Reachables.insert(FilteredRange.begin(), FilteredRange.end());
273 }
274
275 // Filter out type-constrained slots from the override families:
276 FamilyOf = &Families.RetAndParamData;
277 for (auto [Slot, FamilyId] : Families.RetAndParamData)
278 if (!TypeConstraints.contains(Slot))
279 FamilyMembers[FamilyId].push_back(Slot);
280 return llvm::Error::success();
281 }
282
283 llvm::Expected<bool> step() override {
284 // Compute the reachable EPLs from the C1 unsafe pointers over the
285 // pointer-flow graph and the override families; all three are already
286 // C3-filtered, so the result satisfies C1, C2, C3, and C4.
287 computeReachableUnsafePointers();
288 // This is not an iterative algorithm so stop iteration by retruning false:
289 return false;
290 }
291};
292
293AnalysisRegistry::Add<UnsafeBufferReachableAnalysis>
294 RegisterUnsafeBufferReachableAnalysis(
295 "Reachable pointers from unsafe buffer usage in pointer flow graph, "
296 "family-closed across virtual method overrides");
297
298} // namespace
299
300namespace clang::ssaf {
301// NOLINTNEXTLINE(misc-use-internal-linkage)
303} // 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:284
EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned)
An EntityPointerLevel is associated with a level of the declared pointer/array type of an entity.
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::iterator_range< EntityPointerLevelSet::const_iterator > getUnsafeBuffers(const UnsafeBufferUsageEntitySummary &)
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
llvm::DenseMap< EntityId, EntityId > RetAndParamData
Maps each parameter or return slot to the ID of the family it belongs to.