Static Value-Flow Analysis
Loading...
Searching...
No Matches
SparseObjGraph.cpp
Go to the documentation of this file.
1//===- SparseObjGraph.cpp - Static packed Obj routes -----------------------===//
2
4
6#include "Graphs/SVFG.h"
8
9using namespace SVF;
10
11std::string SparseObjEdge::toString() const
12{
13 std::ostringstream out;
14 out << "edge " << getSrcID() << " -> " << getDstID();
15 return out.str();
16}
17
18std::string SparseObjNode::toString() const
19{
20 std::ostringstream out;
21 out << "node " << getId() << " base " << base << " ICFG "
22 << location->getId();
23 if (branch)
24 out << " branch " << branch->getSrcNode()->getId()
25 << " -> " << branch->getDstNode()->getId()
26 << " label " << branch->getSuccessorCondValue();
27 return out.str();
28}
29
31 PointerAnalysis& pta, const SVFIR& svfir) : icfg(graph)
32{
33 //Step 1 collect key: All base Obj ID, value: all def-use sites related to the base id
35 for (auto it = svfg.begin(); it != svfg.end(); ++it)
36 {
37 const VFGNode* vfg = it->second;
38 const ICFGNode* location = vfg->getICFGNode();
39 // Only in-function SVFG nodes participate here; global entry routes
40 // are connected below.
41 if (location && location->getFun())
42 {
43 // An indirect call edge identifies the packed Obj passed to the
44 // callee: C: f(&x) -> P: callee entry. Register both C and P as
45 // events for x so intra-function routing stops and resumes there;
46 // addEdge connects the interprocedural C -> P edge separately.
47 for (const VFGEdge* edge : vfg->getInEdges())
48 {
49 if (const auto* call = SVFUtil::dyn_cast<CallIndSVFGEdge>(edge))
50 {
51 const ICFGNode* caller = call->getSrcNode()->getICFGNode();
52 if (caller)
53 {
54 for (NodeID obj : call->getPointsTo())
55 {
56 if (!svfir.isNullOrBlkObj(obj))
57 {
58 const NodeID base = svfir.getBaseObject(obj)->getId();
59 eventsByBase[base].insert(caller);
60 eventsByBase[base].insert(location);
61 addEdge(base, caller, location, nullptr);
62 }
63 }
64 }
65 }
66 }
67
68 // MRSVFGNode already supplies points-to objects. Group each by
69 // base without modifying the SVFG or strong/weak update rules.
70 const ValVar* pointer = nullptr;
71 if (const auto* memory = SVFUtil::dyn_cast<MRSVFGNode>(vfg))
72 {
73 for (NodeID obj : memory->getPointsTo())
74 {
75 if (!svfir.isNullOrBlkObj(obj))
76 {
77 const NodeID base = svfir.getBaseObject(obj)->getId();
78 eventsByBase[base].insert(location);
79 getOrCreateBaseObjNode(base, location);
80 }
81 }
82 }
83 else if (const auto* store = SVFUtil::dyn_cast<StoreVFGNode>(vfg))
84 pointer = store->getLHSVar();
85 else if (const auto* load = SVFUtil::dyn_cast<LoadVFGNode>(vfg))
86 pointer = load->getRHSVar();
87
88 // Resolve ordinary load/store targets with points-to analysis.
89 // If p points to a[1], record a's base so a[0] and a[1] travel
90 // through this location as one packed Obj.
91 if (pointer)
92 {
93 for (NodeID obj : pta.getPts(pointer->getId()))
94 {
95 if (!svfir.isNullOrBlkObj(obj))
96 {
97 const NodeID base = svfir.getBaseObject(obj)->getId();
98 eventsByBase[base].insert(location);
99 getOrCreateBaseObjNode(base, location);
100 }
101 }
102 }
103 }
104 }
105
106 // Step 2: connect callee Obj outputs to the caller return location.
107 // For C: f(&x); R: return from the call, a RetIndSVFGEdge for x gives
108 // callee-output -> R, where AE can read the computed Obj value.
109 for (auto it = icfg.begin(); it != icfg.end(); ++it)
110 {
111 if (const auto* ret = SVFUtil::dyn_cast<RetICFGNode>(it->second))
112 {
113 for (const VFGNode* target : ret->getVFGNodes())
114 {
115 for (const VFGEdge* edge : target->getInEdges())
116 {
117 if (const auto* output = SVFUtil::dyn_cast<RetIndSVFGEdge>(edge))
118 {
119 const ICFGNode* source = output->getSrcNode()->getICFGNode();
120 if (source)
121 {
122 for (NodeID obj : output->getPointsTo())
123 {
124 if (!svfir.isNullOrBlkObj(obj))
125 {
126 const NodeID base = svfir.getBaseObject(obj)->getId();
127 returnedGroups[ret].insert(base);
128 if (source->getFun())
129 returnedGroupsByCallee[ret][source->getFun()].insert(base);
130 eventsByBase[base].insert(ret);
131 if (source->getFun())
132 eventsByBase[base].insert(source);
133 addEdge(base, source, ret, nullptr);
134 }
135 }
136 }
137 }
138 }
139 }
140 }
141 }
142
143 // Step 3: connect consecutive Obj events in ICFG order within each function.
144 // D: a[0]=1; S: a[1]=7; N: use(a[0]) becomes D -> S -> N.
145 // Branches, calls, and returns also stop a route, even if they were not
146 // among the initial SVFG events.
147 for (const auto& [base, events] : eventsByBase)
148 {
149 Set<const ICFGNode*> sources = events;
150 for (const ICFGNode* event : events)
151 sources.insert(icfg.getFunEntryICFGNode(event->getFun()));
152
153 // Connect global objects from global initialization to function entries.
154 if (svfir.getBaseObject(base)->isGlobalObj())
155 {
156 const ICFGNode* global = icfg.getGlobalICFGNode();
157 for (const ICFGEdge* edge : global->getOutEdges())
158 {
159 const ICFGNode* entry = edge->getDstNode();
160 if (SVFUtil::isa<FunEntryICFGNode>(entry) && sources.count(entry))
161 addEdge(base, global, entry, nullptr);
162 }
163 }
164
165 // Branch/call/return sites found by connectFrom become sources for
166 // the next segment. This queue is used only during graph construction.
167 std::vector<const ICFGNode*> pending(sources.begin(), sources.end());
168 for (size_t i = 0; i < pending.size(); ++i)
169 {
170 Set<const ICFGNode*> newSources;
171 connectFrom(base, pending[i], events, newSources);
172 for (const ICFGNode* event : newSources)
173 {
174 if (sources.insert(event).second)
175 pending.push_back(event);
176 }
177 }
178 }
179
180}
181
182bool SparseObjGraph::isBranch(const ICFGNode* node) const
183{
184 // Only conditional intra-function ICFG edges count as branches.
185 for (const ICFGEdge* out : node->getOutEdges())
186 {
187 const auto* intra = SVFUtil::dyn_cast<IntraCFGEdge>(out);
188 if (intra && intra->getCondition())
189 return true;
190 }
191 return false;
192}
193
195 NodeID baseObjId, const ICFGNode* location)
196{
197 // One node per (base, ICFG location) may group several SVFG nodes.
198 Node*& node = groupNodes[baseObjId][location];
199 if (!node)
200 {
201 node = new Node(getTotalNodeNum(), baseObjId, location, nullptr);
202 addGNode(node->getId(), node);
203 }
204 return node;
205}
206
208 NodeID base, const IntraCFGEdge* branch)
209{
210 // B: if (x > 0) -> N: use(x). B's true and false edges have distinct
211 // nodes; the nodes store edge identity, not AE's refined interval.
212 Node*& node = branchNodes[base][branch];
213 if (!node)
214 {
215 node = new Node(getTotalNodeNum(), base, branch->getSrcNode(), branch);
216 addGNode(node->getId(), node);
217 }
218 return node;
219}
220
222{
223 Edge candidate(source, target);
224 if (source->hasOutgoingEdge(&candidate))
225 return;
226
227 Edge* edge = new Edge(source, target);
228 const bool addedOut = source->addOutgoingEdge(edge);
229 const bool addedIn = target->addIncomingEdge(edge);
230 assert(addedOut && addedIn);
231 (void)addedOut;
232 (void)addedIn;
233 incEdgeNum();
234 if (!target->branch)
235 inputs[target->location].push_back(edge);
236}
237
238void SparseObjGraph::addEdge(NodeID base, const ICFGNode* source,
239 const ICFGNode* target,
240 const IntraCFGEdge* branch)
241{
242 // B: if (z > 0) { use(x); use(z); }, with ordinary loads and no aliasing.
243 // For base=z, connectFrom passes source=B, target=N (the z use), and
244 // B's true edge. The intervening use(x) adds no z edge. The same route
245 // applies when N immediately follows B. inputs[N] records the branch
246 // node -> N; AE checks feasibility and refines z when reading that edge.
247 Node* from = getOrCreateBaseObjNode(base, source);
248 Node* to = getOrCreateBaseObjNode(base, target);
249 Node* input = branch ? getBranchNode(base, branch) : from;
250 if (branch)
251 addRouteEdge(from, input);
252 addRouteEdge(input, to);
253}
254
256 NodeID base, const ICFGNode* source,
257 const Set<const ICFGNode*>& events,
258 Set<const ICFGNode*>& newSources)
259{
260 // From one Obj event, find the next event or control boundary in this
261 // function. Feasibility is not checked here, so AE states share the route.
262 const FunObjVar* fun = source->getFun();
263 if (!fun)
264 return;
265
266 std::vector<std::pair<const ICFGNode*, const IntraCFGEdge*>> first;
267 // inter-procedure
268 if (const auto* call = SVFUtil::dyn_cast<CallICFGNode>(source))
269 {
270 const RetICFGNode* ret = call->getRetICFGNode();
271 bool needsCallerToRetEdge = !returnedGroups[ret].count(base);
272 for (const ICFGEdge* out : call->getOutEdges())
273 {
274 const auto* callEdge = SVFUtil::dyn_cast<CallCFGEdge>(out);
275 if (callEdge && !returnedGroupsByCallee[ret][out->getDstNode()->getFun()].count(base))
276 needsCallerToRetEdge = true;
277 }
278 // C may call f_write(&x) or f_noop(&x). If a callee has no return
279 // edge for x, retain C -> R so that candidate carries the pre-call
280 // value; a candidate with a return edge uses callee-output -> R.
281 if (needsCallerToRetEdge)
282 first.emplace_back(call->getRetICFGNode(), nullptr);
283 }
284 else
285 // intra-procedure
286 {
287 for (const ICFGEdge* out : source->getOutEdges())
288 {
289 if (const auto* intra = SVFUtil::dyn_cast<IntraCFGEdge>(out))
290 first.emplace_back(intra->getDstNode(),
291 intra->getCondition() ? intra : nullptr);
292 }
293 }
294
295 for (const auto& [start, branch] : first)
296 {
297 // Cross ordinary ICFG nodes between events; stop at a branch, call,
298 // return, or the next Obj event, which starts another route segment.
299 // For base=z: B: if (z > 0) { Ux: use(x); Uz: use(z); }.
300 // With ordinary non-aliasing loads, Ux is not a z event; traverse
301 // past Ux and call addEdge(z, B, Uz, true edge) at Uz.
302 Set<const ICFGNode*> visited;
303 std::deque<const ICFGNode*> pending{start};
304 while (!pending.empty())
305 {
306 const ICFGNode* current = pending.front();
307 pending.pop_front();
308 if (current && current->getFun() == fun &&
309 visited.insert(current).second)
310 {
311 const bool objSite = events.count(current);
312 const bool controlBoundary = isBranch(current) ||
315 if (objSite || controlBoundary)
316 {
317 addEdge(base, source, current, branch);
318 if (!objSite)
319 newSources.insert(current);
320 }
321 else
322 {
323 for (const ICFGEdge* out : current->getOutEdges())
324 {
325 if (const auto* intra = SVFUtil::dyn_cast<IntraCFGEdge>(out))
326 pending.push_back(intra->getDstNode());
327 }
328 }
329 }
330 }
331 }
332}
333
334const std::vector<const SparseObjGraph::Edge*>&
336{
337 static const std::vector<const Edge*> empty;
338 const auto it = inputs.find(node);
339 return it == inputs.end() ? empty : it->second;
340}
341
342std::string SparseObjGraph::toString() const
343{
344 std::ostringstream out;
345 for (const auto& entry : *this)
346 out << entry.second->toString() << '\n';
347 for (const auto& entry : *this)
348 for (const Edge* edge : entry.second->getOutEdges())
349 out << edge->toString() << '\n';
350 return out.str();
351}
352
353namespace SVF
354{
355
356template<>
358{
359 DOTGraphTraits(bool simple = false) : DefaultDOTGraphTraits(simple) {}
360
361 static std::string getGraphName(SparseObjGraph*)
362 {
363 return "Sparse object routes";
364 }
365
366 static std::string getNodeLabel(SparseObjNode* node, SparseObjGraph*)
367 {
368 return node->toString();
369 }
370
372 {
373 return node->branch ? "shape=diamond,style=filled,fillcolor=\"#ffedd5\""
374 : "shape=box,style=filled,fillcolor=\"#dbeafe\"";
375 }
376};
377
378} // namespace SVF
379
380void SparseObjGraph::dump(const std::string& file)
381{
383}
NodeType * getSrcNode() const
iterator begin()
Iterators.
bool hasOutgoingEdge() const
const GEdgeSetTy & getOutEdges() const
bool addIncomingEdge(EdgeType *inEdge)
Add incoming and outgoing edges.
bool addOutgoingEdge(EdgeType *outEdge)
static void WriteGraphToFile(SVF::OutStream &O, const std::string &GraphName, const GraphType &GT, bool simple=false)
virtual const FunObjVar * getFun() const
Return the function of this ICFGNode.
Definition ICFGNode.h:75
virtual const std::string toString() const
Definition ICFG.cpp:50
NodeID getId() const
Get ID.
Definition SVFValue.h:158
std::string toString() const
const std::vector< const Edge * > & getInputs(const ICFGNode *node) const
Return incoming edges at this ICFG location for client-side value flow.
bool isBranch(const ICFGNode *node) const
Map< const ICFGNode *, std::vector< const Edge * > > inputs
Node * getOrCreateBaseObjNode(NodeID baseObjId, const ICFGNode *location)
Map< const ICFGNode *, Set< NodeID > > returnedGroups
Map< NodeID, Map< const ICFGNode *, Node * > > groupNodes
Normal nodes are unique by (base, ICFG location); branch nodes by edge.
SparseObjGraph(const SVFG &svfg, ICFG &icfg, PointerAnalysis &pta, const SVFIR &svfir)
Construction uses static graphs and points-to results, not AE state.
void connectFrom(NodeID base, const ICFGNode *source, const Set< const ICFGNode * > &events, Set< const ICFGNode * > &newSources)
Map< const ICFGNode *, Map< const FunObjVar *, Set< NodeID > > > returnedGroupsByCallee
std::string toString() const
Describe the static nodes and edges as text, without AE values.
Map< NodeID, Map< const IntraCFGEdge *, Node * > > branchNodes
void addEdge(NodeID base, const ICFGNode *source, const ICFGNode *target, const IntraCFGEdge *branch)
Node * getBranchNode(NodeID base, const IntraCFGEdge *branch)
void addRouteEdge(Node *source, Node *target)
void dump(const std::string &file)
Write a DOT graph to <file>.dot through GraphPrinter.
const IntraCFGEdge * branch
const ICFGNode * location
std::string toString() const
LLVM_NODISCARD bool isa(const Y &Val)
Definition Casting.h:241
std::ostream & outs()
Overwrite llvm::outs().
Definition SVFUtil.h:58
LLVM_NODISCARD std::enable_if_t<!is_simple_type< Y >::value, typename cast_retty< X, const Y >::ret_type > dyn_cast(const Y &Val)
Definition Casting.h:405
for isBitcode
Definition BasicTypes.h:70
u32_t NodeID
Definition GeneralType.h:76
std::unordered_set< Key, Hash, KeyEqual, Allocator > Set
Definition GeneralType.h:51
std::unordered_map< Key, Value, Hash, KeyEqual, Allocator > Map
Definition GeneralType.h:56
static std::string getGraphName(SparseObjGraph *)
static std::string getNodeLabel(SparseObjNode *node, SparseObjGraph *)
static std::string getNodeAttributes(SparseObjNode *node, SparseObjGraph *)
DefaultDOTGraphTraits(bool simple=false)