Static Value-Flow Analysis
Loading...
Searching...
No Matches
MTASlicer.cpp
Go to the documentation of this file.
1//===- MTASlicer.cpp -- Multi-stage on-demand program slicers -------------===//
2//
3// SVF: Static Value-Flow Analysis
4//
5// Copyright (C) <2013-> <Yulei Sui>
6//
7
8// This program is free software: you can redistribute it and/or modify
9// it under the terms of the GNU Affero General Public License as published by
10// the Free Software Foundation, either version 3 of the License, or
11// (at your option) any later version.
12
13// This program is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16// GNU Affero General Public License for more details.
17
18// You should have received a copy of the GNU Affero General Public License
19// along with this program. If not, see <http://www.gnu.org/licenses/>.
20//
21//===----------------------------------------------------------------------===//
22
23/*
24 * MTASlicer.cpp
25 *
26 * Author: Jiawei Yang
27 *
28 * MTASlicerBase + MultiStageSlicer + SingleSlicer -- the program slicers of
29 * "Multi-Stage On-Demand Program Slicing for Modular Analysis of Multi-Threaded
30 * Programs" (ISSTA 2026).
31 */
32
33#include "MTA/MTASlicer.h"
34#include "MTA/TCT.h"
35#include "SVFIR/SVFIR.h"
36#include "Util/SVFUtil.h"
37#include "Util/CxtStmt.h"
38#include "Util/ThreadAPI.h"
39#include <deque>
40#include <algorithm>
41#include <cassert>
42#include "Graphs/ICFGEdge.h"
43#include "Graphs/ICFGNode.h"
44#include "Graphs/CallGraph.h"
45#include "SVFIR/SVFStatements.h"
46#include "SVFIR/SVFVariables.h"
47#include <fstream>
48#include <sstream>
49#include <queue>
50#include <unordered_map>
51#include <iostream>
52#include <cctype>
53#include "Graphs/SVFG.h"
54#include "Graphs/VFGNode.h"
55#include "Graphs/VFGEdge.h"
56
57using namespace SVF;
58
59namespace SVF
60{
61
62//===----------------------------------------------------------------------===//
63// SlicedTCT - TCT rebuilt over the sliced ThreadCallGraph view.
64//===----------------------------------------------------------------------===//
65
67 : TCT(p),
68 tcgView(slicedView != nullptr && slicedView->getThreadCallGraph() != nullptr ? slicedView->getThreadCallGraph() : nullptr),
69 maxContextLen(maxContextLen)
70{
71 // Base class TCT(p) constructor called build() before tcgView was initialized.
72 // Now that tcgView is initialized, rebuild using sliced view.
73 if (tcgView != nullptr)
74 {
75 build();
76 }
77}
78
80{
81 if (tcgView == nullptr)
82 {
83 TCT::build();
84 return;
85 }
86
87 // The base TCT(p) constructor already built the whole-program TCT. Reset it to
88 // a clean graph so the slice rebuilds from scratch (addGNode reissues ids from
89 // 0; stale nodes left behind would later fail the MHP getCxtOfCxtThread lookup).
90 reset();
91
92 // Call base class build() which will call our overridden methods
93 // (markRelProcs, collectLoopInfoForJoin, collectEntryFunInCallGraph)
94 // that filter using sliced view, then build the TCT tree
95 TCT::build();
96}
97
99{
100 if (tcgView == nullptr)
101 {
103 return;
104 }
105
106 // Get kept fork sites from sliced view
107 std::vector<const ICFGNode*> keptForkSites;
109
110 for (const ICFGNode* forkSite : keptForkSites)
111 {
112 // Get function from fork site
113 const FunObjVar* svfun = forkSite->getFun();
115
116 // Get fork edges from sliced view (use tcgView to get out edges)
117 const CallICFGNode* callNode = SVFUtil::cast<CallICFGNode>(forkSite);
118 const FunObjVar* callerFun = callNode->getFun();
120
122 continue;
123
124 // Use sliced view to get out edges (which filters by kept edges)
125 std::vector<const CallGraphEdge*> outEdges;
127
128 for (const CallGraphEdge* edge : outEdges)
129 {
130 // Check if this is a fork edge
131 if (edge->getEdgeKind() == CallGraphEdge::TDForkEdge)
132 {
133 const CallGraphNode* forkeeNode = edge->getDstNode();
135 {
136 // Check if this edge corresponds to the fork site
137 const CallGraphEdge::CallInstSet& directCalls = edge->getDirectCalls();
138 const CallGraphEdge::CallInstSet& indirectCalls = edge->getIndirectCalls();
139
140 if (directCalls.count(callNode) > 0 || indirectCalls.count(callNode) > 0)
141 {
144 }
145 }
146 }
147 }
148 }
149
150 // Get kept join sites from sliced view
151 std::vector<const ICFGNode*> keptJoinSites;
153
154 for (const ICFGNode* joinSite : keptJoinSites)
155 {
156 const FunObjVar* svfun = joinSite->getFun();
158 }
159
160 if(getMakredProcs().empty())
161 SVFUtil::writeWrnMsg("We didn't recognize any fork site, this is single thread program?");
162}
163
165{
166 // Call base class implementation
168}
169
171{
172 if (tcgView == nullptr)
173 {
175 return;
176 }
177
178 // Get kept join sites from sliced view
179 std::vector<const ICFGNode*> keptJoinSites;
181
182 for(const ICFGNode* join : keptJoinSites)
183 {
184 const FunObjVar* svffun = join->getFun();
185 const SVFBasicBlock* svfbb = join->getBB();
186
187 if(svffun->hasLoopInfo(svfbb))
188 {
189 const LoopBBs& lp = svffun->getLoopInfo(svfbb);
190 if(!lp.empty() && isJoinMustExecutedInLoop(lp,join))
191 {
193 }
194 }
195
197 {
198 inRecurJoinSites.insert(join);
199 }
200 }
201}
202
204{
205 if (tcgView == nullptr)
206 {
208 return;
209 }
210
211 // Use sliced CallGraph view to collect entry functions
212 // Only collect functions that are kept in sliced view
214
215 for (const CallGraphNode* node : keptNodes)
216 {
217 const FunObjVar* fun = node->getFunction();
218 if (SVFUtil::isExtCall(fun))
219 continue;
220
221 // Check if this node has no incoming edges in the sliced view
222 std::vector<const CallGraphEdge*> inEdges;
224 if (inEdges.empty())
225 {
226 entryFuncSet.insert(fun);
227 }
228 }
229
230 assert(!getEntryProcs().empty() && "Can't find any function in module!");
231}
232
234{
235 // Check if the call site and callee are kept in sliced view
236 if (!isKeptEdge(cgEdge))
237 return;
238
239 const CallGraphNode* dstNode = cgEdge->getDstNode();
240 if (!isKeptNode(dstNode))
241 return;
242
243 // Call base class implementation
245}
246
248{
249 const FunObjVar* caller = call->getFun();
250 CallSiteID csId = tcg->getCallSiteID(call, callee);
251
253 {
254 cxt.push_back(csId);
255 // Use custom maxContextLen if set, otherwise use Options::MaxContextLen()
257 if (cxt.size() > maxLen)
258 cxt.erase(cxt.begin());
259 if (cxt.size() > MaxCxtSize)
260 MaxCxtSize = cxt.size();
261 DBOUT(DMTA, dumpCxt(cxt));
262 }
263}
264
266{
267 if (tcgView == nullptr)
268 return true;
269 return tcgView->isKeptNode(node);
270}
271
273{
274 if (tcgView == nullptr)
275 return true;
277 return keptEdges.find(const_cast<CallGraphEdge*>(edge)) != keptEdges.end();
278}
279
280void SlicedTCT::getKeptForkSites(std::vector<const ICFGNode*>& out) const
281{
282 out.clear();
283 if (tcgView == nullptr)
284 {
285 // Fall back to original
286 for (ThreadCallGraph::CallSiteSet::const_iterator it = tcg->forksitesBegin(), eit = tcg->forksitesEnd(); it != eit; ++it)
287 {
288 out.push_back(*it);
289 }
290 return;
291 }
292
293 // Get all fork sites from original ThreadCallGraph, but filter by:
294 // 1. The function containing the fork site is kept
295 // 2. There exists a kept fork edge from this fork site
296 for (ThreadCallGraph::CallSiteSet::const_iterator it = tcg->forksitesBegin(), eit = tcg->forksitesEnd(); it != eit; ++it)
297 {
298 const ICFGNode* forkSite = *it;
299 const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(forkSite);
300 if (callNode == nullptr)
301 continue;
302
303 // Check if the function containing the fork site is kept
304 const FunObjVar* fun = forkSite->getFun();
305 CallGraphNode* cgNode = tcg->getCallGraphNode(fun);
306 if (!isKeptNode(cgNode))
307 continue;
308
309 // Check if there's a kept fork edge from this fork site
310 // Use sliced view to get out edges
311 std::vector<const CallGraphEdge*> outEdges;
313
314 bool hasKeptForkEdge = false;
315 for (const CallGraphEdge* edge : outEdges)
316 {
317 if (edge->getEdgeKind() == CallGraphEdge::TDForkEdge)
318 {
319 // Check if this edge corresponds to the fork site
320 const CallGraphEdge::CallInstSet& directCalls = edge->getDirectCalls();
321 const CallGraphEdge::CallInstSet& indirectCalls = edge->getIndirectCalls();
322
323 if (directCalls.count(callNode) > 0 || indirectCalls.count(callNode) > 0)
324 {
325 // Check if the destination is kept
326 const CallGraphNode* dstNode = edge->getDstNode();
327 if (isKeptNode(dstNode))
328 {
329 hasKeptForkEdge = true;
330 break;
331 }
332 }
333 }
334 }
335
336 if (hasKeptForkEdge)
337 {
338 out.push_back(forkSite);
339 }
340 }
341}
342
343void SlicedTCT::getKeptJoinSites(std::vector<const ICFGNode*>& out) const
344{
345 out.clear();
346 if (tcgView == nullptr)
347 {
348 // Fall back to original
349 for (ThreadCallGraph::CallSiteSet::const_iterator it = tcg->joinsitesBegin(), eit = tcg->joinsitesEnd(); it != eit; ++it)
350 {
351 out.push_back(*it);
352 }
353 return;
354 }
355
356 // Get all join sites from original ThreadCallGraph, but filter by:
357 // 1. The function containing the join site is kept
358 // 2. There exists a kept join edge to this join site
359 for (ThreadCallGraph::CallSiteSet::const_iterator it = tcg->joinsitesBegin(), eit = tcg->joinsitesEnd(); it != eit; ++it)
360 {
361 const ICFGNode* joinSite = *it;
362 const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(joinSite);
363 if (callNode == nullptr)
364 continue;
365
366 // Check if the function containing the join site is kept
367 const FunObjVar* fun = joinSite->getFun();
368 CallGraphNode* cgNode = tcg->getCallGraphNode(fun);
369 if (!isKeptNode(cgNode))
370 continue;
371
372 // Check if there's a kept join edge to this join site
373 // Use sliced view to get in edges
374 std::vector<const CallGraphEdge*> inEdges;
375 tcgView->getInEdgesOf(cgNode, inEdges);
376
377 bool hasKeptJoinEdge = false;
378 for (const CallGraphEdge* edge : inEdges)
379 {
380 if (edge->getEdgeKind() == CallGraphEdge::TDJoinEdge)
381 {
382 // Check if this edge corresponds to the join site
383 const CallGraphEdge::CallInstSet& directCalls = edge->getDirectCalls();
384 const CallGraphEdge::CallInstSet& indirectCalls = edge->getIndirectCalls();
385
386 if (directCalls.count(callNode) > 0 || indirectCalls.count(callNode) > 0)
387 {
388 // Check if the source is kept
389 const CallGraphNode* srcNode = edge->getSrcNode();
390 if (isKeptNode(srcNode))
391 {
392 hasKeptJoinEdge = true;
393 break;
394 }
395 }
396 }
397 }
398
399 if (hasKeptJoinEdge)
400 {
401 out.push_back(joinSite);
402 }
403 }
404}
405
406//===----------------------------------------------------------------------===//
407// MTASlicerBase
408//===----------------------------------------------------------------------===//
409
411 LockAnalysis* lockAnalysis, SVFG* vfg)
412 : svfIr(svfIr), pta(pta), mhp(mhp), lockAnalysis(lockAnalysis), vfg(vfg)
413{
415}
416
418{
419 // No dynamic memory to clean up - all pointers are managed externally
420}
421
422// Helper: Get lock set for an ICFG node
424{
426
427 // Get intra-procedural locks
428 if (lockAnalysis->isInsideIntraLock(node) &&
430 {
432 for (const ICFGNode* lockSite : intraLocks)
433 {
434 allLockSites.insert(lockSite);
435 }
436 }
437
438 // Get context-sensitive locks
440 {
442 for (const CxtStmt& cxtStmt : cxtStmts)
443 {
445 {
449 {
450 allLockSites.insert(cxtLock.getStmt());
451 }
452 }
453 }
454 }
455
456 return allLockSites;
457}
458
459// Helper: Get TCTNode set from ICFGNode
461{
463
464 if (mhp->hasThreadStmtSet(node))
465 {
466 for (const CxtThreadStmt& cts : mhp->getThreadStmtSet(node))
467 {
468 if (mhp->getTCT()->hasGNode(cts.getTid()))
469 {
470 tctNodeSet.insert(mhp->getTCT()->getTCTNode(cts.getTid()));
471 }
472 }
473 }
474
475 return tctNodeSet;
476}
477
478// Helper: Get dependent thread create statements
480{
482
483 const ICFGNode* icfgNode = stmt->getICFGNode();
485
486 for (const TCTNode* tctNode : tctNodeSet)
487 {
488 const CxtThread& cxtThread = tctNode->getCxtThread();
489 const ICFGNode* forkSite = cxtThread.getThread();
490 if (forkSite != nullptr)
491 {
493 if (!svfStmts.empty())
494 {
495 forkSiteStmts.insert(svfStmts.front());
496 }
497 }
498 }
499
500 return forkSiteStmts;
501}
502
503// Data-dependence slice over the thread-aware SVFG (VFG_pre), at SVFG-node
504// granularity: the value-flow nodes reachable backward from the seeds. The
505// value-flow edges already capture direct (top-level), indirect (address-taken
506// / MemSSA), and thread-aware (interference) data dependence.
509{
510
511 assert(vfg != nullptr && "data-dependence slice requires the thread-aware VFG_pre");
512
514 std::deque<const SVFGNode*> worklist;
515 auto seed = [&](const SVFGNode* n)
516 {
517 if (n != nullptr && visited.insert(n).second)
518 worklist.push_back(n);
519 };
520
521 // Seed from the value-flow nodes of the given (e.g. race target) statements.
522 // VFG_pre is a pointer-only SVFG, so a load/store of a NON-pointer value (the
523 // usual case -- a race on an int/float field) has no statement node. For those
524 // we must still preserve the points-to of the dereferenced address pointer, or
525 // the sliced flow-sensitive solve sees an empty slice for it, computes empty
526 // points-to, and drops the race (a soundness bug). So additionally seed from the
527 // definition of each load/store's address pointer (always a pointer, hence
528 // always in the pointer-only SVFG); its backward closure keeps the pointer's
529 // def chain regardless of the value type.
530 for (const SVFStmt* stmt : seeds)
531 {
532 if (vfg->hasStmtVFGNode(stmt))
533 seed(vfg->getStmtVFGNode(stmt));
534
535 NodeID addrPtr = 0;
536 if (const LoadStmt* ld = SVFUtil::dyn_cast<LoadStmt>(stmt))
537 addrPtr = ld->getRHSVarID();
538 else if (const StoreStmt* st = SVFUtil::dyn_cast<StoreStmt>(stmt))
539 addrPtr = st->getLHSVarID();
540 if (addrPtr != 0)
541 {
542 // getDefSVFGNode takes a ValVar (the address pointer is a top-level
543 // value variable).
544 const ValVar* ptrNode = SVFUtil::dyn_cast<ValVar>(svfIr->getGNode(addrPtr));
545 if (ptrNode != nullptr && vfg->hasDefSVFGNode(ptrNode))
547 }
548 }
549
550 // Backward over every value-flow edge.
551 while (!worklist.empty())
552 {
553 const SVFGNode* n = worklist.front();
554 worklist.pop_front();
555 for (const VFGEdge* e : n->getInEdges())
556 seed(e->getSrcNode());
557 }
558
559 return visited;
560}
561
562// Project retained VFG nodes (plus the seeds) onto their ICFG nodes.
565{
567 for (const SVFGNode* n : nodes)
568 if (const StmtVFGNode* s = SVFUtil::dyn_cast<StmtVFGNode>(n))
569 if (s->getICFGNode() != nullptr)
570 result.insert(s->getICFGNode());
571 for (const SVFStmt* stmt : seeds)
572 result.insert(stmt->getICFGNode());
573 return result;
574}
575
581
582// Helper: Collect pthread-related statements (create and join)
585{
587
590
591 // Map pthread_create nodes to their corresponding pthread_join nodes
593
594 // First pass: collect all pthread_create nodes
595 for (const SVFStmt* stmt : vulnerableStmts)
596 {
598
599 for (const SVFStmt* forkSiteStmt : forkSiteStmts)
600 {
601 const ICFGNode* forkSiteNode = forkSiteStmt->getICFGNode();
602 const CallICFGNode* forkCallNode = SVFUtil::dyn_cast<CallICFGNode>(forkSiteNode);
603 if (forkCallNode != nullptr && threadAPI->isTDFork(forkCallNode))
604 {
607 }
608 }
609 }
610
611 // Second pass: find corresponding pthread_join nodes
612 ICFG* icfg = svfIr->getICFG();
613 for (ICFG::iterator it = icfg->begin(), eit = icfg->end(); it != eit; ++it)
614 {
615 const ICFGNode* node = it->second;
616 const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node);
617 if (callNode != nullptr && threadAPI->isTDJoin(callNode))
618 {
619 const SVFVar* joinThread = threadAPI->getJoinedThread(callNode);
620 if (joinThread != nullptr)
621 {
622 for (auto& createStmt : pthreadCreateStmts)
623 {
624 const ICFGNode* createNode = createStmt->getICFGNode();
625 const CallICFGNode* createCallNode = SVFUtil::dyn_cast<CallICFGNode>(createNode);
626 if (createCallNode != nullptr)
627 {
628 const SVFVar* forkedThread = threadAPI->getForkedThread(createCallNode);
629 if (forkedThread != nullptr && threadAPI->isAliasedForkJoin(pta, forkedThread, joinThread))
630 {
632 }
633 }
634 }
635 }
636 }
637 }
638
639 return pthreadCallNodes;
640}
641
642// Helper: Collect mutex-related statements (lock and unlock)
645{
647
650
651 // Map mutex_lock nodes to their corresponding mutex_unlock nodes
653
654 // First pass: collect all mutex_lock nodes from lock sets
655 for (const SVFStmt* stmt : vulnerableStmts)
656 {
657 OrderedSet<const ICFGNode*> lockSet = getLockSet(stmt->getICFGNode());
658 for (const ICFGNode* lockNode : lockSet)
659 {
660 const CallICFGNode* lockCallNode = SVFUtil::dyn_cast<CallICFGNode>(lockNode);
661 if (lockCallNode != nullptr && threadAPI->isTDAcquire(lockCallNode))
662 {
665 }
666 }
667 }
668
669 // Second pass: find corresponding mutex_unlock nodes
670 ICFG* icfg = svfIr->getICFG();
671 for (ICFG::iterator it = icfg->begin(), eit = icfg->end(); it != eit; ++it)
672 {
673 const ICFGNode* node = it->second;
674 const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node);
675 if (callNode != nullptr && threadAPI->isTDRelease(callNode))
676 {
677 const SVFVar* unlockVar = threadAPI->getLockVal(callNode);
678 if (unlockVar != nullptr)
679 {
681 {
682 if (lockCallNode != nullptr)
683 {
684 const SVFVar* lockVar = threadAPI->getLockVal(lockCallNode);
685 if (lockVar != nullptr && pta->alias(unlockVar->getId(), lockVar->getId()))
686 {
687 mutexCallNodes.insert(callNode);
688 }
689 }
690 }
691 }
692 }
693 }
694
695 return mutexCallNodes;
696}
697
698// Helper: Collect common pthread and mutex statements (shared by PTA and MTA slicing)
699std::pair<OrderedSet<const CallICFGNode*>, OrderedSet<const CallICFGNode*>>
701{
702 // Step 1: Collect pthread-related statements, i.e., pthread_create and pthread_join
704
705 // Step 2: Collect mutex-related statements
707
708 return std::make_pair(pthreadCallNodes, mutexCallNodes);
709}
710
711// Keep the control-flow marker nodes the (sliced) lock analysis depends on:
712// every lock/unlock-bearing function's entry and exit nodes.
713//
714// LockAnalysis classifies an intra lock as *partial* (conditional) by reaching
715// the function entry node from the unlock along a lock-free backward path
716// (intraBackwardTraverse: `entryInst == I` -> return false), and bails the
717// forward span at the function exit node (`exitInst == I`). These checks are
718// node-identity tests against the entry/exit markers. The data-dependence slice
719// does not otherwise retain those markers, so on the sliced view the backward
720// walk can never match the entry node and the lock is mis-classified as total --
721// which makes isProtectedByCommonCILock report a spurious common lock and drops
722// a real race (a query-preservation violation). Bridging preserves reachability
723// *to* the kept markers, so once they are retained the sliced lock analysis
724// reproduces the whole-program classification. Markers used: entry block
725// front (cxt-lock start) and back (intra backward marker), and exit block back
726// (intra forward marker).
730{
732 {
733 const FunObjVar* fun = mutexCallNode->getFun();
734 if (fun == nullptr)
735 continue;
736 if (const SVFBasicBlock* entry = fun->getEntryBlock())
737 {
738 sliceResult.insert(entry->front());
739 sliceResult.insert(entry->back());
740 }
741 if (const SVFBasicBlock* exit = fun->getExitBB())
742 sliceResult.insert(exit->back());
743 }
744}
745
746// Build backward ICFG node set from vulnerable nodes
749{
751 std::deque<const ICFGNode*> worklist;
752
753 // Initialize with vulnerable nodes
754 for (const ICFGNode* node : vulnerableNodes)
755 {
756 backwardICFGNodeSet.insert(node);
757 worklist.push_back(node);
758 }
759
760 // Traverse backward along ICFG edges
761 while (!worklist.empty())
762 {
763 const ICFGNode* currICFGNode = worklist.front();
764 worklist.pop_front();
765
766 for (const ICFGEdge* inEdge : currICFGNode->getInEdges())
767 {
768 const ICFGNode* srcNode = inEdge->getSrcNode();
770 {
772 worklist.push_back(srcNode);
773 }
774 }
775 }
776
777 return backwardICFGNodeSet;
778}
779
780// Perform dual slicing (temporal slicing): filter statements based on control flow and parallel execution
783{
785
786 // Build backward ICFG node set
788
789 // Perform control slicing
790 for (const ICFGNode* stmtICFGNode : slicedNodes)
791 {
792 // Check if the ICFG node is in backward_icfg_node_set
794 {
796 }
797 else
798 {
799 // Check if it may happen in parallel with any vulnerable node
800 for (const ICFGNode* bugICFGNode : slicedNodes)
801 {
803 {
805 break;
806 }
807 }
808 }
809 }
810
811 return dualSlicedNodes;
812}
813
814// Call-dependence expansion (used by MultiStageSlicer).
817{
818
819 // Determine keptFunctions from the given nodes
821 for (const ICFGNode* node : nodes)
822 {
823 if (node != nullptr && node->getFun() != nullptr)
824 {
825 keptFunctions.insert(node->getFun());
826 }
827 }
828
829 // Build ancestor closure (upward traversal in call graph)
830 std::queue<const FunObjVar*> worklistFuncs;
831 for (const FunObjVar* fun : keptFunctions)
832 {
833 worklistFuncs.push(fun);
834 }
835
837 for (auto it = callGraph->begin(), eit = callGraph->end(); it != eit; ++it)
838 {
839 const CallGraphNode* node = it->second;
840 if (node && node->getFunction())
841 {
842 fun2Node[node->getFunction()] = node;
843 }
844 }
845
847 while (!worklistFuncs.empty())
848 {
849 const FunObjVar* target = worklistFuncs.front();
850 worklistFuncs.pop();
851 auto nodeIt = fun2Node.find(target);
852 if (nodeIt == fun2Node.end()) continue;
853
854 const CallGraphNode* node = nodeIt->second;
855 for (const CallGraphEdge* inEdge : node->getInEdges())
856 {
857 if (inEdge == nullptr) continue;
858 const CallGraphNode* callerNode = inEdge->getSrcNode();
859 if (callerNode && callerNode->getFunction())
860 {
862 if (visitedFuncs.find(callerFun) == visitedFuncs.end())
863 {
864 keptFunctions.insert(callerFun);
865 visitedFuncs.insert(callerFun);
867 }
868 }
869 }
870 }
871
872 // For each keptFunction, add call/ret nodes and entry/exit nodes
873 ICFG* icfg = svfIr->getICFG();
875 for (const FunObjVar* fun : keptFunctions)
876 {
877 if (!fun) continue;
878
879 // Add function entry/exit nodes
880 if (fun->hasBasicBlock())
881 {
882 if (FunEntryICFGNode* entry = icfg->getFunEntryICFGNode(fun))
883 {
884 expandedNodes.insert(entry);
885 }
886 if (FunExitICFGNode* exit = icfg->getFunExitICFGNode(fun))
887 {
888 expandedNodes.insert(exit);
889 }
890 }
891
892 // Find all call/ret nodes that call this function
893 auto funNodeIt = fun2Node.find(fun);
894 if (funNodeIt != fun2Node.end())
895 {
896 const CallGraphNode* calleeNode = funNodeIt->second;
897
898 // Traverse all edges that call this function
899 for (const CallGraphEdge* inEdge : calleeNode->getInEdges())
900 {
901 if (inEdge == nullptr) continue;
902
903 const CallGraphEdge::CallInstSet &directCalls = inEdge->getDirectCalls();
904 const CallGraphEdge::CallInstSet &indirectCalls = inEdge->getIndirectCalls();
905
906 for (const CallICFGNode* callNode : directCalls)
907 {
908 if (callNode != nullptr)
909 {
910 expandedNodes.insert(callNode);
911 const RetICFGNode* retNode = callNode->getRetICFGNode();
912 if (retNode != nullptr)
913 {
914 expandedNodes.insert(retNode);
915 }
916 }
917 }
918
919 for (const CallICFGNode* callNode : indirectCalls)
920 {
921 if (callNode != nullptr)
922 {
923 expandedNodes.insert(callNode);
924 const RetICFGNode* retNode = callNode->getRetICFGNode();
925 if (retNode != nullptr)
926 {
927 expandedNodes.insert(retNode);
928 }
929 }
930 }
931 }
932 }
933 }
934
935 return expandedNodes;
936}
937
938//===----------------------------------------------------------------------===//
939// MultiStageSlicer
940//===----------------------------------------------------------------------===//
941
943 LockAnalysis* lockAnalysis, SVFG* vfg)
944 : MTASlicerBase(svfIr, pta, mhp, lockAnalysis, vfg)
945{
946}
947
948// Perform slicing for MTA (includes function expansion for IRView)
952{
953
954 // Step 1: Collect common pthread and mutex statements
958
959 // Step 2: Form initial slice result (before function expansion). The ILA
960 // slicing sources are the [INIT] race statements plus the [THREAD-VF]
961 // sources (MSli §4.2) extracted while building VFG_pre: the endpoints and
962 // in-span lock witnesses queried during main-phase value-flow construction.
966 {
967 initialSliceResult.insert(pthreadCallNode->getRetICFGNode());
968 }
969 initialSliceResult.insert(mutexCallNodes.begin(), mutexCallNodes.end());
971 {
972 initialSliceResult.insert(mutexCallNode->getRetICFGNode());
973 }
975 for (const SVFStmt* stmt: vulnerableStatements)
976 {
977 initialSliceResult.insert(stmt->getICFGNode());
978 }
980
981 // Retain loop-exit entry anchors. MHP seeds post-join interleaving directly at
982 // the loop-exit block entry of each join in a loop; keeping those entries makes
983 // the seed land on a kept node, so no runtime seed projection is needed. Reuses
984 // the shared FunObjVar::getExitBlocksOfLoop.
986 for (const CallICFGNode* c : pthreadCallNodes)
987 {
988 if (!threadAPI->isTDJoin(c) || c->getBB() == nullptr) continue;
989 std::vector<const SVFBasicBlock*> exitbbs;
990 c->getFun()->getExitBlocksOfLoop(c->getBB(), exitbbs);
991 for (const SVFBasicBlock* eb : exitbbs)
992 if (!eb->getICFGNodeList().empty())
993 initialSliceResult.insert(eb->front());
994 }
995
996 // Step 3: Perform dual slicing (temporal slicing)
998
999 // Step 4: Expand keptNodes to include call/ret nodes and function entry/exit
1000 // nodes (call dependence).
1002}
1003
1004//===----------------------------------------------------------------------===//
1005// MultiStageSlicer -- stage 2 (FSPTA data-dependence slice)
1006//===----------------------------------------------------------------------===//
1007
1008// The FSPTA data-dependence slice at SVFG-node granularity (memoised so the
1009// backward closure over VFG_pre is computed once and shared with ILA slicing).
1020
1021// Perform slicing for pointer analysis (returns only node set, no IRView needed)
1024{
1025
1026 // Step 1: paper-faithful (§4.3) data-dependence slice over the thread-aware
1027 // SVFG built once in pre-analysis (reusing the memoised SVFG-node closure).
1030
1031 // Retain the lock/unlock-function control-flow markers the sliced lock
1032 // analysis depends on (see collectLockFunctionMarkers); the data-dependence
1033 // SVFG slice does not otherwise keep them.
1035
1036 // Step 2: Perform dual slicing (temporal slicing)
1038
1039 return dualSlicedNodes;
1040}
1041
1042//===----------------------------------------------------------------------===//
1043// SingleSlicer
1044//===----------------------------------------------------------------------===//
1045
1047 LockAnalysis* lockAnalysis, SVFG* vfg)
1048 : MTASlicerBase(svfIr, pta, mhp, lockAnalysis, vfg)
1049{
1050}
1051
1052// Single-pass slice (the baseline of MSli §3/§5.4): the transitive closure of
1053// the target statements under the COMBINED dependence graph -- synchronization,
1054// data, and call dependence -- yielding one slice shared by ILA and FSPTA.
1057{
1058
1059 // Step 1: Synchronization dependence -- the relevant pthread (fork/join) and
1060 // mutex (lock/unlock) statements for the targets.
1064
1065 // Step 2: Seed the working set with the synchronization statements (and their
1066 // return nodes) and the target statements themselves.
1068 currentNodes.insert(pthreadCallNodes.begin(), pthreadCallNodes.end());
1070 currentNodes.insert(pthreadCallNode->getRetICFGNode());
1071 currentNodes.insert(mutexCallNodes.begin(), mutexCallNodes.end());
1073 currentNodes.insert(mutexCallNode->getRetICFGNode());
1075 for (const SVFStmt* stmt : vulnerableStatements)
1076 currentNodes.insert(stmt->getICFGNode());
1077
1078 // Step 3: Close over data dependence (the thread-aware VFG_pre value flow --
1079 // direct + indirect + interference, the same model the FSPTA stage uses) and call
1080 // dependence (function expansion), alternately, until the node set converges.
1081 bool changed = true;
1082 int iteration = 0;
1083 const int maxIterations = 100; // safety bound against non-convergence
1084 while (changed && iteration < maxIterations)
1085 {
1086 iteration++;
1088
1090 for (const ICFGNode* node : currentNodes)
1091 {
1092 const ICFGNode::SVFStmtList& stmts = node->getSVFStmts();
1093 currentStatements.insert(stmts.begin(), stmts.end());
1094 }
1095
1098 currentNodes.insert(dataDepNodes.begin(), dataDepNodes.end());
1099
1101
1103 }
1104
1105 if (iteration >= maxIterations)
1106 SVFUtil::writeWrnMsg("SingleSlicer reached max iterations, may not have converged");
1107
1108 // Step 4: One dual-slicing (temporal) pass at the end.
1110}
1111
1112} // End namespace SVF
#define DBOUT(TYPE, X)
LLVM debug macros, define type of your DBUG model of each pass.
Definition SVFType.h:576
#define DMTA
Definition SVFType.h:597
cJSON * p
Definition cJSON.cpp:2559
cJSON * n
Definition cJSON.cpp:2558
AliasResult alias(const SVFVar *V1, const SVFVar *V2) override
Interface expose to users of our pointer analysis, given Value infos.
Set< const CallICFGNode * > CallInstSet
Definition CallGraph.h:55
const FunObjVar * getFunction() const
Get function of this call node.
Definition CallGraph.h:191
const CallGraphNode * getCallGraphNode(const std::string &name) const
Get call graph node.
CallGraphEdge::CallGraphEdgeSet CallGraphEdgeSet
Definition CallGraph.h:241
CallSiteID getCallSiteID(const CallICFGNode *cs, const FunObjVar *callee) const
Get CallSiteID.
Definition CallGraph.h:389
virtual const FunObjVar * getFunction() const
Get containing function, or null for globals/constants.
const SVFBasicBlock * getEntryBlock() const
const SVFBasicBlock * front() const
const SVFBasicBlock * getExitBB() const
iterator begin()
Iterators.
bool hasGNode(NodeID id) const
Has a node.
NodeType * getGNode(NodeID id) const
Get a node.
const GEdgeSetTy & getInEdges() const
virtual const FunObjVar * getFun() const
Return the function of this ICFGNode.
Definition ICFGNode.h:75
std::list< const SVFStmt * > SVFStmtList
Definition ICFGNode.h:65
const SVFStmtList & getSVFStmts() const
Definition ICFGNode.h:116
ICFGNodeIDToNodeMapTy::iterator iterator
Definition ICFG.h:58
FunExitICFGNode * getFunExitICFGNode(const FunObjVar *fun)
Add a function exit node.
Definition ICFG.cpp:250
FunEntryICFGNode * getFunEntryICFGNode(const FunObjVar *fun)
Add a function entry node.
Definition ICFG.cpp:243
Set< CxtLock > CxtLockSet
bool hasCxtLockfromCxtStmt(const CxtStmt &cts) const
Set< CxtStmt > CxtStmtSet
bool isInsideIntraLock(const ICFGNode *stmt) const
Return true if a statement is inside an intra-procedural lock.
bool isInsideCondIntraLock(const ICFGNode *stmt) const
Return true if a statement is inside a partial lock/unlock pair (conditional lock with unconditional ...
const InstSet & getIntraLockSet(const ICFGNode *stmt) const
bool hasCxtStmtFromInst(const ICFGNode *inst) const
Context-sensitive statement and lock spans.
Set< const ICFGNode * > InstSet
const CxtStmtSet & getCxtStmtsFromInst(const ICFGNode *inst) const
const CxtLockSet & getCxtLockfromCxtStmt(const CxtStmt &cts) const
Definition MHP.h:57
virtual bool mayHappenInParallelCache(const ICFGNode *i1, const ICFGNode *i2)
Definition MHP.cpp:741
ThreadCallGraph * getThreadCallGraph() const
Get ThreadCallGraph.
Definition MHP.h:89
const CxtThreadStmtSet & getThreadStmtSet(const ICFGNode *inst) const
Get/has ThreadStmt.
Definition MHP.h:124
bool hasThreadStmtSet(const ICFGNode *inst) const
Definition MHP.h:130
TCT * getTCT() const
Get Thread Creation Tree.
Definition MHP.h:95
SVFG * vfg
thread-aware VFG_pre (PTA/Single slicers; null for MTA)
Definition MTASlicer.h:132
OrderedSet< const SVFStmt * > getDependentThreadCreate(const SVFStmt *stmt)
OrderedSet< const ICFGNode * > buildBackwardICFGNodeSet(const OrderedSet< const ICFGNode * > &vulnerableNodes)
std::pair< OrderedSet< const CallICFGNode * >, OrderedSet< const CallICFGNode * > > collectCommonThreadStatements(const OrderedSet< const SVFStmt * > &vulnerableStatements)
OrderedSet< const VFGNode * > computeDataDependenceSVFGNodes(const OrderedSet< const SVFStmt * > &seeds, SVFG *vfg)
virtual ~MTASlicerBase()
OrderedSet< const CallICFGNode * > collectMutexStatements(const OrderedSet< const SVFStmt * > &vulnerableStmts)
OrderedSet< const ICFGNode * > expandCallDependence(const OrderedSet< const ICFGNode * > &nodes)
MTASlicerBase(SVFIR *svfIr, AndersenBase *pta, MHP *mhp, LockAnalysis *lockAnalysis, SVFG *vfg=nullptr)
OrderedSet< const ICFGNode * > runDualSlicing(const OrderedSet< const ICFGNode * > &slicedNodes)
OrderedSet< const CallICFGNode * > collectPthreadStatements(const OrderedSet< const SVFStmt * > &vulnerableStmts)
AndersenBase * pta
Definition MTASlicer.h:128
OrderedSet< const ICFGNode * > getLockSet(const ICFGNode *node)
OrderedSet< const ICFGNode * > sliceDataDependenceOverVFG(const OrderedSet< const SVFStmt * > &seeds, SVFG *vfg)
OrderedSet< const TCTNode * > getTCTNodeSetFromNode(const ICFGNode *node)
LockAnalysis * lockAnalysis
Definition MTASlicer.h:130
OrderedSet< const ICFGNode * > svfgNodesToICFGNodes(const OrderedSet< const VFGNode * > &nodes, const OrderedSet< const SVFStmt * > &seeds)
Project the retained VFG nodes (plus the seeds) onto their ICFG nodes.
CallGraph * callGraph
Definition MTASlicer.h:131
OrderedSet< const VFGNode * > retainedSVFGNodes
memoised data-dependence slice
Definition MTASlicer.h:244
const OrderedSet< const VFGNode * > & getRetainedSVFGNodes(const OrderedSet< const SVFStmt * > &vulnerableStatements)
MultiStageSlicer(SVFIR *svfIr, AndersenBase *pta, MHP *mhp, LockAnalysis *lockAnalysis, SVFG *vfg=nullptr)
OrderedSet< const ICFGNode * > runILASlicing(const OrderedSet< const SVFStmt * > &vulnerableStatements, const OrderedSet< const ICFGNode * > &threadVFSources={})
OrderedSet< const ICFGNode * > runPTASlicing(const OrderedSet< const SVFStmt * > &vulnerableStatements)
static Option< u32_t > MaxContextLen
Definition Options.h:81
CallGraph * getCallGraph() const
Return call graph.
bool hasDefSVFGNode(const ValVar *valVar) const
Given a valVar, return whether it has definition site.
Definition SVFG.h:177
const SVFGNode * getDefSVFGNode(const ValVar *valVar) const
Given a valVar, return its definition site.
Definition SVFG.h:171
ICFG * getICFG() const
Definition SVFIR.h:231
ICFGNode * getICFGNode() const
OrderedSet< const ICFGNode * > runSlicing(const OrderedSet< const SVFStmt * > &vulnerableStatements)
SingleSlicer(SVFIR *svfIr, AndersenBase *pta, MHP *mhp, LockAnalysis *lockAnalysis, SVFG *vfg=nullptr)
void getKeptForkSites(std::vector< const ICFGNode * > &out) const
void collectEntryFunInCallGraph() override
Get entry functions that are neither called by other functions nor extern functions.
void collectLoopInfoForJoin() override
Handle join site in loop.
void pushCxt(CallStrCxt &cxt, const CallICFGNode *call, const FunObjVar *callee) override
Override pushCxt to use the custom maxContextLen.
void handleCallRelation(CxtThreadProc &ctp, const CallGraphEdge *cgEdge, const CallICFGNode *cs) override
Handle call relations.
const SlicedThreadCallGraphView * tcgView
Definition MTASlicer.h:103
u32_t maxContextLen
Definition MTASlicer.h:104
bool isKeptEdge(const CallGraphEdge *edge) const
void getKeptJoinSites(std::vector< const ICFGNode * > &out) const
bool isKeptNode(const CallGraphNode *node) const
SlicedTCT(PointerAnalysis *p, const SlicedSVFIRView *slicedView, u32_t maxContextLen=0)
Definition MTASlicer.cpp:66
void build() override
Build TCT.
Definition MTASlicer.cpp:79
void markRelProcs() override
Mark relevant procedures that are backward reachable from any fork/join site.
Definition MTASlicer.cpp:98
const OrderedSet< const CallGraphNode * > & getKeptNodes() const
Get all kept nodes.
bool isKeptNode(const CallGraphNode *node) const
Check if a node is in the sliced view.
const CallGraph::CallGraphEdgeSet & getKeptEdges() const
Get all kept edges.
void getOutEdgesOf(const CallGraphNode *node, std::vector< const CallGraphEdge * > &out) const
Get out edges of a node (only returns kept edges and target nodes)
void getInEdgesOf(const CallGraphNode *node, std::vector< const CallGraphEdge * > &out) const
Get in edges of a node (only returns kept edges and source nodes)
const FunSet & getMakredProcs() const
Get marked candidate functions.
Definition TCT.h:233
bool isInRecursion(const ICFGNode *inst) const
Whether an instruction is in a recursion.
Definition TCT.cpp:113
TCTNode * getTCTNode(NodeID id) const
Get TCT node.
Definition TCT.h:203
FunSet entryFuncSet
Definition TCT.h:624
virtual void collectLoopInfoForJoin()
Handle join site in loop.
Definition TCT.cpp:340
virtual void collectEntryFunInCallGraph()
Get entry functions that are neither called by other functions nor extern functions.
Definition TCT.cpp:209
InstToLoopMap joinSiteToLoopMap
Map a CxtThread to its start routine function.
Definition TCT.h:632
FunSet candidateFuncSet
Procedures that are neither called by other functions nor extern functions.
Definition TCT.h:625
bool inSameCallGraphSCC(const CallGraphNode *src, const CallGraphNode *dst)
Whether two functions in the same callgraph scc.
Definition TCT.h:303
bool isJoinMustExecutedInLoop(const LoopBBs &lp, const ICFGNode *join)
Return true if a join instruction must be executed inside a loop.
Definition TCT.cpp:316
virtual void markRelProcs()
Mark relevant procedures that are backward reachable from any fork/join site.
Definition TCT.cpp:155
Set< const ICFGNode * > inRecurJoinSites
Fork or Join sites in recursions.
Definition TCT.h:633
void dumpCxt(CallStrCxt &cxt)
Dump calling context.
Definition TCT.cpp:541
u32_t MaxCxtSize
Definition TCT.h:451
const FunSet & getEntryProcs() const
Get marked candidate functions.
Definition TCT.h:239
virtual void build()
Build TCT.
Definition TCT.cpp:409
virtual void handleCallRelation(CxtThreadProc &ctp, const CallGraphEdge *cgEdge, const CallICFGNode *call)
Handle call relations.
Definition TCT.cpp:267
ThreadCallGraph * tcg
Definition TCT.h:447
SVFLoopAndDomInfo::LoopBBs LoopBBs
Definition TCT.h:158
void reset()
Definition TCT.h:483
CallSiteSet::const_iterator forksitesEnd() const
CallSiteSet::const_iterator forksitesBegin() const
Fork sites iterators.
CallSiteSet::const_iterator joinsitesEnd() const
CallSiteSet::const_iterator joinsitesBegin() const
Join sites iterators.
ThreadAPI * getThreadAPI() const
Thread API.
bool hasStmtVFGNode(const SVFStmt *svfStmt) const
Existence checks for VFGNodes.
Definition VFG.h:217
StmtVFGNode * getStmtVFGNode(const SVFStmt *svfStmt) const
Get an VFGNode.
Definition VFG.h:261
bool isExtCall(const FunObjVar *fun)
Definition SVFUtil.cpp:441
void writeWrnMsg(const std::string &msg)
Writes a message run through wrnMsg.
Definition SVFUtil.cpp:72
for isBitcode
Definition BasicTypes.h:70
unsigned CallSiteID
Definition GeneralType.h:78
u32_t NodeID
Definition GeneralType.h:76
llvm::IRBuilder IRBuilder
Definition BasicTypes.h:76
iter_range< typename GenericGraphTraits< GraphType >::nodes_iterator > nodes(const GraphType &G)
std::vector< u32_t > CallStrCxt
Definition GeneralType.h:96
static void collectLockFunctionMarkers(const OrderedSet< const CallICFGNode * > &mutexCallNodes, OrderedSet< const ICFGNode * > &sliceResult)
unsigned u32_t
Definition GeneralType.h:67