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 <deque>
39#include <memory>
40
41using namespace SVF;
42using namespace SVFUtil;
43
44
46{
47 stat->startClk();
48 utils = new AbsExtAPI(this);
51
52 analyse();
54 stat->endClk();
56 if (Options::PStat())
58 for (auto& detector: detectors)
59 detector->reportBug();
60}
61
63{
64 stat = new AEStat(this);
65 // Run Andersen's pointer analysis and build WTO
67 icfg = svfir->getICFG();
72}
73
78{
79 static std::unique_ptr<AbstractInterpretation> instance = []()
80 -> std::unique_ptr<AbstractInterpretation>
81 {
82 switch (Options::AESparsity())
83 {
85 return std::unique_ptr<AbstractInterpretation>(
88 return std::unique_ptr<AbstractInterpretation>(
91 default:
92 return std::unique_ptr<AbstractInterpretation>(
94 }
95 }();
96 return *instance;
97}
98
99
102{
103 delete utils;
104 delete stat;
105 delete preAnalysis;
106}
107
112{
113 std::deque<const FunObjVar*> entryFunctions;
114
115 for (auto it = callGraph->begin(); it != callGraph->end(); ++it)
116 {
117 const CallGraphNode* cgNode = it->second;
118 const FunObjVar* fun = cgNode->getFunction();
119
120 // Skip declarations
121 if (fun->isDeclaration())
122 continue;
123
124 // Entry points are functions without callers (no incoming edges)
125 if (cgNode->getInEdges().empty())
126 {
127 // If main exists, put it first for priority using deque's push_front
129 {
130 entryFunctions.push_front(fun);
131 }
132 else
133 {
134 entryFunctions.push_back(fun);
135 }
136 }
137 }
138
139 return entryFunctions;
140}
141
142
145{
146 // Always use multi-entry analysis from all entry points
148}
149
154{
155 // Collect all entry point functions
156 std::deque<const FunObjVar*> entryFunctions = collectProgEntryFuns();
157
158 if (entryFunctions.empty())
159 {
160 assert(false && "No entry functions found for analysis");
161 return;
162 }
163 // handle Global ICFGNode of SVFModule
165 for (const FunObjVar* entryFun : entryFunctions)
166 {
169 }
170}
171
178{
179 const ICFGNode* node = icfg->getGlobalICFGNode();
180 // Global init is one of the few legitimate direct-mutation sites:
181 // updateAbsState filters out ValVars in semi-sparse mode, but NullPtr/
182 // BlkPtr have no SVFVar so we cannot route them through updateAbsValue.
183 // Use the manager's operator[] (auto-creates the entry if absent).
184 AbstractState& init = abstractTrace[node];
185 init = AbstractState();
186 // TODO: we cannot find right SVFVar for NullPtr, so we use init[NullPtr]
187 // directly. Same for BlkPtr below.
189
190 // Global Node, we just need to handle addr, load, store, copy and gep
191 for (const SVFStmt *stmt: node->getSVFStmts())
192 {
194 }
195
196 // BlkPtr represents a pointer whose target is statically unknown (e.g., from
197 // int2ptr casts, external function returns, or unmodeled instructions like
198 // AtomicCmpXchg). It should be an address pointing to the BlackHole object
199 // (ID=2), NOT an interval top.
200 //
201 // History: this was originally set to IntervalValue::top() as a quick fix when
202 // the analysis crashed on programs containing uninitialized BlkPtr. However,
203 // BlkPtr is semantically a *pointer* (address domain), not a numeric value
204 // (interval domain). Setting it to interval top broke cross-domain consistency:
205 // the interval domain and address domain gave contradictory information for the
206 // same variable. The correct representation is an AddressValue containing the
207 // BlackHole virtual address, which means "points to unknown memory".
209}
210
218{
219 // Collect all feasible predecessor states, then merge at the end.
221 bool hasFeasiblePred = false;
222
223 for (auto& edge : node->getInEdges())
224 {
225 const ICFGNode* pred = edge->getSrcNode();
226 if (!hasAbsState(pred))
227 continue;
228
229 if (const IntraCFGEdge* intraCfgEdge = SVFUtil::dyn_cast<IntraCFGEdge>(edge))
230 {
231 if (intraCfgEdge->getCondition())
232 {
235 {
237 hasFeasiblePred = true;
238 }
239 }
240 else
241 {
243 hasFeasiblePred = true;
244 }
245 }
246 else if (SVFUtil::isa<CallCFGEdge>(edge))
247 {
249 hasFeasiblePred = true;
250 }
251 else if (SVFUtil::isa<RetCFGEdge>(edge))
252 {
253 switch (Options::HandleRecur())
254 {
255 case TOP:
257 hasFeasiblePred = true;
258 break;
259 case WIDEN_ONLY:
260 case WIDEN_NARROW:
261 {
262 const RetICFGNode* returnSite = SVFUtil::dyn_cast<RetICFGNode>(node);
263 const CallICFGNode* callSite = returnSite->getCallICFGNode();
265 {
267 hasFeasiblePred = true;
268 }
269 break;
270 }
271 }
272 }
273 }
274
275 if (!hasFeasiblePred)
276 return false;
277
278 updateAbsState(node, merged);
279
280 return true;
281}
282
283
294static const LoadStmt* findBackingLoad(const SVFVar* var)
295{
296 if (var->getInEdges().empty())
297 return nullptr;
298 SVFStmt* inStmt = *var->getInEdges().begin();
299 if (const LoadStmt* ls = SVFUtil::dyn_cast<LoadStmt>(inStmt))
300 return ls;
301 if (const CopyStmt* cs = SVFUtil::dyn_cast<CopyStmt>(inStmt))
302 {
303 const SVFVar* src = cs->getRHSVar();
304 if (!src->getInEdges().empty())
305 return SVFUtil::dyn_cast<LoadStmt>(*src->getInEdges().begin());
306 }
307 return nullptr;
308}
309
324 bool isLHS, const IntervalValue& self,
325 const IntervalValue& other)
326{
327 // Normalize: always reason from the LHS perspective.
328 // If we are the RHS operand, swap the predicate direction.
329 if (!isLHS)
330 {
331 // a > b from b's perspective: b < a
332 static const Map<s32_t, s32_t> swapPred =
333 {
356 };
357 auto it = swapPred.find(predicate);
358 if (it == swapPred.end()) return IntervalValue::top();
359 predicate = it->second;
360 }
361
362 // If false branch, negate the predicate.
363 if (succ == 0)
364 {
365 static const Map<s32_t, s32_t> negPred =
366 {
389 };
390 auto it = negPred.find(predicate);
391 if (it == negPred.end()) return IntervalValue::top();
392 predicate = it->second;
393 }
394
395 // Now compute the constraint on LHS given: LHS <predicate> other
397 switch (predicate)
398 {
399 case CmpStmt::ICMP_EQ:
403 break;
404 case CmpStmt::ICMP_NE:
409 return IntervalValue::top(); // no useful narrowing
415 break;
421 break;
427 break;
433 break;
434 default:
435 return IntervalValue::top();
436 }
437 return result;
438}
439
442{
443 const ICFGNode* pred = edge->getSrcNode();
444 s64_t succ = edge->getSuccessorCondValue();
445 const CmpStmt* cmpStmt = SVFUtil::cast<CmpStmt>(
446 *edge->getCondition()->getInEdges().begin());
447 s32_t predicate = cmpStmt->getPredicate();
448
449 if (cmpStmt->getOpVarID(0) == IRGraph::NullPtr ||
450 cmpStmt->getOpVarID(1) == IRGraph::NullPtr)
451 return true;
452
454 {
455 getAbsValue(cmpStmt->getOpVar(0), pred),
456 getAbsValue(cmpStmt->getOpVar(1), pred)
457 };
458 if (opVal[0].isAddr() || opVal[1].isAddr())
459 return true;
460
461 // Feasibility check: cmp result must be compatible with branch successor
464 if (resVal.isBottom())
465 return false;
466
467 // For each operand: if it is the variable side (non-constant), has a
468 // backing load, and the branch imposes a useful constraint, narrow the
469 // ObjVar behind the load.
470 for (int i = 0; i < 2; i++)
471 {
472 if (opVal[i].getInterval().is_numeral() || !opVal[1-i].getInterval().is_numeral())
473 continue; // skip constant operands and var-vs-var
474
475 if (const LoadStmt* load = findBackingLoad(cmpStmt->getOpVar(i)))
476 {
478 predicate, succ, i == 0,
479 opVal[i].getInterval(), opVal[1-i].getInterval());
480
481 if (!narrowed.isTop())
482 {
483 const AbstractValue& ptrVal = getAbsValue(load->getRHSVar(), pred);
484 if (ptrVal.isAddr())
485 {
486 for (const auto& addr : ptrVal.getAddrs())
487 {
488 NodeID objId = as.getIDFromAddr(addr);
489 if (as.inAddrToValTable(objId))
490 as.load(addr).meet_with(narrowed);
491 }
492 }
493 }
494 }
495 }
496 return true;
497}
498
501{
502 const ICFGNode* pred = edge->getSrcNode();
503 s64_t succ = edge->getSuccessorCondValue();
504 const SVFVar* var = edge->getCondition();
505
506 if (!as.inVarToValTable(var->getId()) && !as.inVarToAddrsTable(var->getId()))
507 as[var->getId()] = getAbsValue(var, pred);
508 IntervalValue& switch_cond = as[var->getId()].getInterval();
510 if (switch_cond.isBottom())
511 return false;
512
514 for (SVFStmt* stmt : var->getInEdges())
515 stmtList.push(stmt);
516 while (!stmtList.empty())
517 {
518 const SVFStmt* stmt = stmtList.pop();
519 if (const LoadStmt* load = SVFUtil::dyn_cast<LoadStmt>(stmt))
520 {
521 const AbstractValue& ptrVal = getAbsValue(load->getRHSVar(), pred);
522 if (ptrVal.isAddr())
523 {
524 for (const auto& addr : ptrVal.getAddrs())
525 {
526 NodeID objId = as.getIDFromAddr(addr);
527 if (as.inAddrToValTable(objId))
528 as.load(addr).meet_with(switch_cond);
529 }
530 }
531 }
532 }
533 return true;
534}
535
538{
539 const SVFVar* cmpVar = edge->getCondition();
540 assert(!cmpVar->getInEdges().empty() && "branch condition has no defining edge?");
541 if (SVFUtil::isa<CmpStmt>(*cmpVar->getInEdges().begin()))
542 return isCmpBranchFeasible(edge, as);
544}
545
553{
554 // Check reachability: pre-state must have been propagated by predecessors
555 bool isFunEntry = SVFUtil::isa<FunEntryICFGNode>(node);
556 if (!hasAbsState(node))
557 {
558 if (isFunEntry)
559 {
560 // Entry point with no callers: inherit from global node
564 else
566 }
567 else
568 {
569 return false; // unreachable node
570 }
571 }
572
573 // Store the previous state for fixpoint detection
575
576 stat->getBlockTrace()++;
578
579 // Handle SVF statements
580 for (const SVFStmt *stmt: node->getSVFStmts())
581 {
583 }
584
585 // Handle call sites
586 if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
587 {
589 }
590
591 // Run detectors
592 for (auto& detector: detectors)
593 detector->detect(node);
595
596 // Track this node as analyzed (for coverage statistics across all entry points)
597 allAnalyzedNodes.insert(node);
598
599 if (getAbsState(node) == prevState)
600 return false;
601
602 return true;
603}
604
612{
613 auto it = preAnalysis->getFuncToWTO().find(funEntry->getFun());
614 if (it == preAnalysis->getFuncToWTO().end())
615 return;
616
617 // Push all top-level WTO components into the worklist in WTO order
618 FIFOWorkList<const ICFGWTOComp*> worklist(it->second->getWTOComponents());
619
620 while (!worklist.empty())
621 {
622 const ICFGWTOComp* comp = worklist.pop();
623
624 if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
625 {
626 const ICFGNode* node = singleton->getICFGNode();
628 handleICFGNode(node);
629 }
630 else if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
631 {
632 if (mergeStatesFromPredecessors(cycle->head()->getICFGNode()))
634 }
635 }
636}
637
638
640{
641 if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
642 {
643 if (isExtCall(callNode))
644 {
646 }
647 else
648 {
649 // Handle both direct and indirect calls uniformly
651 }
652 }
653 else
654 assert (false && "it is not call node");
655}
656
658{
659 return SVFUtil::isExtCall(callNode->getCalledFunction());
660}
661
663{
665 for (auto& detector : detectors)
666 {
667 detector->handleStubFunctions(callNode);
668 }
669}
670
673{
674 // Direct call: get callee directly from call node
675 if (const FunObjVar* callee = callNode->getCalledFunction())
676 return callee;
677
678 // Indirect call: resolve callee through pointer analysis
680 auto it = callsiteMaps.find(callNode);
681 if (it == callsiteMaps.end())
682 return nullptr;
683
684 NodeID call_id = it->second;
685 if (!hasAbsState(callNode))
686 return nullptr;
687
689 if (!Addrs.isAddr() || Addrs.getAddrs().empty())
690 return nullptr;
691
692 NodeID addr = *Addrs.getAddrs().begin();
693 const SVFVar* func_var = getSVFVar(getAbsState(callNode).getIDFromAddr(addr));
694 return SVFUtil::dyn_cast<FunObjVar>(func_var);
695}
696
707{
709 return;
710
711 // Direct call: callee is known
712 if (const FunObjVar* callee = callNode->getCalledFunction())
713 {
716 const RetICFGNode* retNode = callNode->getRetICFGNode();
718 return;
719 }
720
721 // Indirect call: use Andersen's call graph to get all resolved callees.
722 const RetICFGNode* retNode = callNode->getRetICFGNode();
724 {
726 for (const FunObjVar* callee : callees)
727 {
728 if (callee->isDeclaration())
729 continue;
732 }
733 }
734 // Resume return node from caller's state (context-insensitive)
736}
737
738// Loop / recursion handling (handleLoopOrRecursion + cycle helpers +
739// recursion utilities) lives in AELoopRecursion.cpp.
740
742{
743 if (const AddrStmt *addr = SVFUtil::dyn_cast<AddrStmt>(stmt))
744 {
746 }
747 else if (const BinaryOPStmt *binary = SVFUtil::dyn_cast<BinaryOPStmt>(stmt))
748 {
750 }
751 else if (const CmpStmt *cmp = SVFUtil::dyn_cast<CmpStmt>(stmt))
752 {
754 }
755 else if (SVFUtil::isa<UnaryOPStmt>(stmt))
756 {
757 }
758 else if (SVFUtil::isa<BranchStmt>(stmt))
759 {
760 // branch stmt is handled in hasBranchES
761 }
762 else if (const LoadStmt *load = SVFUtil::dyn_cast<LoadStmt>(stmt))
763 {
764 updateStateOnLoad(load);
765 }
766 else if (const StoreStmt *store = SVFUtil::dyn_cast<StoreStmt>(stmt))
767 {
768 updateStateOnStore(store);
769 }
770 else if (const CopyStmt *copy = SVFUtil::dyn_cast<CopyStmt>(stmt))
771 {
773 }
774 else if (const GepStmt *gep = SVFUtil::dyn_cast<GepStmt>(stmt))
775 {
777 }
778 else if (const SelectStmt *select = SVFUtil::dyn_cast<SelectStmt>(stmt))
779 {
781 }
782 else if (const PhiStmt *phi = SVFUtil::dyn_cast<PhiStmt>(stmt))
783 {
785 }
786 else if (const CallPE *callPE = SVFUtil::dyn_cast<CallPE>(stmt))
787 {
788 // To handle Call Edge
789 updateStateOnCall(callPE);
790 }
791 else if (const RetPE *retPE = SVFUtil::dyn_cast<RetPE>(stmt))
792 {
793 updateStateOnRet(retPE);
794 }
795 else
796 assert(false && "implement this part");
797 // NullPtr should not be changed by any statement. If the entry is missing
798 // (not yet auto-inserted) we treat that as "unchanged" — only check the
799 // entry if it actually exists.
800 {
801 const auto& vmap = getAbsState(stmt->getICFGNode()).getVarToVal();
802 auto it = vmap.find(IRGraph::NullPtr);
803 assert(it == vmap.end() ||
804 (!it->second.isInterval() && !it->second.isAddr()));
805 }
806}
807
809{
810 const ICFGNode* node = gep->getICFGNode();
812 AddressValue gepAddrs = getGepObjAddrs(SVFUtil::cast<ValVar>(gep->getRHSVar()), offsetPair);
813 updateAbsValue(gep->getLHSVar(), gepAddrs, node);
814}
815
817{
818 const ICFGNode* node = select->getICFGNode();
819 const AbstractValue& condVal = getAbsValue(select->getCondition(), node);
820 const AbstractValue& tVal = getAbsValue(select->getTrueValue(), node);
821 const AbstractValue& fVal = getAbsValue(select->getFalseValue(), node);
823 if (condVal.getInterval().is_numeral())
824 {
826 }
827 else
828 {
829 resVal = tVal;
830 resVal.join_with(fVal);
831 }
832 updateAbsValue(select->getRes(), resVal, node);
833}
834
836{
837 const ICFGNode* icfgNode = phi->getICFGNode();
839 for (u32_t i = 0; i < phi->getOpVarNum(); i++)
840 {
841 const ICFGNode* opICFGNode = phi->getOpICFGNode(i);
843 {
845 const AbstractValue& opVal = getAbsValue(phi->getOpVar(i), opICFGNode);
847 if (edge)
848 {
849 const IntraCFGEdge* intraEdge = SVFUtil::cast<IntraCFGEdge>(edge);
850 if (intraEdge->getCondition())
851 {
853 rhs.join_with(opVal);
854 }
855 else
856 rhs.join_with(opVal);
857 }
858 else
859 {
860 rhs.join_with(opVal);
861 }
862 }
863 }
864 updateAbsValue(phi->getRes(), rhs, icfgNode);
865}
866
867
871{
872 const ICFGNode* node = callPE->getICFGNode();
873 const SVFVar* res = callPE->getRes();
875 for (u32_t i = 0; i < callPE->getOpVarNum(); i++)
876 {
877 const ICFGNode* opICFGNode = callPE->getOpCallICFGNode(i);
879 {
881 rhs.join_with(opVal);
882 }
883 }
884 updateAbsValue(res, rhs, node);
885}
886
888{
889 const ICFGNode* node = retPE->getICFGNode();
890 const AbstractValue& rhsVal = getAbsValue(retPE->getRHSVar(), node);
891 updateAbsValue(retPE->getLHSVar(), rhsVal, node);
892}
893
894
896{
897 const ICFGNode* node = addr->getICFGNode();
898 // initObjVar mutates _varToAbsVal/_addrToAbsVal directly, so we need
899 // mutable access; route via the manager.
901 as.initObjVar(SVFUtil::cast<ObjVar>(addr->getRHSVar()));
902 // AddrStmt: lhs(ValVar) = &rhs(ObjVar).
903 // as[rhsId] stores the ObjVar's virtual address in _varToVal,
904 // NOT the object contents. So we must use as[] directly for ObjVar.
905 u32_t rhsId = addr->getRHSVarID();
906 if (addr->getRHSVar()->getType()->getKind() == SVFType::SVFIntegerTy)
907 as[rhsId].getInterval().meet_with(utils->getRangeLimitFromType(addr->getRHSVar()->getType()));
908 // LHS is a ValVar (pointer), write through the API
909 updateAbsValue(addr->getLHSVar(), as[rhsId], node);
910}
911
912
914{
915 const ICFGNode* node = binary->getICFGNode();
916 // Treat bottom (uninitialized) operands as top for soundness
917 const AbstractValue& op0Val = getAbsValue(binary->getOpVar(0), node);
918 const AbstractValue& op1Val = getAbsValue(binary->getOpVar(1), node);
919 IntervalValue lhs = op0Val.getInterval().isBottom() ? IntervalValue::top() : op0Val.getInterval();
920 IntervalValue rhs = op1Val.getInterval().isBottom() ? IntervalValue::top() : op1Val.getInterval();
922 switch (binary->getOpcode())
923 {
926 resVal = (lhs + rhs);
927 break;
930 resVal = (lhs - rhs);
931 break;
934 resVal = (lhs * rhs);
935 break;
939 resVal = (lhs / rhs);
940 break;
944 resVal = (lhs % rhs);
945 break;
947 resVal = (lhs ^ rhs);
948 break;
950 resVal = (lhs & rhs);
951 break;
952 case BinaryOPStmt::Or:
953 resVal = (lhs | rhs);
954 break;
956 resVal = (lhs >> rhs);
957 break;
959 resVal = (lhs << rhs);
960 break;
962 resVal = (lhs >> rhs);
963 break;
964 default:
965 assert(false && "undefined binary: ");
966 }
967 updateAbsValue(binary->getRes(), resVal, node);
968}
969
971{
972 const ICFGNode* node = cmp->getICFGNode();
973 u32_t op0 = cmp->getOpVarID(0);
974 u32_t op1 = cmp->getOpVarID(1);
975 const AbstractValue& op0Val = getAbsValue(cmp->getOpVar(0), node);
976 const AbstractValue& op1Val = getAbsValue(cmp->getOpVar(1), node);
977
978 // if it is address
979 if (op0Val.isAddr() && op1Val.isAddr())
980 {
982 const AddressValue& addrOp0 = op0Val.getAddrs();
983 const AddressValue& addrOp1 = op1Val.getAddrs();
984 if (addrOp0.equals(addrOp1))
985 {
986 resVal = IntervalValue(1, 1);
987 }
988 else if (addrOp0.hasIntersect(addrOp1))
989 {
990 resVal = IntervalValue(0, 1);
991 }
992 else
993 {
994 resVal = IntervalValue(0, 0);
995 }
996 updateAbsValue(cmp->getRes(), resVal, node);
997 }
998 // if op0 or op1 is nullptr, compare abstractValue instead of touching addr or interval
999 else if (op0 == IRGraph::NullPtr || op1 == IRGraph::NullPtr)
1000 {
1001 IntervalValue resVal = (op0Val.equals(op1Val)) ? IntervalValue(1, 1) : IntervalValue(0, 0);
1002 updateAbsValue(cmp->getRes(), resVal, node);
1003 }
1004 else
1005 {
1006 {
1008 if (op0Val.isInterval() && op1Val.isInterval())
1009 {
1010 // Treat bottom (uninitialized) operands as top for soundness
1011 IntervalValue lhs = op0Val.getInterval().isBottom() ? IntervalValue::top() : op0Val.getInterval(),
1012 rhs = op1Val.getInterval().isBottom() ? IntervalValue::top() : op1Val.getInterval();
1013 // AbstractValue
1014 auto predicate = cmp->getPredicate();
1015 switch (predicate)
1016 {
1017 case CmpStmt::ICMP_EQ:
1018 case CmpStmt::FCMP_OEQ:
1019 case CmpStmt::FCMP_UEQ:
1020 resVal = (lhs == rhs);
1021 // resVal = (lhs.getInterval() == rhs.getInterval());
1022 break;
1023 case CmpStmt::ICMP_NE:
1024 case CmpStmt::FCMP_ONE:
1025 case CmpStmt::FCMP_UNE:
1026 resVal = (lhs != rhs);
1027 break;
1028 case CmpStmt::ICMP_UGT:
1029 case CmpStmt::ICMP_SGT:
1030 case CmpStmt::FCMP_OGT:
1031 case CmpStmt::FCMP_UGT:
1032 resVal = (lhs > rhs);
1033 break;
1034 case CmpStmt::ICMP_UGE:
1035 case CmpStmt::ICMP_SGE:
1036 case CmpStmt::FCMP_OGE:
1037 case CmpStmt::FCMP_UGE:
1038 resVal = (lhs >= rhs);
1039 break;
1040 case CmpStmt::ICMP_ULT:
1041 case CmpStmt::ICMP_SLT:
1042 case CmpStmt::FCMP_OLT:
1043 case CmpStmt::FCMP_ULT:
1044 resVal = (lhs < rhs);
1045 break;
1046 case CmpStmt::ICMP_ULE:
1047 case CmpStmt::ICMP_SLE:
1048 case CmpStmt::FCMP_OLE:
1049 case CmpStmt::FCMP_ULE:
1050 resVal = (lhs <= rhs);
1051 break;
1053 resVal = IntervalValue(0, 0);
1054 break;
1055 case CmpStmt::FCMP_TRUE:
1056 resVal = IntervalValue(1, 1);
1057 break;
1058 case CmpStmt::FCMP_ORD:
1059 case CmpStmt::FCMP_UNO:
1060 // FCMP_ORD: true if both operands are not NaN
1061 // FCMP_UNO: true if either operand is NaN
1062 // Conservatively return [0, 1] since we don't track NaN
1063 resVal = IntervalValue(0, 1);
1064 break;
1065 default:
1066 assert(false && "undefined compare: ");
1067 }
1068 updateAbsValue(cmp->getRes(), resVal, node);
1069 }
1070 else if (op0Val.isAddr() && op1Val.isAddr())
1071 {
1072 const AddressValue& lhs = op0Val.getAddrs();
1073 const AddressValue& rhs = op1Val.getAddrs();
1074 auto predicate = cmp->getPredicate();
1075 switch (predicate)
1076 {
1077 case CmpStmt::ICMP_EQ:
1078 case CmpStmt::FCMP_OEQ:
1079 case CmpStmt::FCMP_UEQ:
1080 {
1081 if (lhs.hasIntersect(rhs))
1082 {
1083 resVal = IntervalValue(0, 1);
1084 }
1085 else if (lhs.empty() && rhs.empty())
1086 {
1087 resVal = IntervalValue(1, 1);
1088 }
1089 else
1090 {
1091 resVal = IntervalValue(0, 0);
1092 }
1093 break;
1094 }
1095 case CmpStmt::ICMP_NE:
1096 case CmpStmt::FCMP_ONE:
1097 case CmpStmt::FCMP_UNE:
1098 {
1099 if (lhs.hasIntersect(rhs))
1100 {
1101 resVal = IntervalValue(0, 1);
1102 }
1103 else if (lhs.empty() && rhs.empty())
1104 {
1105 resVal = IntervalValue(0, 0);
1106 }
1107 else
1108 {
1109 resVal = IntervalValue(1, 1);
1110 }
1111 break;
1112 }
1113 case CmpStmt::ICMP_UGT:
1114 case CmpStmt::ICMP_SGT:
1115 case CmpStmt::FCMP_OGT:
1116 case CmpStmt::FCMP_UGT:
1117 {
1118 if (lhs.size() == 1 && rhs.size() == 1)
1119 {
1120 resVal = IntervalValue(*lhs.begin() > *rhs.begin());
1121 }
1122 else
1123 {
1124 resVal = IntervalValue(0, 1);
1125 }
1126 break;
1127 }
1128 case CmpStmt::ICMP_UGE:
1129 case CmpStmt::ICMP_SGE:
1130 case CmpStmt::FCMP_OGE:
1131 case CmpStmt::FCMP_UGE:
1132 {
1133 if (lhs.size() == 1 && rhs.size() == 1)
1134 {
1135 resVal = IntervalValue(*lhs.begin() >= *rhs.begin());
1136 }
1137 else
1138 {
1139 resVal = IntervalValue(0, 1);
1140 }
1141 break;
1142 }
1143 case CmpStmt::ICMP_ULT:
1144 case CmpStmt::ICMP_SLT:
1145 case CmpStmt::FCMP_OLT:
1146 case CmpStmt::FCMP_ULT:
1147 {
1148 if (lhs.size() == 1 && rhs.size() == 1)
1149 {
1150 resVal = IntervalValue(*lhs.begin() < *rhs.begin());
1151 }
1152 else
1153 {
1154 resVal = IntervalValue(0, 1);
1155 }
1156 break;
1157 }
1158 case CmpStmt::ICMP_ULE:
1159 case CmpStmt::ICMP_SLE:
1160 case CmpStmt::FCMP_OLE:
1161 case CmpStmt::FCMP_ULE:
1162 {
1163 if (lhs.size() == 1 && rhs.size() == 1)
1164 {
1165 resVal = IntervalValue(*lhs.begin() <= *rhs.begin());
1166 }
1167 else
1168 {
1169 resVal = IntervalValue(0, 1);
1170 }
1171 break;
1172 }
1174 resVal = IntervalValue(0, 0);
1175 break;
1176 case CmpStmt::FCMP_TRUE:
1177 resVal = IntervalValue(1, 1);
1178 break;
1179 case CmpStmt::FCMP_ORD:
1180 case CmpStmt::FCMP_UNO:
1181 // FCMP_ORD: true if both operands are not NaN
1182 // FCMP_UNO: true if either operand is NaN
1183 // Conservatively return [0, 1] since we don't track NaN
1184 resVal = IntervalValue(0, 1);
1185 break;
1186 default:
1187 assert(false && "undefined compare: ");
1188 }
1189 updateAbsValue(cmp->getRes(), resVal, node);
1190 }
1191 }
1192 }
1193}
1194
1196{
1197 const ICFGNode* node = load->getICFGNode();
1198 updateAbsValue(load->getLHSVar(),
1199 loadValue(SVFUtil::cast<ValVar>(load->getRHSVar()), node), node);
1200}
1201
1203{
1204 const ICFGNode* node = store->getICFGNode();
1205 storeValue(SVFUtil::cast<ValVar>(store->getLHSVar()),
1206 getAbsValue(store->getRHSVar(), node), node);
1207}
1208
1210{
1211 const ICFGNode* node = copy->getICFGNode();
1212 const SVFVar* lhsVar = copy->getLHSVar();
1213 const SVFVar* rhsVar = copy->getRHSVar();
1214
1215 auto getZExtValue = [&](const SVFVar* var)
1216 {
1217 const SVFType* type = var->getType();
1218 if (SVFUtil::isa<SVFIntegerType>(type))
1219 {
1220 u32_t bits = type->getByteSize() * 8;
1221 const AbstractValue& val = getAbsValue(var, node);
1222 if (val.getInterval().is_numeral())
1223 {
1224 if (bits == 8)
1225 {
1226 int8_t signed_i8_value = val.getInterval().getIntNumeral();
1229 }
1230 else if (bits == 16)
1231 {
1232 s16_t signed_i16_value = val.getInterval().getIntNumeral();
1235 }
1236 else if (bits == 32)
1237 {
1238 s32_t signed_i32_value = val.getInterval().getIntNumeral();
1241 }
1242 else if (bits == 64)
1243 {
1244 s64_t signed_i64_value = val.getInterval().getIntNumeral();
1246 }
1247 else
1248 assert(false && "cannot support int type other than u8/16/32/64");
1249 }
1250 else
1251 {
1252 return IntervalValue::top();
1253 }
1254 }
1255 return IntervalValue::top();
1256 };
1257
1258 auto getTruncValue = [&](const SVFVar* var, const SVFType* dstType)
1259 {
1260 const IntervalValue& itv = getAbsValue(var, node).getInterval();
1261 if(itv.isBottom()) return itv;
1263 s64_t int_ub = itv.ub().getIntNumeral();
1264 u32_t dst_bits = dstType->getByteSize() * 8;
1265 if (dst_bits == 8)
1266 {
1267 int8_t s8_lb = static_cast<int8_t>(int_lb);
1268 int8_t s8_ub = static_cast<int8_t>(int_ub);
1269 if (s8_lb > s8_ub)
1271 return IntervalValue(s8_lb, s8_ub);
1272 }
1273 else if (dst_bits == 16)
1274 {
1275 s16_t s16_lb = static_cast<s16_t>(int_lb);
1276 s16_t s16_ub = static_cast<s16_t>(int_ub);
1277 if (s16_lb > s16_ub)
1279 return IntervalValue(s16_lb, s16_ub);
1280 }
1281 else if (dst_bits == 32)
1282 {
1283 s32_t s32_lb = static_cast<s32_t>(int_lb);
1284 s32_t s32_ub = static_cast<s32_t>(int_ub);
1285 if (s32_lb > s32_ub)
1287 return IntervalValue(s32_lb, s32_ub);
1288 }
1289 else
1290 {
1291 assert(false && "cannot support dst int type other than u8/16/32");
1292 abort();
1293 }
1294 };
1295
1296 const AbstractValue& rhsVal = getAbsValue(rhsVar, node);
1297
1298 if (copy->getCopyKind() == CopyStmt::COPYVAL)
1299 {
1301 }
1302 else if (copy->getCopyKind() == CopyStmt::ZEXT)
1303 {
1304 updateAbsValue(lhsVar, getZExtValue(rhsVar), node);
1305 }
1306 else if (copy->getCopyKind() == CopyStmt::SEXT)
1307 {
1308 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1309 }
1310 else if (copy->getCopyKind() == CopyStmt::FPTOSI)
1311 {
1312 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1313 }
1314 else if (copy->getCopyKind() == CopyStmt::FPTOUI)
1315 {
1316 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1317 }
1318 else if (copy->getCopyKind() == CopyStmt::SITOFP)
1319 {
1320 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1321 }
1322 else if (copy->getCopyKind() == CopyStmt::UITOFP)
1323 {
1324 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1325 }
1326 else if (copy->getCopyKind() == CopyStmt::TRUNC)
1327 {
1328 updateAbsValue(lhsVar, getTruncValue(rhsVar, lhsVar->getType()), node);
1329 }
1330 else if (copy->getCopyKind() == CopyStmt::FPTRUNC)
1331 {
1332 updateAbsValue(lhsVar, rhsVal.getInterval(), node);
1333 }
1334 else if (copy->getCopyKind() == CopyStmt::INTTOPTR)
1335 {
1336 //insert nullptr
1337 }
1338 else if (copy->getCopyKind() == CopyStmt::PTRTOINT)
1339 {
1341 }
1342 else if (copy->getCopyKind() == CopyStmt::BITCAST)
1343 {
1344 if (rhsVal.isAddr())
1346 }
1347 else
1348 assert(false && "undefined copy kind");
1349}
1350
1351
1352
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 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:121
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
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:43
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 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)
bool isBranchFeasible(const IntraCFGEdge *edge, AbstractState &as)
Returns true if the branch is reachable; narrows as in-place.
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)
virtual void handleGlobalNode()
Initialize abstract state for the global ICFG node and process global statements.
void handleFunction(const ICFGNode *funEntry, const CallICFGNode *caller=nullptr)
Handle a function body via worklist-driven WTO traversal starting from funEntry.
virtual bool isExtCall(const CallICFGNode *callNode)
bool isSwitchBranchFeasible(const IntraCFGEdge *edge, AbstractState &as)
Returns true if the switch branch is feasible; narrows as in-place.
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
AbstractValue loadValue(const ValVar *pointer, const ICFGNode *node)
virtual void handleExtCall(const CallICFGNode *callNode)
AddressValue getGepObjAddrs(const ValVar *pointer, IntervalValue offset)
IntervalValue getGepElementIndex(const GepStmt *gep)
virtual void joinStates(AbstractState &dst, const AbstractState &src)
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.
SVFIR * svfir
Data and helpers reachable from SparseAbstractInterpretation.
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 updateStateOnRet(const RetPE *retPE)
bool hasAbsState(const ICFGNode *node)
void updateStateOnCopy(const CopyStmt *copy)
std::deque< const FunObjVar * > collectProgEntryFuns()
Get all entry point functions (functions without callers)
void updateStateOnLoad(const LoadStmt *load)
void updateStateOnBinary(const BinaryOPStmt *binary)
Map< const ICFGNode *, AbstractState > abstractTrace
per-node trace; owned here
static AbstractInterpretation & getAEInstance()
bool isCmpBranchFeasible(const IntraCFGEdge *edge, AbstractState &as)
Returns true if the cmp-conditional branch is feasible; narrows as in-place.
virtual void updateAbsValue(const ValVar *var, const AbstractValue &val, const ICFGNode *node)
Set< const ICFGNode * > allAnalyzedNodes
void storeValue(const ValVar *pointer, const AbstractValue &val, const ICFGNode *node)
virtual void handleLoopOrRecursion(const ICFGCycleWTO *cycle, const CallICFGNode *caller=nullptr)
Handle a WTO cycle (loop or recursive function) using widening/narrowing iteration.
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
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.
const GEdgeSetTy & getInEdges() const
const SVFStmtList & getSVFStmts() const
Definition ICFGNode.h:115
ICFGEdge * getICFGEdge(const ICFGNode *src, const ICFGNode *dst, ICFGEdge::ICFGEdgeK kind)
Get a SVFG edge according to src and dst.
Definition ICFG.cpp:311
void updateCallGraph(CallGraph *callgraph)
update ICFG for indirect calls
Definition ICFG.cpp:427
FunEntryICFGNode * getFunEntryICFGNode(const FunObjVar *fun)
Add a function entry node.
Definition ICFG.cpp:242
GlobalICFGNode * getGlobalICFGNode() const
Definition ICFG.h:244
NodeID getBlkPtr() const
Definition IRGraph.h:255
void meet_with(const IntervalValue &other)
Return a intersected IntervalValue.
static BoundedInt minus_infinity()
Get minus infinity -inf.
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:246
static const OptionMap< u32_t > AESparsity
Definition Options.h:243
static const Option< bool > PStat
Definition Options.h:116
const ValVar * getRHSVar() const
const ValVar * getLHSVar() const
ICFG * getICFG() const
Definition SVFIR.h:229
const CallSiteToFunPtrMap & getIndirectCallsites() const
Add/get indirect callsites.
Definition SVFIR.h:451
const SVFVar * getSVFVar(NodeID id) const
ObjVar/GepObjVar/BaseObjVar.
Definition SVFIR.h:133
static SVFIR * getPAG(bool buildFromFile=false)
Singleton design here to make sure we only have one instance during any analysis.
Definition SVFIR.h:118
virtual void endClk()
Definition SVFStat.h:63
virtual void startClk()
Definition SVFStat.h:58
ICFGNode * getICFGNode() const
u32_t getByteSize() const
Definition SVFType.h:289
const ValVar * getRHSVar() const
const ValVar * getLHSVar() const
bool isProgEntryFunction(const FunObjVar *)
Program entry function e.g. main.
Definition SVFUtil.cpp:442
bool isExtCall(const FunObjVar *fun)
Definition SVFUtil.cpp:437
for isBitcode
Definition BasicTypes.h:70
u32_t NodeID
Definition GeneralType.h:56
signed short s16_t
Definition GeneralType.h:54
unsigned short u16_t
Definition GeneralType.h:53
WTONode< ICFG > ICFGSingletonWTO
Definition ICFGWTO.h:45
llvm::IRBuilder IRBuilder
Definition BasicTypes.h:76
signed s32_t
Definition GeneralType.h:48
unsigned u32_t
Definition GeneralType.h:47
signed long long s64_t
Definition GeneralType.h:50
WTOComponent< ICFG > ICFGWTOComp
Definition ICFGWTO.h:44