Static Value-Flow Analysis
Loading...
Searching...
No Matches
AbstractInterpretation.cpp
Go to the documentation of this file.
1//===- AbstractExecution.cpp -- Abstract Execution---------------------------------//
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//
25// Created on: Jan 10, 2024
26// Author: Xiao Cheng, Jiawei Wang
27//
28
31#include "AE/Svfexe/AbsExtAPI.h"
32#include "SVFIR/SVFIR.h"
33#include "Util/Options.h"
34#include "Util/WorkList.h"
35#include "Graphs/CallGraph.h"
36#include "WPA/Andersen.h"
37#include <cmath>
38#include <memory>
39
40using namespace SVF;
41using namespace SVFUtil;
42
43
45{
46 stat->startClk();
47 utils = new AbsExtAPI(this);
50
51 analyse();
53 stat->endClk();
55 if (Options::PStat())
57 for (auto& detector: detectors)
58 detector->reportBug();
59}
60
62{
63 stat = new AEStat(this);
64 // Run Andersen's pointer analysis and build WTO
66 icfg = svfir->getICFG();
71}
72
77{
78 // Leak the singleton on purpose. AbstractInterpretation owns a
79 // Map<std::string, std::function<void(const CallICFGNode*)>> func_map
80 // whose lambda closures back-reference state owned by other globals
81 // (preAnalysis's WTO, the call graph, ...). Letting the static
82 // unique_ptr's atexit-time destructor run hits a static-destruction-
83 // order issue: the func_map hashtable's destructor calls into
84 // std::function destroyers whose closures touch already-destroyed
85 // state, and ~_Hashtable() segfaults during normal program shutdown.
86 //
87 // Reliably reproducible from any downstream tool that drives a full
88 // AE analysis to completion and then exits normally:
89 // - SSA's ass3 binary (Software-Security-Analysis/Assignment-3)
90 // - pysvf via Python interpreter shutdown
91 //
92 // A process-lifetime singleton has no observable lifecycle past
93 // program exit, so leaking is benign and avoids the use-after-destroy.
95 {
96 switch (Options::AESparsity())
97 {
103 default:
104 return new AbstractInterpretation();
105 }
106 }();
107 return *instance;
108}
109
110
113{
114 delete utils;
115 delete stat;
116 delete preAnalysis;
117}
118
123{
127 auto* callGraphSCC = preAnalysis->getCallGraphSCC();
128
129 for (auto it = callGraph->begin(); it != callGraph->end(); ++it)
130 {
131 const CallGraphNode* cgNode = it->second;
132 const FunObjVar* fun = cgNode->getFunction();
133
134 // Skip declarations
135 if (fun->isDeclaration())
136 continue;
137
138 if (mainEntry)
139 {
141 {
142 entryFunctions.push(fun);
143 break;
144 }
145 }
146 else
147 {
148 NodeID repNodeId = callGraphSCC->repNode(cgNode->getId());
149 if (visitedEntrySCCs.count(repNodeId))
150 continue;
151
152 const NodeBS& cgSCCNodes = callGraphSCC->subNodes(repNodeId);
153 bool hasExternalCaller = false;
154 for (NodeID nodeId : cgSCCNodes)
155 {
157 for (auto inEdge : sccNode->getInEdges())
158 {
159 if (!cgSCCNodes.test(inEdge->getSrcID()))
160 {
161 hasExternalCaller = true;
162 break;
163 }
164 }
166 break;
167 }
168
170 continue;
171
173 const FunObjVar* entryFun = fun;
174 for (NodeID nodeId : cgSCCNodes)
175 {
176 const FunObjVar* sccFun = callGraph->getGNode(nodeId)->getFunction();
178 {
180 break;
181 }
182 }
184 }
185 }
186
187 if (mainEntry && entryFunctions.empty())
188 {
190 "AE -ae-fun-entry=main requires a program entry function, but main/svf.main was not found.\n");
191 assert(false && "No program entry function found for -ae-fun-entry=main");
192 abort();
193 }
194
195 return entryFunctions;
196}
197
198
204
209{
210 // Collect all entry point functions
212
213 if (entryFunctions.empty())
214 {
215 assert(false && "No entry functions found for analysis");
216 return;
217 }
218 // handle Global ICFGNode of SVFModule
221 while (!entryFunctions.empty())
222 {
223 const FunObjVar* entryFun = entryFunctions.pop();
226 handleFunction(funEntry, nullptr);
227 }
228}
229
236{
237 const ICFGNode* node = icfg->getGlobalICFGNode();
238 // Global init is one of the few legitimate direct-mutation sites:
239 // updateAbsState filters out ValVars in semi-sparse mode, but NullPtr/
240 // BlkPtr have no SVFVar so we cannot route them through updateAbsValue.
241 // Use the manager's operator[] (auto-creates the entry if absent).
242 AbstractState& init = abstractTrace[node];
243 init = AbstractState();
244 // NullPtr has no backing SVFVar. Model it directly as the singleton null
245 // address; BlkPtr is initialized directly below for the same reason.
247
248 // Global Node, we just need to handle addr, load, store, copy and gep
249 for (const SVFStmt *stmt: node->getSVFStmts())
250 {
251 handleSVFStatement(stmt);
252 }
253
254 // BlkPtr is the canonical unknown value. Keep its address-domain meaning
255 // for pointer uses, and also give it numeric top so external-input stores
256 // can flow through ordinary store/load state as [-inf, +inf].
258 blkPtrValue.getAddrs().insert(BlackHoleObjAddr);
260}
261
269{
270 // Collect all feasible predecessor states, then merge at the end.
272 bool hasFeasiblePred = false;
273
274 for (auto& edge : node->getInEdges())
275 {
276 const ICFGNode* pred = edge->getSrcNode();
277 if (!hasAbsState(pred))
278 continue;
279
280 if (const IntraCFGEdge* intraCfgEdge = SVFUtil::dyn_cast<IntraCFGEdge>(edge))
281 {
282 if (intraCfgEdge->getCondition())
283 {
286 {
289 hasFeasiblePred = true;
290 }
291 }
292 else
293 {
295 hasFeasiblePred = true;
296 }
297 }
298 else if (SVFUtil::isa<CallCFGEdge>(edge))
299 {
301 hasFeasiblePred = true;
302 }
303 else if (SVFUtil::isa<RetCFGEdge>(edge))
304 {
305 switch (Options::HandleRecur())
306 {
307 case TOP:
309 hasFeasiblePred = true;
310 break;
311 case WIDEN_ONLY:
312 case WIDEN_NARROW:
313 {
314 const RetICFGNode* returnSite = SVFUtil::dyn_cast<RetICFGNode>(node);
315 const CallICFGNode* callSite = returnSite->getCallICFGNode();
317 {
319 hasFeasiblePred = true;
320 }
321 break;
322 }
323 }
324 }
325 }
326
327 if (!hasFeasiblePred)
328 return false;
329
330 updateAbsState(node, merged);
331
332 return true;
333}
334
345static const LoadStmt* findBackingLoad(const SVFVar* var)
346{
347 if (var->getInEdges().empty())
348 return nullptr;
349 SVFStmt* inStmt = *var->getInEdges().begin();
350 if (const LoadStmt* ls = SVFUtil::dyn_cast<LoadStmt>(inStmt))
351 return ls;
352 if (const CopyStmt* cs = SVFUtil::dyn_cast<CopyStmt>(inStmt))
353 {
354 const SVFVar* src = cs->getRHSVar();
355 if (!src->getInEdges().empty())
356 return SVFUtil::dyn_cast<LoadStmt>(*src->getInEdges().begin());
357 }
358 return nullptr;
359}
360
375 bool isLHS, const IntervalValue& self,
376 const IntervalValue& other)
377{
378 // Normalize: always reason from the LHS perspective.
379 // If we are the RHS operand, swap the predicate direction.
380 if (!isLHS)
381 {
382 // a > b from b's perspective: b < a
383 static const Map<s32_t, s32_t> swapPred =
384 {
407 };
408 auto it = swapPred.find(predicate);
409 if (it == swapPred.end()) return IntervalValue::top();
410 predicate = it->second;
411 }
412
413 // If false branch, negate the predicate.
414 if (succ == 0)
415 {
416 static const Map<s32_t, s32_t> negPred =
417 {
440 };
441 auto it = negPred.find(predicate);
442 if (it == negPred.end()) return IntervalValue::top();
443 predicate = it->second;
444 }
445
446 // Now compute the constraint on LHS given: LHS <predicate> other
448 switch (predicate)
449 {
450 case CmpStmt::ICMP_EQ:
453 result.meet_with(other);
454 break;
455 case CmpStmt::ICMP_NE:
460 return IntervalValue::top(); // no useful narrowing
465 result.meet_with(IntervalValue(other.lb() + 1, IntervalValue::plus_infinity()));
466 break;
472 break;
477 result.meet_with(IntervalValue(IntervalValue::minus_infinity(), other.ub() - 1));
478 break;
484 break;
485 default:
486 return IntervalValue::top();
487 }
488 return result;
489}
490
493{
494 const ICFGNode* pred = edge->getSrcNode();
495 s64_t succ = edge->getSuccessorCondValue();
496 const CmpStmt* cmpStmt = SVFUtil::cast<CmpStmt>(
497 *edge->getCondition()->getInEdges().begin());
498 const AbstractValue& cmpValue = getAbsValue(cmpStmt->getRes(), pred);
499 assert(cmpValue.isInterval() &&
500 "CmpStmt result must be represented by a Boolean interval");
501
502 // Feasibility check: cmp result must be compatible with branch successor
503 IntervalValue resVal = cmpValue.getInterval();
505 return !resVal.isBottom();
506}
507
510{
511 const ICFGNode* pred = edge->getSrcNode();
512 s64_t succ = edge->getSuccessorCondValue();
513 const SVFVar* var = edge->getCondition();
514
516 IntervalValue switch_cond = condVal.getInterval();
518 if (switch_cond.isBottom())
519 return false;
520 return true;
521}
522
525{
526 const SVFVar* cond = edge->getCondition();
527 const ICFGNode* pred = edge->getSrcNode();
528 const ICFGNode* succNode = edge->getDstNode();
529 s64_t succ = edge->getSuccessorCondValue();
530
531 assert(!cond->getInEdges().empty() &&
532 "branch condition has no defining edge?");
533 const SVFStmt* condDef = *cond->getInEdges().begin();
534
535 if (const CmpStmt* cmpStmt = SVFUtil::dyn_cast<CmpStmt>(condDef))
536 {
537 s32_t predicate = cmpStmt->getPredicate();
538
539 if (cmpStmt->getOpVarID(0) == IRGraph::NullPtr ||
540 cmpStmt->getOpVarID(1) == IRGraph::NullPtr)
541 {
542 // p == NULL / p != NULL: no interval obj to refine.
543 }
544 else
545 {
546 AbstractValue opVal[2] = {getAbsValue(cmpStmt->getOpVar(0), pred),
547 getAbsValue(cmpStmt->getOpVar(1), pred)
548 };
549
550 const bool hasIntervalCmp =
551 opVal[0].isInterval() && opVal[1].isInterval();
552 if (!hasIntervalCmp && (opVal[0].isAddr() || opVal[1].isAddr()))
553 {
554 // Pointer-valued cmp: branch feasibility only.
555 }
556 else
557 {
558 for (int i = 0; i < 2; i++)
559 {
560 const int other = 1 - i;
561 const LoadStmt* load =
562 findBackingLoad(cmpStmt->getOpVar(i));
563
564 if (opVal[i].getInterval().is_numeral())
565 {
566 // Example: in x < 5, operand 5 is not refined.
567 }
568 else if (!opVal[other].getInterval().is_numeral())
569 {
570 // Example: x < y, neither side has a fixed bound.
571 }
572 else if (!load)
573 {
574 // Example: cmp uses a computed temporary, not load p.
575 }
576 else
577 {
579 predicate, succ, i == 0, opVal[i].getInterval(),
580 opVal[other].getInterval());
581
582 if (narrowed.isTop())
583 {
584 // != and unsupported predicates reach here.
585 }
586 else
587 {
588 const ICFGNode* loadIcfg = load->getICFGNode();
589 const AbstractValue& ptrVal =
591 if (!ptrVal.isAddr())
592 {
593 // Cannot map load p back to concrete ObjVars.
594 }
595 else
596 {
597 for (const auto& addr : ptrVal.getAddrs())
598 {
599 NodeID objId = as.getIDFromAddr(addr);
602 }
603 }
604 }
605 }
606 }
607 }
608 }
609 }
610 else
611 {
612 const SVFVar* var = cond;
613
615 IntervalValue switch_cond = condVal.getInterval();
617 if (switch_cond.isBottom())
618 {
619 // This case label is not reachable from cond's interval.
620 }
621 else
622 {
623 as[var->getId()] = AbstractValue(switch_cond);
624
626 for (SVFStmt* stmt : var->getInEdges())
627 stmtList.push(stmt);
628 while (!stmtList.empty())
629 {
630 const SVFStmt* stmt = stmtList.pop();
631 const LoadStmt* load = SVFUtil::dyn_cast<LoadStmt>(stmt);
632 if (!load)
633 {
634 // Skip non-load definitions of the switch condition.
635 }
636 else
637 {
638 const ICFGNode* loadIcfg = load->getICFGNode();
639 const AbstractValue& ptrVal =
641 if (!ptrVal.isAddr())
642 {
643 // Cannot map load p back to concrete ObjVars.
644 }
645 else
646 {
647 for (const auto& addr : ptrVal.getAddrs())
648 {
649 NodeID objId = as.getIDFromAddr(addr);
652 }
653 }
654 }
655 }
656 }
657 }
658}
659
662 const ICFGNode* loadIcfg, const ICFGNode* /*succ*/)
663{
664 // Default (dense / semi-sparse): MEET narrowed onto obj's current
665 // value, store back into the local `as`. Caller's joinStates
666 // propagates `as` into `merged`, then `updateAbsState(succ, merged)`
667 // commits it to trace[succ].
668 //
669 // We can't go through the polymorphic updateAbsValue here: `as` is
670 // a transient per-edge predState copy that lives outside
671 // abstractTrace, so it has no node id. Writing via `updateAbsValue`
672 // with `succ` as the node would land in trace[succ] but get
673 // clobbered by the subsequent `updateAbsState(succ, merged)`; with
674 // `loadIcfg` it would corrupt the obj's authoritative value at its
675 // load site. AbstractState::store on the transient `as` is the
676 // only sound primitive — and recordBranchRefinement itself is the
677 // virtual customisation point (FullSparse routes to
678 // refinementTrace instead of touching `as`).
679 const ObjVar* objVar = SVFUtil::dyn_cast<ObjVar>(svfir->getGNode(objId));
681 {
683 if (cur.isInterval())
684 {
688 as.store(addr, AbstractValue(itv));
689 }
690 }
691}
692
695{
696 const SVFVar* cmpVar = edge->getCondition();
697 assert(!cmpVar->getInEdges().empty() && "branch condition has no defining edge?");
698 if (SVFUtil::isa<CmpStmt>(*cmpVar->getInEdges().begin()))
701}
702
710{
711 // Check reachability: pre-state must have been propagated by predecessors
712 bool isFunEntry = SVFUtil::isa<FunEntryICFGNode>(node);
713 if (!hasAbsState(node))
714 {
715 if (isFunEntry)
716 {
717 // Entry point with no callers: inherit from global node
721 else
723 }
724 else
725 {
726 return false; // unreachable node
727 }
728 }
729
730 // Store the previous state for fixpoint detection
732
733 stat->getBlockTrace()++;
735
736 // Handle SVF statements
737 for (const SVFStmt *stmt: node->getSVFStmts())
738 {
739 handleSVFStatement(stmt);
740 }
741
742 // Handle call sites
743 if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
744 {
746 }
747
748 // Run detectors
749 for (auto& detector: detectors)
750 detector->detect(node);
752
753 // Track this node as analyzed (for coverage statistics across all entry points)
754 allAnalyzedNodes.insert(node);
755
756 if (getAbsState(node) == prevState)
757 return false;
758
759 return true;
760}
761
769{
770 auto it = preAnalysis->getFuncToWTO().find(funEntry->getFun());
771 assert(it != preAnalysis->getFuncToWTO().end() && "Missing WTO for function");
772
773 // Push all top-level WTO components into the worklist in WTO order
774 FIFOWorkList<const ICFGWTOComp*> worklist(it->second->getWTOComponents());
775
776 while (!worklist.empty())
777 {
778 const ICFGWTOComp* comp = worklist.pop();
779
780 if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
781 {
782 const ICFGNode* node = singleton->getICFGNode();
784 handleICFGNode(node);
785 }
786 else if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
787 {
788 if (mergeStatesFromPredecessors(cycle->head()->getICFGNode()))
790 }
791 }
792}
793
794
796{
797 if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
798 {
799 if (isExtCall(callNode))
800 {
802 }
803 else
804 {
805 // Handle both direct and indirect calls uniformly
807 }
808 }
809 else
810 assert (false && "it is not call node");
811}
812
814{
815 return SVFUtil::isExtCall(callNode->getCalledFunction());
816}
817
819{
821 for (auto& detector : detectors)
822 {
823 detector->handleStubFunctions(callNode);
824 }
825}
826
829{
830 // Direct call: get callee directly from call node
831 if (const FunObjVar* callee = callNode->getCalledFunction())
832 return callee;
833
834 // Indirect call: resolve callee through pointer analysis
836 auto it = callsiteMaps.find(callNode);
837 if (it == callsiteMaps.end())
838 return nullptr;
839
840 NodeID call_id = it->second;
841 if (!hasAbsState(callNode))
842 return nullptr;
843
845 if (!Addrs.isAddr() || Addrs.getAddrs().empty())
846 return nullptr;
847
848 NodeID addr = *Addrs.getAddrs().begin();
849 const SVFVar* func_var = getSVFVar(getAbsState(callNode).getIDFromAddr(addr));
850 return SVFUtil::dyn_cast<FunObjVar>(func_var);
851}
852
863{
865 return;
866
867 // Direct call: callee is known
868 if (const FunObjVar* callee = callNode->getCalledFunction())
869 {
872 const RetICFGNode* retNode = callNode->getRetICFGNode();
874 return;
875 }
876
877 // Indirect call: use Andersen's call graph to get all resolved callees.
878 const RetICFGNode* retNode = callNode->getRetICFGNode();
880 {
882 for (const FunObjVar* callee : callees)
883 {
884 if (callee->isDeclaration())
885 continue;
888 }
889 }
890 // Resume return node from caller's state (context-insensitive)
892}
893
894// Loop / recursion handling (handleLoopOrRecursion + cycle helpers +
895// recursion utilities) lives in AELoopRecursion.cpp.
896
898{
899 if (const AddrStmt *addr = SVFUtil::dyn_cast<AddrStmt>(stmt))
900 {
902 }
903 else if (const BinaryOPStmt *binary = SVFUtil::dyn_cast<BinaryOPStmt>(stmt))
904 {
906 }
907 else if (const CmpStmt *cmp = SVFUtil::dyn_cast<CmpStmt>(stmt))
908 {
910 }
911 else if (SVFUtil::isa<UnaryOPStmt>(stmt))
912 {
913 }
914 else if (SVFUtil::isa<BranchStmt>(stmt))
915 {
916 // branch stmt is handled in hasBranchES
917 }
918 else if (const LoadStmt *load = SVFUtil::dyn_cast<LoadStmt>(stmt))
919 {
920 updateStateOnLoad(load);
921 }
922 else if (const StoreStmt *store = SVFUtil::dyn_cast<StoreStmt>(stmt))
923 {
924 updateStateOnStore(store);
925 }
926 else if (const CopyStmt *copy = SVFUtil::dyn_cast<CopyStmt>(stmt))
927 {
929 }
930 else if (const GepStmt *gep = SVFUtil::dyn_cast<GepStmt>(stmt))
931 {
933 }
934 else if (const SelectStmt *select = SVFUtil::dyn_cast<SelectStmt>(stmt))
935 {
937 }
938 else if (const PhiStmt *phi = SVFUtil::dyn_cast<PhiStmt>(stmt))
939 {
941 }
942 else if (const CallPE *callPE = SVFUtil::dyn_cast<CallPE>(stmt))
943 {
944 // To handle Call Edge
945 updateStateOnCall(callPE);
946 }
947 else if (const RetPE *retPE = SVFUtil::dyn_cast<RetPE>(stmt))
948 {
949 updateStateOnRet(retPE);
950 }
951 else
952 assert(false && "implement this part");
953 // NullPtr should not be changed by any statement. If the entry is missing
954 // (not yet auto-inserted) we treat that as "unchanged" — only check the
955 // entry if it actually exists.
956 {
957 const auto& vmap = getAbsState(stmt->getICFGNode()).getVarToVal();
958 auto it = vmap.find(IRGraph::NullPtr);
959 (void)it; // Suppress warning of unused variable under release build
960 assert(it == vmap.end() ||
961 (it->second.isAddr() &&
962 it->second.getAddrs().equals(AddressValue(NullMemAddr))));
963 }
964}
965
967{
968 const ICFGNode* node = gep->getICFGNode();
970 AddressValue gepAddrs = getGepObjAddrs(SVFUtil::cast<ValVar>(gep->getRHSVar()), offsetPair);
971 updateAbsValue(gep->getLHSVar(), gepAddrs, node);
972}
973
975{
976 const ICFGNode* node = select->getICFGNode();
977 const AbstractValue& condVal = getAbsValue(select->getCondition(), node);
978 const AbstractValue& tVal = getAbsValue(select->getTrueValue(), node);
979 const AbstractValue& fVal = getAbsValue(select->getFalseValue(), node);
981 if (condVal.getInterval().is_numeral())
982 {
984 }
985 else
986 {
987 resVal = tVal;
988 resVal.join_with(fVal);
989 }
990 updateAbsValue(select->getRes(), resVal, node);
991}
992
994{
995 const ICFGNode* icfgNode = phi->getICFGNode();
997 for (u32_t i = 0; i < phi->getOpVarNum(); i++)
998 {
999 const ICFGNode* opICFGNode = phi->getOpICFGNode(i);
1001 {
1003 const AbstractValue& opVal = getAbsValue(phi->getOpVar(i), opICFGNode);
1005 if (edge)
1006 {
1007 const IntraCFGEdge* intraEdge = SVFUtil::cast<IntraCFGEdge>(edge);
1008 if (intraEdge->getCondition())
1009 {
1011 rhs.join_with(opVal);
1012 }
1013 else
1014 rhs.join_with(opVal);
1015 }
1016 else
1017 {
1018 rhs.join_with(opVal);
1019 }
1020 }
1021 }
1022 updateAbsValue(phi->getRes(), rhs, icfgNode);
1023}
1024
1025
1029{
1030 const ICFGNode* node = callPE->getICFGNode();
1031 const SVFVar* res = callPE->getRes();
1033 for (u32_t i = 0; i < callPE->getOpVarNum(); i++)
1034 {
1035 const ICFGNode* opICFGNode = callPE->getOpCallICFGNode(i);
1037 {
1038 const AbstractValue& opVal = getAbsValue(callPE->getOpVar(i), opICFGNode);
1039 rhs.join_with(opVal);
1040 }
1041 }
1042 updateAbsValue(res, rhs, node);
1043}
1044
1046{
1047 const ICFGNode* node = retPE->getICFGNode();
1048 const AbstractValue& rhsVal = getAbsValue(retPE->getRHSVar(), node);
1049 updateAbsValue(retPE->getLHSVar(), rhsVal, node);
1050}
1051
1052
1054{
1055 const ICFGNode* node = addr->getICFGNode();
1056 // initObjVar mutates _varToAbsVal/_addrToAbsVal directly, so we need
1057 // mutable access; route via the manager.
1058 AbstractState& as = getAbsState(node);
1059 as.initObjVar(SVFUtil::cast<ObjVar>(addr->getRHSVar()));
1060 // AddrStmt: lhs(ValVar) = &rhs(ObjVar).
1061 // as[rhsId] stores the ObjVar's virtual address in _varToVal,
1062 // NOT the object contents. So we must use as[] directly for ObjVar.
1063 u32_t rhsId = addr->getRHSVarID();
1064 if (addr->getRHSVar()->getType()->getKind() == SVFType::SVFIntegerTy)
1065 as[rhsId].getInterval().meet_with(utils->getRangeLimitFromType(addr->getRHSVar()->getType()));
1066 // LHS is a ValVar (pointer), write through the API
1067 updateAbsValue(addr->getLHSVar(), as[rhsId], node);
1068}
1069
1070
1072{
1073 const ICFGNode* node = binary->getICFGNode();
1074 // Treat bottom (uninitialized) operands as top for soundness
1075 const AbstractValue& op0Val = getAbsValue(binary->getOpVar(0), node);
1076 const AbstractValue& op1Val = getAbsValue(binary->getOpVar(1), node);
1077 IntervalValue lhs = op0Val.getInterval().isBottom() ? IntervalValue::top() : op0Val.getInterval();
1078 IntervalValue rhs = op1Val.getInterval().isBottom() ? IntervalValue::top() : op1Val.getInterval();
1080 switch (binary->getOpcode())
1081 {
1082 case BinaryOPStmt::Add:
1083 case BinaryOPStmt::FAdd:
1084 resVal = (lhs + rhs);
1085 break;
1086 case BinaryOPStmt::Sub:
1087 case BinaryOPStmt::FSub:
1088 resVal = (lhs - rhs);
1089 break;
1090 case BinaryOPStmt::Mul:
1091 case BinaryOPStmt::FMul:
1092 resVal = (lhs * rhs);
1093 break;
1094 case BinaryOPStmt::SDiv:
1095 case BinaryOPStmt::FDiv:
1096 case BinaryOPStmt::UDiv:
1097 resVal = (lhs / rhs);
1098 break;
1099 case BinaryOPStmt::SRem:
1100 case BinaryOPStmt::FRem:
1101 case BinaryOPStmt::URem:
1102 resVal = (lhs % rhs);
1103 break;
1104 case BinaryOPStmt::Xor:
1105 resVal = (lhs ^ rhs);
1106 break;
1107 case BinaryOPStmt::And:
1108 resVal = (lhs & rhs);
1109 break;
1110 case BinaryOPStmt::Or:
1111 resVal = (lhs | rhs);
1112 break;
1113 case BinaryOPStmt::AShr:
1114 resVal = (lhs >> rhs);
1115 break;
1116 case BinaryOPStmt::Shl:
1117 resVal = (lhs << rhs);
1118 break;
1119 case BinaryOPStmt::LShr:
1120 resVal = (lhs >> rhs);
1121 break;
1122 default:
1123 assert(false && "undefined binary: ");
1124 }
1125 updateAbsValue(binary->getRes(), resVal, node);
1126}
1127
1132 u32_t predicate, const AddressValue& lhs, const AddressValue& rhs) const
1133{
1135 predicate <= CmpStmt::LAST_ICMP_PREDICATE &&
1136 "pointer comparison must use an ICMP predicate");
1137
1138 const bool lhsTargetKnown =
1139 !lhs.isBottom() && !lhs.contains(BlackHoleObjAddr);
1140 const bool rhsTargetKnown =
1141 !rhs.isBottom() && !rhs.contains(BlackHoleObjAddr);
1143 const bool targetsMayBeEqual = lhs.hasIntersect(rhs);
1144 const bool targetsMustBeEqual =
1145 targetsMayBeEqual && lhs.size() == 1 && rhs.size() == 1;
1146
1148 if (hasUnknownTarget)
1149 {
1150 // Case 1: an unknown target may equal or differ from the other target.
1152 }
1153 else if (!targetsMayBeEqual)
1154 {
1155 // Case 2: known disjoint target sets are definitely unequal.
1157 }
1158 else if (targetsMustBeEqual)
1159 {
1160 // Case 3: intersecting single-target sets contain the same target.
1162 }
1163 else
1164 {
1165 // Case 4: intersecting sets with multiple choices may be equal.
1167 }
1168
1170 if (predicate == CmpStmt::ICMP_EQ)
1171 result = equality;
1172 else if (predicate == CmpStmt::ICMP_NE)
1174 return result;
1175}
1176
1178 u32_t predicate, const IntervalValue& lhs, const IntervalValue& rhs) const
1179{
1181 switch (predicate)
1182 {
1183 case CmpStmt::ICMP_EQ:
1184 case CmpStmt::FCMP_OEQ:
1185 case CmpStmt::FCMP_UEQ:
1186 result = (lhs == rhs);
1187 break;
1188 case CmpStmt::ICMP_NE:
1189 case CmpStmt::FCMP_ONE:
1190 case CmpStmt::FCMP_UNE:
1191 result = (lhs != rhs);
1192 break;
1193 case CmpStmt::ICMP_UGT:
1194 case CmpStmt::ICMP_SGT:
1195 case CmpStmt::FCMP_OGT:
1196 case CmpStmt::FCMP_UGT:
1197 result = (lhs > rhs);
1198 break;
1199 case CmpStmt::ICMP_UGE:
1200 case CmpStmt::ICMP_SGE:
1201 case CmpStmt::FCMP_OGE:
1202 case CmpStmt::FCMP_UGE:
1203 result = (lhs >= rhs);
1204 break;
1205 case CmpStmt::ICMP_ULT:
1206 case CmpStmt::ICMP_SLT:
1207 case CmpStmt::FCMP_OLT:
1208 case CmpStmt::FCMP_ULT:
1209 result = (lhs < rhs);
1210 break;
1211 case CmpStmt::ICMP_ULE:
1212 case CmpStmt::ICMP_SLE:
1213 case CmpStmt::FCMP_OLE:
1214 case CmpStmt::FCMP_ULE:
1215 result = (lhs <= rhs);
1216 break;
1218 result = IntervalValue(0, 0);
1219 break;
1220 case CmpStmt::FCMP_TRUE:
1221 result = IntervalValue(1, 1);
1222 break;
1223 case CmpStmt::FCMP_ORD:
1224 case CmpStmt::FCMP_UNO:
1225 // Keep both outcomes because the interval domain does not track NaN.
1226 result = IntervalValue(0, 1);
1227 break;
1228 default:
1229 assert(false && "undefined compare: ");
1230 }
1231 return result;
1232}
1233
1235{
1236 const ICFGNode* node = cmp->getICFGNode();
1237 const AbstractValue& lhsValue = getAbsValue(cmp->getOpVar(0), node);
1238 const AbstractValue& rhsValue = getAbsValue(cmp->getOpVar(1), node);
1239 const bool pointerCmp = cmp->getOpVar(0)->getType()->isPointerTy();
1240 assert(pointerCmp == cmp->getOpVar(1)->getType()->isPointerTy() &&
1241 "CmpStmt operands must belong to the same value domain");
1242
1244 if (pointerCmp)
1245 {
1246 result = evaluatePointerCmp(cmp->getPredicate(), lhsValue.getAddrs(),
1247 rhsValue.getAddrs());
1248 }
1249 else
1250 {
1251 const IntervalValue lhs = lhsValue.isInterval()
1252 ? lhsValue.getInterval()
1254 const IntervalValue rhs = rhsValue.isInterval()
1255 ? rhsValue.getInterval()
1257 result = evaluateIntervalCmp(cmp->getPredicate(), lhs, rhs);
1258 }
1259 updateAbsValue(cmp->getRes(), result, node);
1260}
1261
1263{
1264 const ICFGNode* node = load->getICFGNode();
1266 loadValue(SVFUtil::cast<ValVar>(load->getRHSVar()), node);
1267 updateAbsValue(load->getLHSVar(), loaded, node);
1268}
1269
1271{
1272 const ICFGNode* node = store->getICFGNode();
1273 AbstractValue val = getAbsValue(store->getRHSVar(), node);
1274 storeValue(SVFUtil::cast<ValVar>(store->getLHSVar()), val, node);
1275}
1276
1278{
1279 const ICFGNode* node = copy->getICFGNode();
1280 const SVFVar* lhsVar = copy->getLHSVar();
1281 const SVFVar* rhsVar = copy->getRHSVar();
1282
1283 auto getZExtValue = [&](const SVFVar* var)
1284 {
1285 const SVFType* type = var->getType();
1286 if (SVFUtil::isa<SVFIntegerType>(type))
1287 {
1288 u32_t bits = type->getByteSize() * 8;
1289 const AbstractValue& val = getAbsValue(var, node);
1290 if (val.getInterval().is_numeral())
1291 {
1292 if (bits == 8)
1293 {
1294 int8_t signed_i8_value = val.getInterval().getIntNumeral();
1297 }
1298 else if (bits == 16)
1299 {
1300 s16_t signed_i16_value = val.getInterval().getIntNumeral();
1303 }
1304 else if (bits == 32)
1305 {
1306 s32_t signed_i32_value = val.getInterval().getIntNumeral();
1309 }
1310 else if (bits == 64)
1311 {
1312 s64_t signed_i64_value = val.getInterval().getIntNumeral();
1314 }
1315 else
1316 assert(false && "cannot support int type other than u8/16/32/64");
1317 }
1318 else
1319 {
1320 return IntervalValue::top();
1321 }
1322 }
1323 return IntervalValue::top();
1324 };
1325
1326 auto getTruncValue = [&](const SVFVar* var, const SVFType* dstType)
1327 {
1328 const IntervalValue& itv = getAbsValue(var, node).getInterval();
1329 if(itv.isBottom()) return itv;
1331 s64_t int_ub = itv.ub().getIntNumeral();
1332 u32_t dst_bits = dstType->getByteSize() * 8;
1333 if (dst_bits == 8)
1334 {
1335 int8_t s8_lb = static_cast<int8_t>(int_lb);
1336 int8_t s8_ub = static_cast<int8_t>(int_ub);
1337 if (s8_lb > s8_ub)
1339 return IntervalValue(s8_lb, s8_ub);
1340 }
1341 else if (dst_bits == 16)
1342 {
1343 s16_t s16_lb = static_cast<s16_t>(int_lb);
1344 s16_t s16_ub = static_cast<s16_t>(int_ub);
1345 if (s16_lb > s16_ub)
1347 return IntervalValue(s16_lb, s16_ub);
1348 }
1349 else if (dst_bits == 32)
1350 {
1351 s32_t s32_lb = static_cast<s32_t>(int_lb);
1352 s32_t s32_ub = static_cast<s32_t>(int_ub);
1353 if (s32_lb > s32_ub)
1355 return IntervalValue(s32_lb, s32_ub);
1356 }
1357 else
1358 {
1359 assert(false && "cannot support dst int type other than u8/16/32");
1360 abort();
1361 }
1362 };
1363
1364 const AbstractValue& rhsVal = getAbsValue(rhsVar, node);
1365
1366 if (copy->getCopyKind() == CopyStmt::COPYVAL)
1367 {
1369 }
1370 else if (copy->getCopyKind() == CopyStmt::ZEXT)
1371 {
1372 updateAbsValue(lhsVar, getZExtValue(rhsVar), node);
1373 }
1374 else if (copy->getCopyKind() == CopyStmt::SEXT)
1375 {
1376 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1377 }
1378 else if (copy->getCopyKind() == CopyStmt::FPTOSI)
1379 {
1380 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1381 }
1382 else if (copy->getCopyKind() == CopyStmt::FPTOUI)
1383 {
1384 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1385 }
1386 else if (copy->getCopyKind() == CopyStmt::SITOFP)
1387 {
1388 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1389 }
1390 else if (copy->getCopyKind() == CopyStmt::UITOFP)
1391 {
1392 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1393 }
1394 else if (copy->getCopyKind() == CopyStmt::TRUNC)
1395 {
1396 updateAbsValue(lhsVar, getTruncValue(rhsVar, lhsVar->getType()), node);
1397 }
1398 else if (copy->getCopyKind() == CopyStmt::FPTRUNC)
1399 {
1400 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1401 }
1402 else if (copy->getCopyKind() == CopyStmt::INTTOPTR)
1403 {
1404 //insert nullptr
1405 }
1406 else if (copy->getCopyKind() == CopyStmt::PTRTOINT)
1407 {
1409 }
1410 else if (copy->getCopyKind() == CopyStmt::BITCAST)
1411 {
1412 if (rhsVal.isAddr())
1414 }
1415 else
1416 assert(false && "undefined copy kind");
1417}
static const LoadStmt * findBackingLoad(const SVFVar *var)
static IntervalValue computeCmpConstraint(s32_t predicate, s64_t succ, bool isLHS, const IntervalValue &self, const IntervalValue &other)
#define NullMemAddr
#define BlackHoleObjAddr
newitem type
Definition cJSON.cpp:2739
copy
Definition cJSON.cpp:414
void finializeStat()
Definition AEStat.cpp:44
u32_t & getBlockTrace()
Definition AEStat.h:70
void performStat() override
Definition AEStat.cpp:120
u32_t & getICFGNodeTrace()
Definition AEStat.h:78
void countStateSize()
Definition AEStat.cpp:31
const Map< const FunObjVar *, const ICFGWTO * > & getFuncToWTO() const
Accessors for WTO data.
Definition AEWTO.h:72
CallGraph * getCallGraph() const
Definition AEWTO.h:60
CallGraphSCC * getCallGraphSCC() const
Definition AEWTO.h:64
void initWTO()
Build WTO for each function using call graph SCC.
Definition AEWTO.cpp:51
Handles external API calls and manages abstract states.
Definition AbsExtAPI.h:49
void collectCheckPoint()
void handleExtAPI(const CallICFGNode *call)
Handles an external API call.
void checkPointAllSet()
IntervalValue getRangeLimitFromType(const SVFType *type)
Gets the range limit from a type.
void handleFunction(const ICFGNode *funEntry, const CallICFGNode *caller)
Handle a function body via worklist-driven WTO traversal starting from funEntry.
void updateStateOnCall(const CallPE *callPE)
const FunObjVar * getCallee(const CallICFGNode *callNode)
Get callee function: directly for direct calls, via pointer analysis for indirect calls.
AbstractState & getAbsState(const ICFGNode *node)
void updateStateOnStore(const StoreStmt *store)
virtual void handleFunCall(const CallICFGNode *callNode)
void analyzeFromAllProgEntries()
Analyze all entry points (functions without callers)
void updateStateOnGep(const GepStmt *gep)
bool isCmpBranchEdgeFeasible(const IntraCFGEdge *edge, AbstractState &as)
Returns true if the cmp-conditional branch is feasible.
virtual bool hasAbsValue(const ValVar *var, const ICFGNode *node) const
Side-effect-free existence check.
virtual bool isExtCall(const CallICFGNode *callNode)
void updateStateOnPhi(const PhiStmt *phi)
bool handleICFGNode(const ICFGNode *node)
Handle an ICFG node: execute statements; return true if state changed.
std::vector< std::unique_ptr< AEDetector > > detectors
virtual AbstractValue loadValue(const ValVar *pointer, const ICFGNode *node)
Virtual so full-sparse can layer the GepObj overlay on top.
virtual void handleExtCall(const CallICFGNode *callNode)
bool isBranchEdgeFeasible(const IntraCFGEdge *edge, AbstractState &as)
AddressValue getGepObjAddrs(const ValVar *pointer, IntervalValue offset)
IntervalValue getGepElementIndex(const GepStmt *gep)
virtual void joinStates(AbstractState &dst, const AbstractState &src)
virtual bool mergeStatesFromPredecessors(const ICFGNode *node)
void updateStateOnSelect(const SelectStmt *select)
virtual void handleSVFStatement(const SVFStmt *stmt)
Dispatch an SVF statement (Addr/Binary/Cmp/Load/Store/Copy/Gep/Select/Phi/Call/Ret) to its handler.
bool skipRecursiveCall(const CallICFGNode *callNode)
Skip recursive callsites (within SCC); entry calls from outside SCC are not skipped.
IntervalValue evaluateIntervalCmp(u32_t predicate, const IntervalValue &lhs, const IntervalValue &rhs) const
SVFIR * svfir
Data and helpers reachable from SparseAbstractInterpretation.
virtual void handleLoopOrRecursion(const ICFGCycleWTO *cycle, const CallICFGNode *caller)
Handle a WTO cycle (loop or recursive function) using widening/narrowing iteration.
void updateStateOnAddr(const AddrStmt *addr)
virtual ~AbstractInterpretation()
Destructor.
virtual const AbstractValue & getAbsValue(const ValVar *var, const ICFGNode *node)
virtual void handleCallSite(const ICFGNode *node)
Handle a call site node: dispatch to ext-call, direct-call, or indirect-call handling.
void collectBranchRefinement(const IntraCFGEdge *edge, AbstractState &as)
void updateStateOnRet(const RetPE *retPE)
IntervalValue evaluatePointerCmp(u32_t predicate, const AddressValue &lhs, const AddressValue &rhs) const
bool hasAbsState(const ICFGNode *node)
void updateStateOnCopy(const CopyStmt *copy)
FIFOWorkList< const FunObjVar * > collectProgEntryFuns()
Get all entry point functions (functions without callers)
bool isSwitchBranchEdgeFeasible(const IntraCFGEdge *edge, AbstractState &as)
Returns true if the switch branch is feasible.
void updateStateOnLoad(const LoadStmt *load)
void updateStateOnBinary(const BinaryOPStmt *binary)
Map< const ICFGNode *, AbstractState > abstractTrace
per-node trace; owned here
static AbstractInterpretation & getAEInstance()
virtual void recordBranchRefinement(NodeID objId, const IntervalValue &narrowed, AbstractState &as, const ICFGNode *loadIcfg, const ICFGNode *succ)
virtual void updateAbsValue(const ValVar *var, const AbstractValue &val, const ICFGNode *node)
Set< const ICFGNode * > allAnalyzedNodes
virtual void storeValue(const ValVar *pointer, const AbstractValue &val, const ICFGNode *node)
const SVFVar * getSVFVar(NodeID varId) const
Retrieve SVFVar given its ID; asserts if no such variable exists.
void updateStateOnCmp(const CmpStmt *cmp)
virtual void updateAbsState(const ICFGNode *node, const AbstractState &state)
const VarToAbsValMap & getVarToVal() const
get var2val map
static u32_t getVirtualMemAddress(u32_t idx)
The physical address starts with 0x7f...... + idx.
bool isInterval() const
IntervalValue & getInterval()
s64_t getIntNumeral() const
const FunObjVar * getFunction() const
Get function of this call node.
Definition CallGraph.h:191
bool hasIndCSCallees(const CallICFGNode *cs) const
Definition CallGraph.h:335
const FunctionSet & getIndCSCallees(const CallICFGNode *cs) const
Definition CallGraph.h:339
const CallICFGNode * getOpCallICFGNode(u32_t op_idx) const
Return the CallICFGNode of the i-th operand.
@ ICMP_SGT
signed greater than
@ FCMP_UEQ
1 0 0 1 True if unordered or equal
@ FCMP_ONE
0 1 1 0 True if ordered and operands are unequal
@ ICMP_UGE
unsigned greater or equal
@ FCMP_UGT
1 0 1 0 True if unordered or greater than
@ ICMP_ULE
unsigned less or equal
@ FCMP_OGE
0 0 1 1 True if ordered and greater than or equal
@ FCMP_OLT
0 1 0 0 True if ordered and less than
@ FCMP_OGT
0 0 1 0 True if ordered and greater than
@ ICMP_NE
not equal
@ FCMP_TRUE
1 1 1 1 Always true (always folded)
@ ICMP_ULT
unsigned less than
@ FCMP_ULE
1 1 0 1 True if unordered, less than, or equal
@ ICMP_SLT
signed less than
@ ICMP_UGT
unsigned greater than
@ FCMP_OEQ
0 0 0 1 True if ordered and equal
@ FCMP_ORD
0 1 1 1 True if ordered (no nans)
@ FCMP_OLE
0 1 0 1 True if ordered and less than or equal
@ FCMP_FALSE
0 0 0 0 Always false (always folded)
@ FCMP_ULT
1 1 0 0 True if unordered or less than
@ FCMP_UNO
1 0 0 0 True if unordered: isnan(X) | isnan(Y)
@ FCMP_UGE
1 0 1 1 True if unordered, greater than, or equal
@ ICMP_SGE
signed greater or equal
@ FCMP_UNE
1 1 1 0 True if unordered or not equal
@ ICMP_SLE
signed less or equal
bool empty() const
Definition WorkList.h:161
bool isDeclaration() const
iterator begin()
Iterators.
NodeType * getGNode(NodeID id) const
Get a node.
const GEdgeSetTy & getInEdges() const
const SVFStmtList & getSVFStmts() const
Definition ICFGNode.h:116
ICFGEdge * getICFGEdge(const ICFGNode *src, const ICFGNode *dst, ICFGEdge::ICFGEdgeK kind)
Get a SVFG edge according to src and dst.
Definition ICFG.cpp:312
void updateCallGraph(CallGraph *callgraph)
update ICFG for indirect calls
Definition ICFG.cpp:428
FunEntryICFGNode * getFunEntryICFGNode(const FunObjVar *fun)
Add a function entry node.
Definition ICFG.cpp:243
GlobalICFGNode * getGlobalICFGNode() const
Definition ICFG.h:244
NodeID getBlkPtr() const
Definition IRGraph.h:254
void meet_with(const IntervalValue &other)
Return a intersected IntervalValue.
static BoundedInt minus_infinity()
Get minus infinity -inf.
const BoundedInt & ub() const
Return the upper bound.
bool isBottom() const
bool is_zero() const
Return true if the IntervalValue is [0, 0].
static BoundedInt plus_infinity()
Get plus infinity +inf.
static IntervalValue top()
Create the IntervalValue [-inf, +inf].
const BoundedInt & lb() const
Return the lower bound.
const ValVar * getLHSVar() const
const ValVar * getRHSVar() const
const ValVar * getRes() const
Result SVFVar.
const ValVar * getOpVar(u32_t pos) const
Operand SVFVars.
u32_t getOpVarNum() const
static const OptionMap< u32_t > HandleRecur
recursion handling mode, Default: TOP
Definition Options.h:240
static const OptionMap< u32_t > AESparsity
Definition Options.h:236
static const OptionMap< u32_t > AEFunEntry
Definition Options.h:237
static const Option< bool > PStat
Definition Options.h:115
const ValVar * getRHSVar() const
const ValVar * getLHSVar() const
ICFG * getICFG() const
Definition SVFIR.h:231
const CallSiteToFunPtrMap & getIndirectCallsites() const
Add/get indirect callsites.
Definition SVFIR.h:453
const SVFVar * getSVFVar(NodeID id) const
ObjVar/GepObjVar/BaseObjVar.
Definition SVFIR.h:135
static SVFIR * getPAG(bool buildFromFile=false)
Singleton design here to make sure we only have one instance during any analysis.
Definition SVFIR.h:120
virtual void endClk()
Definition SVFStat.h:66
virtual void startClk()
Definition SVFStat.h:61
ICFGNode * getICFGNode() const
u32_t getByteSize() const
Definition SVFType.h:287
NodeID getId() const
Get ID.
Definition SVFValue.h:158
const ValVar * getRHSVar() const
const ValVar * getLHSVar() const
bool isProgEntryFunction(const FunObjVar *)
Program entry function e.g. main.
Definition SVFUtil.cpp:446
std::string errMsg(const std::string &msg)
Print error message by converting a string into red string output.
Definition SVFUtil.cpp:82
std::ostream & errs()
Overwrite llvm::errs()
Definition SVFUtil.h:58
bool isExtCall(const FunObjVar *fun)
Definition SVFUtil.cpp:441
for isBitcode
Definition BasicTypes.h:70
u32_t NodeID
Definition GeneralType.h:76
signed short s16_t
Definition GeneralType.h:74
unsigned short u16_t
Definition GeneralType.h:73
WTONode< ICFG > ICFGSingletonWTO
Definition ICFGWTO.h:48
llvm::IRBuilder IRBuilder
Definition BasicTypes.h:76
signed s32_t
Definition GeneralType.h:68
unsigned u32_t
Definition GeneralType.h:67
signed long long s64_t
Definition GeneralType.h:70
WTOComponent< ICFG > ICFGWTOComp
Definition ICFGWTO.h:47