diff --git a/svf/include/AE/Core/AbstractState.h b/svf/include/AE/Core/AbstractState.h index 235a3125b4..026b3f3749 100644 --- a/svf/include/AE/Core/AbstractState.h +++ b/svf/include/AE/Core/AbstractState.h @@ -208,9 +208,15 @@ class AbstractState } /// get abstract value of variable + /// Returns a default (bottom) AbstractValue if varId is not in the map, + /// which is safe for sparse abstract states that only store needed vars. inline virtual const AbstractValue &operator[](u32_t varId) const { - return _varToAbsVal.at(varId); + auto it = _varToAbsVal.find(varId); + if (it != _varToAbsVal.end()) + return it->second; + static AbstractValue defaultVal; + return defaultVal; } /// whether the variable is in varToAddrs table diff --git a/svf/include/AE/Core/IntervalValue.h b/svf/include/AE/Core/IntervalValue.h index 2eaabfa79d..fc8ba24666 100644 --- a/svf/include/AE/Core/IntervalValue.h +++ b/svf/include/AE/Core/IntervalValue.h @@ -898,23 +898,22 @@ inline IntervalValue operator<<(const IntervalValue &lhs, const IntervalValue &r if (shift.isBottom()) return IntervalValue::bottom(); BoundedInt lb = 0; - // If the shift is greater than 32, the result is always 0 - if ((s32_t) shift.lb().getNumeral() >= 32 || shift.lb().is_infinity()) + if ((s64_t) shift.lb().getNumeral() >= 63 || shift.lb().is_infinity()) { lb = IntervalValue::minus_infinity(); } else { - lb = (1 << (s32_t) shift.lb().getNumeral()); + lb = ((s64_t)1 << (s64_t) shift.lb().getNumeral()); } BoundedInt ub = 0; - if (shift.ub().is_infinity()) + if ((s64_t) shift.ub().getNumeral() >= 63 || shift.ub().is_infinity()) { ub = IntervalValue::plus_infinity(); } else { - ub = (1 << (s32_t) shift.ub().getNumeral()); + ub = ((s64_t)1 << (s64_t) shift.ub().getNumeral()); } IntervalValue coeff(lb, ub); return lhs * coeff; diff --git a/svf/include/AE/Svfexe/AEDetector.h b/svf/include/AE/Svfexe/AEDetector.h index 425844cfe8..efbd248649 100644 --- a/svf/include/AE/Svfexe/AEDetector.h +++ b/svf/include/AE/Svfexe/AEDetector.h @@ -77,6 +77,15 @@ class AEDetector */ virtual void detect(AbstractState& as, const ICFGNode* node) = 0; + /** + * @brief Returns additional variable IDs needed by this detector at the given node. + * Used by sparse propagation to ensure the detector's needed vars are fetched. + */ + virtual Set getNeededVarsForSparse(const ICFGNode* node) + { + return {}; + } + /** * @brief Pure virtual function for handling stub external API calls. (e.g. UNSAFE_BUFACCESS) * @param call Pointer to the ext call ICFG node. @@ -177,14 +186,18 @@ class BufOverflowDetector : public AEDetector * @param as Reference to the abstract state. * @param node Pointer to the ICFG node. */ - void detect(AbstractState& as, const ICFGNode*); + void detect(AbstractState& as, const ICFGNode*) override; + /** + * @brief Returns additional variable IDs needed by the buffer overflow detector. + */ + Set getNeededVarsForSparse(const ICFGNode* node) override; /** * @brief Handles external API calls related to buffer overflow detection. * @param call Pointer to the call ICFG node. */ - void handleStubFunctions(const CallICFGNode*); + void handleStubFunctions(const CallICFGNode*) override; /** * @brief Adds an offset to a GEP object. @@ -268,7 +281,7 @@ class BufOverflowDetector : public AEDetector /** * @brief Reports all detected buffer overflow bugs. */ - void reportBug() + void reportBug() override { if (!nodeToBugInfo.empty()) { @@ -348,13 +361,18 @@ class NullptrDerefDetector : public AEDetector * @param as Reference to the abstract state. * @param node Pointer to the ICFG node. */ - void detect(AbstractState& as, const ICFGNode* node); + void detect(AbstractState& as, const ICFGNode* node) override; + + /** + * @brief Returns additional variable IDs needed by the nullptr deref detector. + */ + Set getNeededVarsForSparse(const ICFGNode* node) override; /** * @brief Handles external API calls related to nullptr dereferences. * @param call Pointer to the call ICFG node. */ - void handleStubFunctions(const CallICFGNode* call); + void handleStubFunctions(const CallICFGNode* call) override; /** * @brief Checks if an Abstract Value is uninitialized. @@ -401,7 +419,7 @@ class NullptrDerefDetector : public AEDetector /** * @brief Reports all detected nullptr dereference bugs. */ - void reportBug() + void reportBug() override { if (!nodeToBugInfo.empty()) { diff --git a/svf/include/AE/Svfexe/AbsExtAPI.h b/svf/include/AE/Svfexe/AbsExtAPI.h index 858ebd752d..da723b0151 100644 --- a/svf/include/AE/Svfexe/AbsExtAPI.h +++ b/svf/include/AE/Svfexe/AbsExtAPI.h @@ -74,6 +74,12 @@ class AbsExtAPI */ void handleExtAPI(const CallICFGNode *call); + /** + * @brief Returns additional variable IDs needed by external API handling at the given node. + * Used by sparse propagation to ensure ext API's needed vars are fetched. + */ + static Set getNeededVarsForSparse(const ICFGNode* node); + // --- Shared primitives used by string/memory handlers --- /// Get the byte size of each element for a pointer/array variable. @@ -106,7 +112,7 @@ class AbsExtAPI * @return Reference to the abstract state. * @throws Assertion if no trace exists for the node. */ - AbstractState& getAbsStateFromTrace(const ICFGNode* node); + AbstractState& getAbstractState(const ICFGNode* node); protected: SVFIR* svfir; ///< Pointer to the SVF intermediate representation. diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index cdc9288108..c34a1c805c 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -33,6 +33,7 @@ #include "AE/Core/ICFGWTO.h" #include "AE/Svfexe/AEDetector.h" #include "AE/Svfexe/PreAnalysis.h" +#include "AE/Svfexe/SparseDefUse.h" #include "AE/Svfexe/AbsExtAPI.h" #include "Util/SVFBugReport.h" #include "Util/SVFStat.h" @@ -165,23 +166,11 @@ class AbstractInterpretation Set checkpoints; // for CI check - /** - * @brief Retrieves the abstract state from the trace for a given ICFG node. - * @param node Pointer to the ICFG node. - * @return Reference to the abstract state. - * @throws Assertion if no trace exists for the node. - */ - AbstractState& getAbsStateFromTrace(const ICFGNode* node) + /// Get abstract state for an ICFG node + AbstractState& getAbstractState(const ICFGNode* node) { - if (abstractTrace.count(node) == 0) - { - assert(false && "No preAbsTrace for this node"); - abort(); - } - else - { - return abstractTrace[node]; - } + assert(abstractTrace.count(node) && "No abstract state for this node"); + return abstractTrace[node]; } private: @@ -196,6 +185,47 @@ class AbstractInterpretation */ bool mergeStatesFromPredecessors(const ICFGNode * icfgNode); + /// Get the definition ICFG node of a top-level variable (sparse mode only) + const ICFGNode* getDefICFGNode(const ValVar* var) + { + assert(Options::SparseAE() && "getDefICFGNode is only for sparse mode"); + return var->getICFGNode(); + } + + /// Get the definition ICFG nodes of an address-taken variable (sparse mode only) + const std::vector& getDefICFGNodes(const ObjVar* var) + { + assert(Options::SparseAE() && sparseDefUse && "getDefICFGNodes is only for sparse mode"); + return sparseDefUse->getDefICFGNodes(var->getId()); + } + + /// Get top-level ValVars accessed at an ICFG node (sparse mode only) + std::vector getValVars(const ICFGNode* node); + + /// Get address-taken ObjVars accessed at an ICFG node via points-to (sparse mode only) + std::vector getObjVars(const ICFGNode* node); + + /// Get all SVFVars (ValVars + ObjVars) accessed at an ICFG node (sparse mode only) + std::vector getSVFVars(const ICFGNode* node); + + /// Retrieve abstract value at the definition site of a top-level variable + AbstractValue getAbstractValue(const ValVar* var); + + /// Retrieve abstract value at the definition sites of an address-taken variable (join all defs) + AbstractValue getAbstractValue(const ObjVar* var); + + /// Retrieve abstract value at the definition site(s) of an SVF variable (dispatches to ValVar/ObjVar) + AbstractValue getAbstractValue(const SVFVar* var); + + /// Build an AbstractState from definition sites of multiple top-level variables + AbstractState getAbstractState(const std::vector& vars); + + /// Build an AbstractState from definition sites of multiple address-taken variables + AbstractState getAbstractState(const std::vector& vars); + + /// Build an AbstractState from definition sites of multiple SVF variables + AbstractState getAbstractState(const std::vector& vars); + /** * Check if execution state exist at the branch edge * @@ -240,6 +270,9 @@ class AbstractInterpretation */ bool handleICFGNode(const ICFGNode* node); + /// Sparse state propagation: build minimal state from def sites + bool sparseStatePropagate(const ICFGNode* icfgNode); + /** * Get the next nodes of a node within the same function * @@ -323,9 +356,10 @@ class AbstractInterpretation AEStat* stat; PreAnalysis* preAnalysis{nullptr}; + SparseDefUse* sparseDefUse{nullptr}; - bool hasAbsStateFromTrace(const ICFGNode* node) + bool hasAbstractState(const ICFGNode* node) { return abstractTrace.count(node) != 0; } diff --git a/svf/include/AE/Svfexe/PreAnalysis.h b/svf/include/AE/Svfexe/PreAnalysis.h index 6d446bb43f..f9074ed102 100644 --- a/svf/include/AE/Svfexe/PreAnalysis.h +++ b/svf/include/AE/Svfexe/PreAnalysis.h @@ -69,13 +69,37 @@ class PreAnalysis /// Build WTO for each function using call graph SCC void initWTO(); + /// Get points-to set for a variable from Andersen's analysis + const PointsTo& getPts(NodeID id) const; + /// Accessors for WTO data const Map& getFuncToWTO() const { return funcToWTO; } + /// Build widen sets for all WTO cycles (call after initWTO) + void buildWidenSets(); + + /// Get the set of variable IDs that need widening at a cycle head + /// Includes both top-level ValVars and address-taken ObjVars defined in the loop body + const Set& getWidenVars(const ICFGNode* cycleHead) const + { + auto it = cycleHeadToWidenVars.find(cycleHead); + if (it != cycleHeadToWidenVars.end()) + return it->second; + return emptyWidenSet; + } + private: + /// Collect all ICFG nodes in a WTO cycle (head + body, including nested cycles) + void collectCycleNodes(const ICFGCycleWTO* cycle, std::vector& nodes); + + /// Collect defined variables at an ICFG node (top-level defs + address-taken via pts) + void collectDefsAtNode(const ICFGNode* node, Set& defs); + + /// Build widen set for a single WTO cycle (recursive for nested cycles) + void buildWidenSetForCycle(const ICFGCycleWTO* cycle); SVFIR* svfir; ICFG* icfg; AndersenWaveDiff* pta; @@ -83,6 +107,8 @@ class PreAnalysis CallGraphSCC* callGraphSCC; Map funcToWTO; + Map> cycleHeadToWidenVars; + static const Set emptyWidenSet; }; } // End namespace SVF diff --git a/svf/include/AE/Svfexe/SparseDefUse.h b/svf/include/AE/Svfexe/SparseDefUse.h new file mode 100644 index 0000000000..7300df8c6c --- /dev/null +++ b/svf/include/AE/Svfexe/SparseDefUse.h @@ -0,0 +1,73 @@ +//===- SparseDefUse.h -- Sparse Def-Use Table for Abstract Interpretation-// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * SparseDefUse.h + * + * Created on: Feb 9, 2026 + * Author: Jiawei Wang + * + * Simple def-use table for address-taken objects. + * Maps each ObjVar to the ICFG nodes that store to it (via StoreStmt). + * Top-level variables don't need this table since ValVar::getICFGNode() + * gives their single definition point in SSA form. + */ + +#ifndef INCLUDE_AE_SVFEXE_SPARSEDEFUSE_H_ +#define INCLUDE_AE_SVFEXE_SPARSEDEFUSE_H_ + +#include "SVFIR/SVFIR.h" +#include "Graphs/ICFG.h" +#include "MemoryModel/PointerAnalysis.h" + +namespace SVF +{ + +class SparseDefUse +{ +public: + SparseDefUse(PointerAnalysis* pta) + : pta(pta) {} + + ~SparseDefUse() = default; + + /// Build the table by scanning all ICFG nodes for StoreStmts + void build(ICFG* icfg); + + /// Get the ICFG nodes that may define an ObjVar (via store) + const std::vector& getDefICFGNodes(NodeID objId) const + { + auto it = objToDefNodes.find(objId); + if (it != objToDefNodes.end()) + return it->second; + return emptyVec; + } + +private: + PointerAnalysis* pta; + Map> objToDefNodes; + static const std::vector emptyVec; +}; + +} // End namespace SVF + +#endif /* INCLUDE_AE_SVFEXE_SPARSEDEFUSE_H_ */ diff --git a/svf/include/Util/Options.h b/svf/include/Util/Options.h index 0fa17910d4..8bedf56996 100644 --- a/svf/include/Util/Options.h +++ b/svf/include/Util/Options.h @@ -243,6 +243,8 @@ class Options static const Option WidenDelay; /// recursion handling mode, Default: TOP static const OptionMap HandleRecur; + /// Enable sparse state propagation using Use-Def table + static const Option SparseAE; /// the max time consumptions (seconds). Default: 4 hours 14400s static const Option Timeout; /// bug info output file, Default: output.db diff --git a/svf/lib/AE/Svfexe/AEDetector.cpp b/svf/lib/AE/Svfexe/AEDetector.cpp index 186ed80fe0..42348da1b7 100644 --- a/svf/lib/AE/Svfexe/AEDetector.cpp +++ b/svf/lib/AE/Svfexe/AEDetector.cpp @@ -123,7 +123,7 @@ void BufOverflowDetector::handleStubFunctions(const SVF::CallICFGNode* callNode) if (callNode->arg_size() < 2) return; AbstractState& as = - AbstractInterpretation::getAEInstance().getAbsStateFromTrace( + AbstractInterpretation::getAEInstance().getAbstractState( callNode); u32_t size_id = callNode->getArgument(1)->getId(); IntervalValue val = as[size_id].getInterval(); @@ -152,7 +152,7 @@ void BufOverflowDetector::handleStubFunctions(const SVF::CallICFGNode* callNode) // void UNSAFE_BUFACCESS(void* data, int size); AbstractInterpretation::getAEInstance().checkpoints.erase(callNode); if (callNode->arg_size() < 2) return; - AbstractState&as = AbstractInterpretation::getAEInstance().getAbsStateFromTrace(callNode); + AbstractState&as = AbstractInterpretation::getAEInstance().getAbstractState(callNode); u32_t size_id = callNode->getArgument(1)->getId(); IntervalValue val = as[size_id].getInterval(); if (val.isBottom()) @@ -591,7 +591,7 @@ void NullptrDerefDetector::handleStubFunctions(const CallICFGNode* callNode) AbstractInterpretation::getAEInstance().checkpoints.erase(callNode); if (callNode->arg_size() < 1) return; - AbstractState& as = AbstractInterpretation::getAEInstance().getAbsStateFromTrace(callNode); + AbstractState& as = AbstractInterpretation::getAEInstance().getAbstractState(callNode); const SVFVar* arg0Val = callNode->getArgument(0); // opt may directly dereference a null pointer and call UNSAFE_LOAD(null) @@ -614,7 +614,7 @@ void NullptrDerefDetector::handleStubFunctions(const CallICFGNode* callNode) // void SAFE_LOAD(void* ptr); AbstractInterpretation::getAEInstance().checkpoints.erase(callNode); if (callNode->arg_size() < 1) return; - AbstractState&as = AbstractInterpretation::getAEInstance().getAbsStateFromTrace(callNode); + AbstractState&as = AbstractInterpretation::getAEInstance().getAbstractState(callNode); const SVFVar* arg0Val = callNode->getArgument(0); // opt may directly dereference a null pointer and call UNSAFE_LOAD(null)ols bool isSafe = canSafelyDerefPtr(as, arg0Val) && arg0Val->getId() != 0; @@ -715,4 +715,57 @@ bool NullptrDerefDetector::canSafelyDerefPtr(AbstractState& as, const SVFVar* va return true; +} + +Set BufOverflowDetector::getNeededVarsForSparse(const ICFGNode* node) +{ + Set vars; + if (!SVFUtil::isa(node)) + { + // detect() checks GepStmt: as[lhs] and as[rhs] + for (const SVFStmt* stmt : node->getSVFStmts()) + { + if (const GepStmt* gep = SVFUtil::dyn_cast(stmt)) + { + vars.insert(gep->getLHSVarID()); + vars.insert(gep->getRHSVarID()); + } + } + } + else + { + // detect() and handleStubFunctions() check call args + const CallICFGNode* callNode = SVFUtil::cast(node); + for (u32_t i = 0; i < callNode->arg_size(); ++i) + vars.insert(callNode->getArgument(i)->getId()); + } + return vars; +} + +Set NullptrDerefDetector::getNeededVarsForSparse(const ICFGNode* node) +{ + Set vars; + if (SVFUtil::isa(node)) + { + // detectExtAPI() and handleStubFunctions() check call args + const CallICFGNode* callNode = SVFUtil::cast(node); + for (u32_t i = 0; i < callNode->arg_size(); ++i) + vars.insert(callNode->getArgument(i)->getId()); + } + else + { + // detect() checks GepStmt rhs and LoadStmt lhs via canSafelyDerefPtr + for (const auto& stmt : node->getSVFStmts()) + { + if (const GepStmt* gep = SVFUtil::dyn_cast(stmt)) + { + vars.insert(gep->getRHSVarID()); + } + else if (const LoadStmt* load = SVFUtil::dyn_cast(stmt)) + { + vars.insert(load->getLHSVarID()); + } + } + } + return vars; } \ No newline at end of file diff --git a/svf/lib/AE/Svfexe/AbsExtAPI.cpp b/svf/lib/AE/Svfexe/AbsExtAPI.cpp index d9aabf40e8..6f49683a85 100644 --- a/svf/lib/AE/Svfexe/AbsExtAPI.cpp +++ b/svf/lib/AE/Svfexe/AbsExtAPI.cpp @@ -44,7 +44,7 @@ void AbsExtAPI::initExtFunMap() #define SSE_FUNC_PROCESS(LLVM_NAME ,FUNC_NAME) \ auto sse_##FUNC_NAME = [this](const CallICFGNode *callNode) { \ /* run real ext function */ \ - AbstractState& as = getAbsStateFromTrace(callNode); \ + AbstractState& as = getAbstractState(callNode); \ u32_t rhs_id = callNode->getArgument(0)->getId(); \ if (!as.inVarToValTable(rhs_id)) return; \ u32_t rhs = as[rhs_id].getInterval().lb().getIntNumeral(); \ @@ -78,7 +78,7 @@ void AbsExtAPI::initExtFunMap() { AbstractInterpretation::getAEInstance().checkpoints.erase(callNode); u32_t arg0 = callNode->getArgument(0)->getId(); - AbstractState&as = getAbsStateFromTrace(callNode); + AbstractState&as = getAbstractState(callNode); if (as[arg0].getInterval().equals(IntervalValue(1, 1))) { SVFUtil::errs() << SVFUtil::sucMsg("The assertion is successfully verified!!\n"); @@ -96,7 +96,7 @@ void AbsExtAPI::initExtFunMap() { u32_t arg0 = callNode->getArgument(0)->getId(); u32_t arg1 = callNode->getArgument(1)->getId(); - AbstractState&as = getAbsStateFromTrace(callNode); + AbstractState&as = getAbstractState(callNode); if (as[arg0].getInterval().equals(as[arg1].getInterval())) { SVFUtil::errs() << SVFUtil::sucMsg("The assertion is successfully verified!!\n"); @@ -113,7 +113,7 @@ void AbsExtAPI::initExtFunMap() auto svf_print = [&](const CallICFGNode* callNode) { if (callNode->arg_size() < 2) return; - AbstractState&as = getAbsStateFromTrace(callNode); + AbstractState&as = getAbstractState(callNode); u32_t num_id = callNode->getArgument(0)->getId(); std::string text = strRead(as, callNode->getArgument(1)); assert(as.inVarToValTable(num_id) && "print() should pass integer"); @@ -127,7 +127,7 @@ void AbsExtAPI::initExtFunMap() auto svf_set_value = [&](const CallICFGNode* callNode) { if (callNode->arg_size() < 2) return; - AbstractState&as = getAbsStateFromTrace(callNode); + AbstractState&as = getAbstractState(callNode); AbstractValue& num = as[callNode->getArgument(0)->getId()]; AbstractValue& lb = as[callNode->getArgument(1)->getId()]; AbstractValue& ub = as[callNode->getArgument(2)->getId()]; @@ -150,7 +150,7 @@ void AbsExtAPI::initExtFunMap() auto sse_scanf = [&](const CallICFGNode* callNode) { - AbstractState& as = getAbsStateFromTrace(callNode); + AbstractState& as = getAbstractState(callNode); //scanf("%d", &data); if (callNode->arg_size() < 2) return; @@ -174,7 +174,7 @@ void AbsExtAPI::initExtFunMap() { //fscanf(stdin, "%d", &data); if (callNode->arg_size() < 3) return; - AbstractState& as = getAbsStateFromTrace(callNode); + AbstractState& as = getAbstractState(callNode); u32_t dst_id = callNode->getArgument(2)->getId(); if (!as.inVarToAddrsTable(dst_id)) { @@ -203,7 +203,7 @@ void AbsExtAPI::initExtFunMap() auto sse_fread = [&](const CallICFGNode *callNode) { if (callNode->arg_size() < 3) return; - AbstractState&as = getAbsStateFromTrace(callNode); + AbstractState&as = getAbstractState(callNode); u32_t block_count_id = callNode->getArgument(2)->getId(); u32_t block_size_id = callNode->getArgument(1)->getId(); IntervalValue block_count = as[block_count_id].getInterval(); @@ -220,7 +220,7 @@ void AbsExtAPI::initExtFunMap() auto sse_snprintf = [&](const CallICFGNode *callNode) { if (callNode->arg_size() < 2) return; - AbstractState&as = getAbsStateFromTrace(callNode); + AbstractState&as = getAbstractState(callNode); u32_t size_id = callNode->getArgument(1)->getId(); u32_t dst_id = callNode->getArgument(0)->getId(); // get elem size of arg2 @@ -261,7 +261,7 @@ void AbsExtAPI::initExtFunMap() // itoa(num, ch, 10); // num: int, ch: char*, 10 is decimal if (callNode->arg_size() < 3) return; - AbstractState&as = getAbsStateFromTrace(callNode); + AbstractState&as = getAbstractState(callNode); u32_t num_id = callNode->getArgument(0)->getId(); u32_t num = (u32_t) as[num_id].getInterval().getNumeral(); @@ -273,7 +273,7 @@ void AbsExtAPI::initExtFunMap() auto sse_strlen = [&](const CallICFGNode *callNode) { if (callNode->arg_size() < 1) return; - AbstractState& as = getAbsStateFromTrace(callNode); + AbstractState& as = getAbstractState(callNode); u32_t lhsId = callNode->getRetICFGNode()->getActualRet()->getId(); // strlen/wcslen return the number of characters (not bytes). // getStrlen returns byte-scaled length (len * elemSize) for use @@ -293,7 +293,7 @@ void AbsExtAPI::initExtFunMap() { // recv(sockfd, buf, len, flags); if (callNode->arg_size() < 4) return; - AbstractState&as = getAbsStateFromTrace(callNode); + AbstractState&as = getAbstractState(callNode); u32_t len_id = callNode->getArgument(2)->getId(); IntervalValue len = as[len_id].getInterval() - IntervalValue(1); u32_t lhsId = callNode->getRetICFGNode()->getActualRet()->getId(); @@ -305,7 +305,7 @@ void AbsExtAPI::initExtFunMap() auto sse_free = [&](const CallICFGNode *callNode) { if (callNode->arg_size() < 1) return; - AbstractState& as = getAbsStateFromTrace(callNode); + AbstractState& as = getAbstractState(callNode); const u32_t freePtr = callNode->getArgument(0)->getId(); for (auto addr: as[freePtr].getAddrs()) { @@ -335,7 +335,7 @@ void AbsExtAPI::initExtFunMap() } }; -AbstractState& AbsExtAPI::getAbsStateFromTrace(const SVF::ICFGNode* node) +AbstractState& AbsExtAPI::getAbstractState(const SVF::ICFGNode* node) { if (abstractTrace.count(node) == 0) { @@ -380,7 +380,7 @@ std::string AbsExtAPI::strRead(AbstractState& as, const SVFVar* rhs) void AbsExtAPI::handleExtAPI(const CallICFGNode *call) { - AbstractState& as = getAbsStateFromTrace(call); + AbstractState& as = getAbstractState(call); const FunObjVar *fun = call->getCalledFunction(); assert(fun && "FunObjVar* is nullptr"); ExtAPIType extType = UNCLASSIFIED; @@ -556,7 +556,7 @@ IntervalValue AbsExtAPI::getStrlen(AbstractState& as, const SVF::SVFVar *strValu /// Covers: strcpy, __strcpy_chk, stpcpy, wcscpy, __wcscpy_chk void AbsExtAPI::handleStrcpy(const CallICFGNode *call) { - AbstractState& as = getAbsStateFromTrace(call); + AbstractState& as = getAbstractState(call); const SVFVar* dst = call->getArgument(0); const SVFVar* src = call->getArgument(1); IntervalValue srcLen = getStrlen(as, src); @@ -569,7 +569,7 @@ void AbsExtAPI::handleStrcpy(const CallICFGNode *call) /// Covers: strcat, __strcat_chk, wcscat, __wcscat_chk void AbsExtAPI::handleStrcat(const CallICFGNode *call) { - AbstractState& as = getAbsStateFromTrace(call); + AbstractState& as = getAbstractState(call); const SVFVar* dst = call->getArgument(0); const SVFVar* src = call->getArgument(1); IntervalValue dstLen = getStrlen(as, dst); @@ -582,7 +582,7 @@ void AbsExtAPI::handleStrcat(const CallICFGNode *call) /// Covers: strncat, __strncat_chk, wcsncat, __wcsncat_chk void AbsExtAPI::handleStrncat(const CallICFGNode *call) { - AbstractState& as = getAbsStateFromTrace(call); + AbstractState& as = getAbstractState(call); const SVFVar* dst = call->getArgument(0); const SVFVar* src = call->getArgument(1); IntervalValue n = as[call->getArgument(2)->getId()].getInterval(); @@ -753,4 +753,16 @@ IntervalValue AbsExtAPI::getRangeLimitFromType(const SVFType* type) return IntervalValue::top(); // other types, return top interval } +} + +Set AbsExtAPI::getNeededVarsForSparse(const ICFGNode* node) +{ + Set vars; + if (const CallICFGNode* callNode = SVFUtil::dyn_cast(node)) + { + // External API handlers access call arguments via as[argId] + for (u32_t i = 0; i < callNode->arg_size(); ++i) + vars.insert(callNode->getArgument(i)->getId()); + } + return vars; } \ No newline at end of file diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index ca80dea97e..e37a4c7d78 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -54,11 +54,20 @@ void AbstractInterpretation::runOnModule(ICFG *_icfg) icfg->updateCallGraph(callGraph); preAnalysis->initWTO(); + // Build sparse def-use table and widen sets if sparse mode is enabled + if (Options::SparseAE()) + { + sparseDefUse = new SparseDefUse(preAnalysis->getPointerAnalysis()); + sparseDefUse->build(icfg); + preAnalysis->buildWidenSets(); + } + /// collect checkpoint collectCheckPoint(); analyse(); checkPointAllSet(); + stat->endClk(); stat->finializeStat(); if (Options::PStat()) @@ -76,6 +85,7 @@ AbstractInterpretation::~AbstractInterpretation() { delete stat; delete preAnalysis; + delete sparseDefUse; } /// Collect entry point functions for analysis. @@ -221,7 +231,7 @@ bool AbstractInterpretation::mergeStatesFromPredecessors(const ICFGNode * icfgNo // Only include return edge if the corresponding callsite was processed // (skipped recursive callsites in WIDEN_ONLY/WIDEN_NARROW won't have state) const RetICFGNode* retNode = SVFUtil::dyn_cast(icfgNode); - if (hasAbsStateFromTrace(retNode->getCallICFGNode())) + if (hasAbstractState(retNode->getCallICFGNode())) { workList.push_back(abstractTrace[retCfgEdge->getSrcNode()]); } @@ -566,10 +576,316 @@ void AbstractInterpretation::handleSingletonWTO(const ICFGSingletonWTO *icfgSing handleCallSite(callnode); } for (auto& detector: detectors) - detector->detect(getAbsStateFromTrace(node), node); + detector->detect(getAbstractState(node), node); stat->countStateSize(); } +/// Get top-level ValVars accessed at an ICFG node (sparse mode only). +/// Collects all ValVar LHS/RHS from SVFStmts, call arguments, and branch conditions. +/// Helper: insert a ValVar by NodeID into result if not already seen. +static void collectValVar(SVFIR* svfir, NodeID id, Set& seen, + std::vector& result) +{ + if (seen.insert(id).second) + { + const SVFVar* var = svfir->getGNode(id); + if (const ValVar* vv = SVFUtil::dyn_cast(var)) + result.push_back(vv); + } +} + +std::vector AbstractInterpretation::getValVars(const ICFGNode* node) +{ + assert(Options::SparseAE() && "getValVars is only for sparse mode"); + Set seen; + std::vector result; + + for (const SVFStmt* stmt : node->getSVFStmts()) + { + if (const StoreStmt* store = SVFUtil::dyn_cast(stmt)) + { + collectValVar(svfir, store->getRHSVarID(), seen, result); + collectValVar(svfir, store->getLHSVarID(), seen, result); + } + else if (const LoadStmt* load = SVFUtil::dyn_cast(stmt)) + { + collectValVar(svfir, load->getRHSVarID(), seen, result); + collectValVar(svfir, load->getLHSVarID(), seen, result); + } + else if (const CopyStmt* copy = SVFUtil::dyn_cast(stmt)) + { + collectValVar(svfir, copy->getRHSVarID(), seen, result); + collectValVar(svfir, copy->getLHSVarID(), seen, result); + } + else if (const AddrStmt* addr = SVFUtil::dyn_cast(stmt)) + { + collectValVar(svfir, addr->getLHSVarID(), seen, result); + } + else if (const GepStmt* gep = SVFUtil::dyn_cast(stmt)) + { + collectValVar(svfir, gep->getRHSVarID(), seen, result); + collectValVar(svfir, gep->getLHSVarID(), seen, result); + } + else if (const PhiStmt* phi = SVFUtil::dyn_cast(stmt)) + { + collectValVar(svfir, phi->getResID(), seen, result); + for (u32_t i = 0; i < phi->getOpVarNum(); ++i) + collectValVar(svfir, phi->getOpVarID(i), seen, result); + } + else if (const SelectStmt* select = SVFUtil::dyn_cast(stmt)) + { + collectValVar(svfir, select->getResID(), seen, result); + for (u32_t i = 0; i < select->getOpVarNum(); ++i) + collectValVar(svfir, select->getOpVarID(i), seen, result); + } + else if (const BinaryOPStmt* binary = SVFUtil::dyn_cast(stmt)) + { + collectValVar(svfir, binary->getResID(), seen, result); + for (u32_t i = 0; i < binary->getOpVarNum(); ++i) + collectValVar(svfir, binary->getOpVarID(i), seen, result); + } + else if (const CmpStmt* cmp = SVFUtil::dyn_cast(stmt)) + { + collectValVar(svfir, cmp->getResID(), seen, result); + for (u32_t i = 0; i < cmp->getOpVarNum(); ++i) + collectValVar(svfir, cmp->getOpVarID(i), seen, result); + } + else if (const CallPE* callPE = SVFUtil::dyn_cast(stmt)) + { + collectValVar(svfir, callPE->getRHSVarID(), seen, result); + collectValVar(svfir, callPE->getLHSVarID(), seen, result); + } + else if (const RetPE* retPE = SVFUtil::dyn_cast(stmt)) + { + collectValVar(svfir, retPE->getRHSVarID(), seen, result); + collectValVar(svfir, retPE->getLHSVarID(), seen, result); + } + else if (const UnaryOPStmt* unary = SVFUtil::dyn_cast(stmt)) + { + collectValVar(svfir, unary->getResID(), seen, result); + collectValVar(svfir, unary->getOpVarID(), seen, result); + } + } + + // CallICFGNode arguments (needed for external API handling like svf_assert) + if (const CallICFGNode* callNode = SVFUtil::dyn_cast(node)) + { + for (u32_t i = 0; i < callNode->arg_size(); ++i) + collectValVar(svfir, callNode->getArgument(i)->getId(), seen, result); + } + + // Condition variables on outgoing edges + for (const auto& edge : node->getOutEdges()) + { + if (const IntraCFGEdge* intraCfgEdge = SVFUtil::dyn_cast(edge)) + { + if (const SVFVar* condVar = intraCfgEdge->getCondition()) + collectValVar(svfir, condVar->getId(), seen, result); + } + } + + return result; +} + +/// Get address-taken ObjVars accessed at an ICFG node (sparse mode only). +/// Resolves points-to sets of all ValVars at this node via Andersen's analysis. +std::vector AbstractInterpretation::getObjVars(const ICFGNode* node) +{ + assert(Options::SparseAE() && "getObjVars is only for sparse mode"); + std::vector valVars = getValVars(node); + Set seen; + std::vector result; + + for (const ValVar* vv : valVars) + { + const PointsTo& pts = preAnalysis->getPts(vv->getId()); + for (NodeID objId : pts) + { + if (seen.insert(objId).second) + { + const SVFVar* var = svfir->getGNode(objId); + if (const ObjVar* ov = SVFUtil::dyn_cast(var)) + result.push_back(ov); + } + } + } + + return result; +} + +/// Get all SVFVars (ValVars + ObjVars) accessed at an ICFG node (sparse mode only). +std::vector AbstractInterpretation::getSVFVars(const ICFGNode* node) +{ + assert(Options::SparseAE() && "getSVFVars is only for sparse mode"); + std::vector result; + + for (const ValVar* vv : getValVars(node)) + result.push_back(vv); + for (const ObjVar* ov : getObjVars(node)) + result.push_back(ov); + + return result; +} + +/// Retrieve abstract value at the definition site of a top-level variable. +/// ValVars have a single SSA def site (their ICFGNode). Constants have null ICFGNode +/// and are fetched from the global node. +AbstractValue AbstractInterpretation::getAbstractValue(const ValVar* var) +{ + NodeID varId = var->getId(); + const ICFGNode* defNode = var->getICFGNode(); + // Constants have null ICFGNode; fetch from global state + if (!defNode) + defNode = icfg->getGlobalICFGNode(); + if (defNode && abstractTrace.count(defNode)) + { + const AbstractState& defState = abstractTrace[defNode]; + if (defState.inVarToValTable(varId) || defState.inVarToAddrsTable(varId)) + return defState[varId]; + } + return AbstractValue(); +} + +/// Retrieve abstract value at the definition sites of an address-taken variable. +/// ObjVars may be defined at multiple StoreStmt sites (flow-insensitive); values are joined. +/// Also checks the global node for objects initialized by AddrStmt. +AbstractValue AbstractInterpretation::getAbstractValue(const ObjVar* var) +{ + assert(Options::SparseAE() && sparseDefUse && "getAbstractValue(ObjVar) requires sparse mode"); + NodeID objId = var->getId(); + const auto& objDefNodes = sparseDefUse->getDefICFGNodes(objId); + const ICFGNode* globalNode = icfg->getGlobalICFGNode(); + + bool firstDef = true; + AbstractValue joinedVal; + + for (const ICFGNode* defNode : objDefNodes) + { + if (abstractTrace.count(defNode)) + { + const AbstractState& defState = abstractTrace[defNode]; + if (defState.inAddrToValTable(objId) || defState.inAddrToAddrsTable(objId)) + { + const AbstractValue& objVal = defState.getLocToVal().at(objId); + if (firstDef) + { + joinedVal = objVal; + firstDef = false; + } + else + { + joinedVal.join_with(objVal); + } + } + } + } + + // Also check the global node (objects initialized by AddrStmt) + if (abstractTrace.count(globalNode)) + { + const AbstractState& globalState = abstractTrace[globalNode]; + if (globalState.inAddrToValTable(objId) || globalState.inAddrToAddrsTable(objId)) + { + const AbstractValue& objVal = globalState.getLocToVal().at(objId); + if (firstDef) + { + joinedVal = objVal; + firstDef = false; + } + else + { + joinedVal.join_with(objVal); + } + } + } + + return joinedVal; +} + +/// Retrieve abstract value at the definition site(s) of an SVF variable. +/// Dispatches to ValVar or ObjVar overload. +AbstractValue AbstractInterpretation::getAbstractValue(const SVFVar* var) +{ + if (const ValVar* vv = SVFUtil::dyn_cast(var)) + return getAbstractValue(vv); + else if (const ObjVar* ov = SVFUtil::dyn_cast(var)) + return getAbstractValue(ov); + return AbstractValue(); +} + +/// Build an AbstractState from definition sites of multiple top-level variables. +AbstractState AbstractInterpretation::getAbstractState(const std::vector& vars) +{ + AbstractState state; + for (const ValVar* vv : vars) + { + AbstractValue val = getAbstractValue(vv); + if (val.isInterval() || val.isAddr()) + state[vv->getId()] = val; + } + return state; +} + +/// Build an AbstractState from definition sites of multiple address-taken variables. +AbstractState AbstractInterpretation::getAbstractState(const std::vector& vars) +{ + AbstractState state; + for (const ObjVar* ov : vars) + { + AbstractValue val = getAbstractValue(ov); + if (val.isInterval() || val.isAddr()) + state.store(AbstractState::getVirtualMemAddress(ov->getId()), val); + } + return state; +} + +/// Build an AbstractState from definition sites of multiple SVF variables (ValVars + ObjVars). +AbstractState AbstractInterpretation::getAbstractState(const std::vector& vars) +{ + // Separate into ValVars and ObjVars, then merge the two states + std::vector valVars; + std::vector objVars; + for (const SVFVar* var : vars) + { + if (const ValVar* vv = SVFUtil::dyn_cast(var)) + valVars.push_back(vv); + else if (const ObjVar* ov = SVFUtil::dyn_cast(var)) + objVars.push_back(ov); + } + AbstractState state = getAbstractState(valVars); + AbstractState objState = getAbstractState(objVars); + state.joinWith(objState); + return state; +} + +/// Sparse state propagation: build a minimal state from def sites for an ICFG node. +/// Checks reachability (at least one predecessor with state), uses getAbstractState +/// to fetch values from definition sites, writes to abstractTrace[node]. +/// Returns true if propagation succeeded (node is reachable), false otherwise. +bool AbstractInterpretation::sparseStatePropagate(const ICFGNode* node) +{ + // Check reachability: at least one predecessor must have state + bool reachable = false; + for (auto& edge : node->getInEdges()) + { + if (abstractTrace.find(edge->getSrcNode()) != abstractTrace.end()) + { reachable = true; break; } + } + if (!reachable) + return false; + + // Build state from ValVar and ObjVar def sites + std::vector valVars = getValVars(node); + std::vector objVars = getObjVars(node); + + AbstractState sparseState = getAbstractState(valVars); + AbstractState objState = getAbstractState(objVars); + sparseState.joinWith(objState); + + abstractTrace[node] = sparseState; + return true; +} + /** * Handle an ICFG node by merging states from predecessors and processing statements * Returns true if the abstract state has changed, false if fixpoint reached or infeasible @@ -578,33 +894,39 @@ bool AbstractInterpretation::handleICFGNode(const ICFGNode* node) { // Store the previous state for fixpoint detection AbstractState prevState; - bool hadPrevState = hasAbsStateFromTrace(node); + bool hadPrevState = hasAbstractState(node); if (hadPrevState) prevState = abstractTrace[node]; // For function entry nodes, initialize state from predecessors or global bool isFunEntry = SVFUtil::isa(node); + + // Merge states from predecessors (dense) or sparse propagation + bool mergeSuccess = false; + if (Options::SparseAE() && sparseDefUse) + { + mergeSuccess = sparseStatePropagate(node); + } + else + { + mergeSuccess = mergeStatesFromPredecessors(node); + } + if (isFunEntry) { - // Try to merge from predecessors first (handles call edges) - if (!mergeStatesFromPredecessors(node)) + if (!mergeSuccess) { // No predecessors with state - inherit from global node const ICFGNode* globalNode = icfg->getGlobalICFGNode(); - if (hasAbsStateFromTrace(globalNode)) - { + if (hasAbstractState(globalNode)) abstractTrace[node] = abstractTrace[globalNode]; - } else - { abstractTrace[node] = AbstractState(); - } } } else { - // Merge states from predecessors - if (!mergeStatesFromPredecessors(node)) + if (!mergeSuccess) return false; } @@ -625,7 +947,7 @@ bool AbstractInterpretation::handleICFGNode(const ICFGNode* node) // Run detectors for (auto& detector: detectors) - detector->detect(getAbsStateFromTrace(node), node); + detector->detect(getAbstractState(node), node); stat->countStateSize(); // Track this node as analyzed (for coverage statistics across all entry points) @@ -792,7 +1114,7 @@ bool AbstractInterpretation::isRecursiveFun(const FunObjVar* fun) /// Handle recursive call in TOP mode: set all stores and return value to TOP void AbstractInterpretation::handleRecursiveCall(const CallICFGNode *callNode) { - AbstractState& as = getAbsStateFromTrace(callNode); + AbstractState& as = getAbstractState(callNode); setTopToObjInRecursion(callNode); const RetICFGNode *retNode = callNode->getRetICFGNode(); if (retNode->getSVFStmts().size() > 0) @@ -831,10 +1153,10 @@ const FunObjVar* AbstractInterpretation::getCallee(const CallICFGNode* callNode) return nullptr; NodeID call_id = it->second; - if (!hasAbsStateFromTrace(callNode)) + if (!hasAbstractState(callNode)) return nullptr; - AbstractState& as = getAbsStateFromTrace(callNode); + AbstractState& as = getAbstractState(callNode); if (!as.inVarToAddrsTable(call_id)) return nullptr; @@ -899,7 +1221,7 @@ bool AbstractInterpretation::shouldApplyNarrowing(const FunObjVar* fun) /// possible indirect call targets. void AbstractInterpretation::handleFunCall(const CallICFGNode *callNode) { - AbstractState& as = getAbsStateFromTrace(callNode); + AbstractState& as = getAbstractState(callNode); abstractTrace[callNode] = as; // Skip recursive callsites (within SCC); entry calls are not skipped @@ -997,11 +1319,40 @@ void AbstractInterpretation::handleLoopOrRecursion(const ICFGCycleWTO* cycle, co { // Get the abstract state before processing the cycle head AbstractState prev_head_state; - if (hasAbsStateFromTrace(cycle_head)) + if (hasAbstractState(cycle_head)) prev_head_state = abstractTrace[cycle_head]; // Process the cycle head node handleICFGNode(cycle_head); + + // In sparse mode, the cycle head state only contains variables directly + // used at the head node. Fetch all variables defined in the loop body + // (the "widen set") so they participate in widening/narrowing. + if (Options::SparseAE() && preAnalysis) + { + const Set& widenVars = preAnalysis->getWidenVars(cycle_head); + if (!widenVars.empty()) + { + AbstractState& headState = abstractTrace[cycle_head]; + for (NodeID varId : widenVars) + { + const SVFVar* var = svfir->getGNode(varId); + if (const ObjVar* ov = SVFUtil::dyn_cast(var)) + { + AbstractValue val = getAbstractValue(ov); + if (val.isInterval() || val.isAddr()) + headState.store(AbstractState::getVirtualMemAddress(ov->getId()), val); + } + else if (const ValVar* vv = SVFUtil::dyn_cast(var)) + { + AbstractValue val = getAbstractValue(vv); + if (val.isInterval() || val.isAddr()) + headState[vv->getId()] = val; + } + } + } + } + AbstractState cur_head_state = abstractTrace[cycle_head]; // Start widening or narrowing if cur_iter >= widen delay threshold @@ -1110,14 +1461,14 @@ void AbstractInterpretation::handleSVFStatement(const SVFStmt *stmt) else assert(false && "implement this part"); // NullPtr is index 0, it should not be changed - assert(!getAbsStateFromTrace(stmt->getICFGNode())[IRGraph::NullPtr].isInterval() && - !getAbsStateFromTrace(stmt->getICFGNode())[IRGraph::NullPtr].isAddr()); + assert(!getAbstractState(stmt->getICFGNode())[IRGraph::NullPtr].isInterval() && + !getAbstractState(stmt->getICFGNode())[IRGraph::NullPtr].isAddr()); } /// Set all store values in a recursive function to TOP (used in TOP mode) void AbstractInterpretation::setTopToObjInRecursion(const CallICFGNode *callNode) { - AbstractState& as = getAbsStateFromTrace(callNode); + AbstractState& as = getAbstractState(callNode); const RetICFGNode *retNode = callNode->getRetICFGNode(); if (retNode->getSVFStmts().size() > 0) { @@ -1361,7 +1712,7 @@ void AbstractInterpretation::checkPointAllSet() void AbstractInterpretation::updateStateOnGep(const GepStmt *gep) { - AbstractState& as = getAbsStateFromTrace(gep->getICFGNode()); + AbstractState& as = getAbstractState(gep->getICFGNode()); u32_t rhs = gep->getRHSVarID(); u32_t lhs = gep->getLHSVarID(); IntervalValue offsetPair = as.getElementIndex(gep); @@ -1377,7 +1728,7 @@ void AbstractInterpretation::updateStateOnGep(const GepStmt *gep) void AbstractInterpretation::updateStateOnSelect(const SelectStmt *select) { - AbstractState& as = getAbsStateFromTrace(select->getICFGNode()); + AbstractState& as = getAbstractState(select->getICFGNode()); u32_t res = select->getResID(); u32_t tval = select->getTrueValue()->getId(); u32_t fval = select->getFalseValue()->getId(); @@ -1396,17 +1747,17 @@ void AbstractInterpretation::updateStateOnSelect(const SelectStmt *select) void AbstractInterpretation::updateStateOnPhi(const PhiStmt *phi) { const ICFGNode* icfgNode = phi->getICFGNode(); - AbstractState& as = getAbsStateFromTrace(icfgNode); + AbstractState& as = getAbstractState(icfgNode); u32_t res = phi->getResID(); AbstractValue rhs; for (u32_t i = 0; i < phi->getOpVarNum(); i++) { NodeID curId = phi->getOpVarID(i); const ICFGNode* opICFGNode = phi->getOpICFGNode(i); - if (hasAbsStateFromTrace(opICFGNode)) + if (hasAbstractState(opICFGNode)) { AbstractState tmpEs = abstractTrace[opICFGNode]; - AbstractState& opAs = getAbsStateFromTrace(opICFGNode); + AbstractState& opAs = getAbstractState(opICFGNode); const ICFGEdge* edge = icfg->getICFGEdge(opICFGNode, icfgNode, ICFGEdge::IntraCF); // if IntraEdge, check the condition, if it is feasible, join the value // if IntraEdge but not conditional edge, join the value @@ -1434,7 +1785,7 @@ void AbstractInterpretation::updateStateOnPhi(const PhiStmt *phi) void AbstractInterpretation::updateStateOnCall(const CallPE *callPE) { - AbstractState& as = getAbsStateFromTrace(callPE->getICFGNode()); + AbstractState& as = getAbstractState(callPE->getICFGNode()); NodeID lhs = callPE->getLHSVarID(); NodeID rhs = callPE->getRHSVarID(); as[lhs] = as[rhs]; @@ -1442,7 +1793,7 @@ void AbstractInterpretation::updateStateOnCall(const CallPE *callPE) void AbstractInterpretation::updateStateOnRet(const RetPE *retPE) { - AbstractState& as = getAbsStateFromTrace(retPE->getICFGNode()); + AbstractState& as = getAbstractState(retPE->getICFGNode()); NodeID lhs = retPE->getLHSVarID(); NodeID rhs = retPE->getRHSVarID(); as[lhs] = as[rhs]; @@ -1451,7 +1802,7 @@ void AbstractInterpretation::updateStateOnRet(const RetPE *retPE) void AbstractInterpretation::updateStateOnAddr(const AddrStmt *addr) { - AbstractState& as = getAbsStateFromTrace(addr->getICFGNode()); + AbstractState& as = getAbstractState(addr->getICFGNode()); as.initObjVar(SVFUtil::cast(addr->getRHSVar())); if (addr->getRHSVar()->getType()->getKind() == SVFType::SVFIntegerTy) as[addr->getRHSVarID()].getInterval().meet_with(utils->getRangeLimitFromType(addr->getRHSVar()->getType())); @@ -1465,7 +1816,7 @@ void AbstractInterpretation::updateStateOnBinary(const BinaryOPStmt *binary) /// You are only required to handle integer predicates, including Add, FAdd, Sub, FSub, Mul, FMul, SDiv, FDiv, UDiv, /// SRem, FRem, URem, Xor, And, Or, AShr, Shl, LShr const ICFGNode* node = binary->getICFGNode(); - AbstractState& as = getAbsStateFromTrace(node); + AbstractState& as = getAbstractState(node); u32_t op0 = binary->getOpVarID(0); u32_t op1 = binary->getOpVarID(1); u32_t res = binary->getResID(); @@ -1523,7 +1874,7 @@ void AbstractInterpretation::updateStateOnBinary(const BinaryOPStmt *binary) void AbstractInterpretation::updateStateOnCmp(const CmpStmt *cmp) { - AbstractState& as = getAbsStateFromTrace(cmp->getICFGNode()); + AbstractState& as = getAbstractState(cmp->getICFGNode()); u32_t op0 = cmp->getOpVarID(0); u32_t op1 = cmp->getOpVarID(1); // if it is address @@ -1752,7 +2103,7 @@ void AbstractInterpretation::updateStateOnCmp(const CmpStmt *cmp) void AbstractInterpretation::updateStateOnLoad(const LoadStmt *load) { - AbstractState& as = getAbsStateFromTrace(load->getICFGNode()); + AbstractState& as = getAbstractState(load->getICFGNode()); u32_t rhs = load->getRHSVarID(); u32_t lhs = load->getLHSVarID(); as[lhs] = as.loadValue(rhs); @@ -1760,7 +2111,7 @@ void AbstractInterpretation::updateStateOnLoad(const LoadStmt *load) void AbstractInterpretation::updateStateOnStore(const StoreStmt *store) { - AbstractState& as = getAbsStateFromTrace(store->getICFGNode()); + AbstractState& as = getAbstractState(store->getICFGNode()); u32_t rhs = store->getRHSVarID(); u32_t lhs = store->getLHSVarID(); as.storeValue(lhs, as[rhs]); @@ -1864,7 +2215,7 @@ void AbstractInterpretation::updateStateOnCopy(const CopyStmt *copy) } }; - AbstractState& as = getAbsStateFromTrace(copy->getICFGNode()); + AbstractState& as = getAbstractState(copy->getICFGNode()); u32_t lhs = copy->getLHSVarID(); u32_t rhs = copy->getRHSVarID(); diff --git a/svf/lib/AE/Svfexe/PreAnalysis.cpp b/svf/lib/AE/Svfexe/PreAnalysis.cpp index 2d2b7a396d..e592473c06 100644 --- a/svf/lib/AE/Svfexe/PreAnalysis.cpp +++ b/svf/lib/AE/Svfexe/PreAnalysis.cpp @@ -80,5 +80,126 @@ void PreAnalysis::initWTO() funcToWTO[it->second->getFunction()] = iwto; } } +} + +const PointsTo& PreAnalysis::getPts(NodeID id) const +{ + return pta->getPts(id); +} + +const Set PreAnalysis::emptyWidenSet; + +void PreAnalysis::buildWidenSets() +{ + for (auto& [func, wto] : funcToWTO) + { + for (const ICFGWTOComp* comp : wto->getWTOComponents()) + { + if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast(comp)) + buildWidenSetForCycle(cycle); + } + } +} + +void PreAnalysis::buildWidenSetForCycle(const ICFGCycleWTO* cycle) +{ + const ICFGNode* head = cycle->head()->getICFGNode(); + Set& widenVars = cycleHeadToWidenVars[head]; + // Collect all nodes in cycle body (not including head itself) + for (const ICFGWTOComp* comp : cycle->getWTOComponents()) + { + if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast(comp)) + { + collectDefsAtNode(singleton->getICFGNode(), widenVars); + } + else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast(comp)) + { + // Recursively handle nested cycles + buildWidenSetForCycle(subCycle); + // Also collect nested cycle nodes' defs into parent's widen set + std::vector nestedNodes; + collectCycleNodes(subCycle, nestedNodes); + for (const ICFGNode* node : nestedNodes) + collectDefsAtNode(node, widenVars); + } + } + // Also collect defs at cycle head itself + collectDefsAtNode(head, widenVars); +} + +void PreAnalysis::collectCycleNodes(const ICFGCycleWTO* cycle, std::vector& nodes) +{ + nodes.push_back(cycle->head()->getICFGNode()); + for (const ICFGWTOComp* comp : cycle->getWTOComponents()) + { + if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast(comp)) + { + nodes.push_back(singleton->getICFGNode()); + } + else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast(comp)) + { + collectCycleNodes(subCycle, nodes); + } + } +} + +void PreAnalysis::collectDefsAtNode(const ICFGNode* node, Set& defs) +{ + for (const SVFStmt* stmt : node->getSVFStmts()) + { + // Top-level variable definitions (LHS of assignments) + if (const StoreStmt* store = SVFUtil::dyn_cast(stmt)) + { + // Address-taken: pts(lhs_pointer) are the defined objects + NodeID ptrId = store->getLHSVarID(); + const PointsTo& pts = pta->getPts(ptrId); + for (NodeID objId : pts) + defs.insert(objId); + } + else if (const LoadStmt* load = SVFUtil::dyn_cast(stmt)) + { + defs.insert(load->getLHSVarID()); + } + else if (const CopyStmt* copy = SVFUtil::dyn_cast(stmt)) + { + defs.insert(copy->getLHSVarID()); + } + else if (const AddrStmt* addr = SVFUtil::dyn_cast(stmt)) + { + defs.insert(addr->getLHSVarID()); + } + else if (const GepStmt* gep = SVFUtil::dyn_cast(stmt)) + { + defs.insert(gep->getLHSVarID()); + } + else if (const PhiStmt* phi = SVFUtil::dyn_cast(stmt)) + { + defs.insert(phi->getResID()); + } + else if (const SelectStmt* select = SVFUtil::dyn_cast(stmt)) + { + defs.insert(select->getResID()); + } + else if (const BinaryOPStmt* binary = SVFUtil::dyn_cast(stmt)) + { + defs.insert(binary->getResID()); + } + else if (const UnaryOPStmt* unary = SVFUtil::dyn_cast(stmt)) + { + defs.insert(unary->getResID()); + } + else if (const CmpStmt* cmp = SVFUtil::dyn_cast(stmt)) + { + defs.insert(cmp->getResID()); + } + else if (const CallPE* callPE = SVFUtil::dyn_cast(stmt)) + { + defs.insert(callPE->getLHSVarID()); + } + else if (const RetPE* retPE = SVFUtil::dyn_cast(stmt)) + { + defs.insert(retPE->getLHSVarID()); + } + } } diff --git a/svf/lib/AE/Svfexe/SparseDefUse.cpp b/svf/lib/AE/Svfexe/SparseDefUse.cpp new file mode 100644 index 0000000000..42126d3770 --- /dev/null +++ b/svf/lib/AE/Svfexe/SparseDefUse.cpp @@ -0,0 +1,56 @@ +//===- SparseDefUse.cpp -- Sparse Def-Use Table Implementation------------// +// +// SVF: Static Value-Flow Analysis +// +// Copyright (C) <2013-> +// + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. + +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// +//===----------------------------------------------------------------------===// + +/* + * SparseDefUse.cpp + * + * Created on: Feb 9, 2026 + * Author: Jiawei Wang + */ + +#include "AE/Svfexe/SparseDefUse.h" + +using namespace SVF; + +const std::vector SparseDefUse::emptyVec; + +void SparseDefUse::build(ICFG* icfg) +{ + // Single pass: scan every ICFG node for StoreStmts. + // For each store, get pts(LHS pointer) and add node to each object's def list. + for (auto it = icfg->begin(); it != icfg->end(); ++it) + { + const ICFGNode* node = it->second; + for (const SVFStmt* stmt : node->getSVFStmts()) + { + if (const StoreStmt* store = SVFUtil::dyn_cast(stmt)) + { + NodeID ptrId = store->getLHSVarID(); + const PointsTo& pts = pta->getPts(ptrId); + for (NodeID objId : pts) + { + objToDefNodes[objId].push_back(node); + } + } + } + } +} diff --git a/svf/lib/Util/Options.cpp b/svf/lib/Util/Options.cpp index a783ce4e95..efae00ce6e 100644 --- a/svf/lib/Util/Options.cpp +++ b/svf/lib/Util/Options.cpp @@ -802,6 +802,8 @@ const OptionMap Options::HandleRecur( } } ); +const Option Options::SparseAE( + "sparse", "Enable sparse state propagation using Use-Def table", false); const Option Options::Timeout( "timeout", "time out (seconds), set -1 (no timeout), default 14400s",14400); const Option Options::OutputName(