Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion svf/include/AE/Core/AbstractState.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 4 additions & 5 deletions svf/include/AE/Core/IntervalValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
30 changes: 24 additions & 6 deletions svf/include/AE/Svfexe/AEDetector.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<NodeID> 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.
Expand Down Expand Up @@ -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<NodeID> 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.
Expand Down Expand Up @@ -268,7 +281,7 @@ class BufOverflowDetector : public AEDetector
/**
* @brief Reports all detected buffer overflow bugs.
*/
void reportBug()
void reportBug() override
{
if (!nodeToBugInfo.empty())
{
Expand Down Expand Up @@ -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<NodeID> 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.
Expand Down Expand Up @@ -401,7 +419,7 @@ class NullptrDerefDetector : public AEDetector
/**
* @brief Reports all detected nullptr dereference bugs.
*/
void reportBug()
void reportBug() override
{
if (!nodeToBugInfo.empty())
{
Expand Down
8 changes: 7 additions & 1 deletion svf/include/AE/Svfexe/AbsExtAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<NodeID> getNeededVarsForSparse(const ICFGNode* node);

// --- Shared primitives used by string/memory handlers ---

/// Get the byte size of each element for a pointer/array variable.
Expand Down Expand Up @@ -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.
Expand Down
68 changes: 51 additions & 17 deletions svf/include/AE/Svfexe/AbstractInterpretation.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -165,23 +166,11 @@ class AbstractInterpretation

Set<const CallICFGNode*> 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:
Expand All @@ -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<const ICFGNode*>& 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<const ValVar*> getValVars(const ICFGNode* node);

/// Get address-taken ObjVars accessed at an ICFG node via points-to (sparse mode only)
std::vector<const ObjVar*> getObjVars(const ICFGNode* node);

/// Get all SVFVars (ValVars + ObjVars) accessed at an ICFG node (sparse mode only)
std::vector<const SVFVar*> 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<const ValVar*>& vars);

/// Build an AbstractState from definition sites of multiple address-taken variables
AbstractState getAbstractState(const std::vector<const ObjVar*>& vars);

/// Build an AbstractState from definition sites of multiple SVF variables
AbstractState getAbstractState(const std::vector<const SVFVar*>& vars);

/**
* Check if execution state exist at the branch edge
*
Expand Down Expand Up @@ -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
*
Expand Down Expand Up @@ -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;
}
Expand Down
26 changes: 26 additions & 0 deletions svf/include/AE/Svfexe/PreAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,20 +69,46 @@ 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<const FunObjVar*, const ICFGWTO*>& 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<NodeID>& 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<const ICFGNode*>& nodes);

/// Collect defined variables at an ICFG node (top-level defs + address-taken via pts)
void collectDefsAtNode(const ICFGNode* node, Set<NodeID>& defs);

/// Build widen set for a single WTO cycle (recursive for nested cycles)
void buildWidenSetForCycle(const ICFGCycleWTO* cycle);
SVFIR* svfir;
ICFG* icfg;
AndersenWaveDiff* pta;
CallGraph* callGraph;
CallGraphSCC* callGraphSCC;

Map<const FunObjVar*, const ICFGWTO*> funcToWTO;
Map<const ICFGNode*, Set<NodeID>> cycleHeadToWidenVars;
static const Set<NodeID> emptyWidenSet;
};

} // End namespace SVF
Expand Down
73 changes: 73 additions & 0 deletions svf/include/AE/Svfexe/SparseDefUse.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//===- SparseDefUse.h -- Sparse Def-Use Table for Abstract Interpretation-//
//
// SVF: Static Value-Flow Analysis
//
// Copyright (C) <2013-> <Yulei Sui>
//

// 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 <http://www.gnu.org/licenses/>.
//
//===----------------------------------------------------------------------===//

/*
* 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<const ICFGNode*>& getDefICFGNodes(NodeID objId) const
{
auto it = objToDefNodes.find(objId);
if (it != objToDefNodes.end())
return it->second;
return emptyVec;
}

private:
PointerAnalysis* pta;
Map<NodeID, std::vector<const ICFGNode*>> objToDefNodes;
static const std::vector<const ICFGNode*> emptyVec;
};

} // End namespace SVF

#endif /* INCLUDE_AE_SVFEXE_SPARSEDEFUSE_H_ */
2 changes: 2 additions & 0 deletions svf/include/Util/Options.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ class Options
static const Option<u32_t> WidenDelay;
/// recursion handling mode, Default: TOP
static const OptionMap<u32_t> HandleRecur;
/// Enable sparse state propagation using Use-Def table
static const Option<bool> SparseAE;
/// the max time consumptions (seconds). Default: 4 hours 14400s
static const Option<u32_t> Timeout;
/// bug info output file, Default: output.db
Expand Down
Loading
Loading