AE CPP APIs - SVF-tools/Software-Security-Analysis GitHub Wiki

Essential API Documentation for Labs and Assignments

Lab-Exercise-3

API Introduction
getNodeID("variable") Retrieves the node ID of the specified variable.
IntervalValue(lower, upper) Creates an interval value.
AbstractValue::getInterval() Retrieves the interval value of the abstract state.
AbstractValue::join_with(value) Merges the current value with another value.
getMemObjAddress("variable") Retrieves the memory object address of the specified variable.
AddressValue(getMemObjAddress("variable")) Creates an address value initialized to the memory object address of the specified variable.
AEState::widening(state) Performs widening on the given state.
AEState::narrowing(state) Performs narrowing on the given state.
AEState::joinWith(state) Merges the current state with another state.
AbstractValue::meet_with(value) Performs an intersection operation between the current value and another value.
getGepObjAddress("variable", offset) Retrieves the GEP (GetElementPtr) object address of the specified variable with the given offset.
AEState::loadValue(varId) Loads the abstract value from the variable ID's address.
AEState::storeValue(varId, val) Stores the abstract value at the variable ID's address.
AEState::printAbstractState() Prints the abstract trace for debugging purposes.

AEState::widening(state)

  • Perform widening on the current state with another state.

    For example,

    void exampleWidening() {
        AEState as1, as2;
        NodeID a = getNodeID("a");
        as1[a] = IntervalValue(1, 5); // as1: a in [1, 5]
        as2[a] = IntervalValue(3, 10); // as2: a in [3, 10]
        AEState widenedState = as1.widening(as2); // widenedState: a in [1, +inf]
    }
    

    Input: state (an AEState)

    Output: Widened state (an AEState)


AEState::loadValue(varId) and AEState::storeValue(varId, val)

  • Load and store values associated with a variable ID.

    For example,

    void exampleLoadValue() {
        AEState as;
        NodeID a = getNodeID("a");
        NodeID p = getNodeID("p");
        NodeID malloc = getNodeID("malloc");
        as[p] = AddressValue(getMemObjAddress("malloc"));
        as.storeValue(p, IntervalValue(42, 42)); // Store 42 at address p
    
        AbstractValue loadedValue = as.loadValue(p);
    
        std::cout << loadedValue.toString() << std::endl;
    }
    

    Input: varId (a NodeID), val (an AbstractValue)

    Output: Loaded value (an AbstractValue, can be Interval Value or Address Value)


AEState::printAbstractState()

  • Print the abstract trace for debugging purposes.

    For example,

    void examplePrintAbstractState() {
        AEState as;
        NodeID a = getNodeID("a");
        NodeID b = getNodeID("b");
    
        as[a] = IntervalValue(1, 5); // a in [1, 5]
        as[b] = IntervalValue(3, 7); // b in [3, 7]
    
        as.printAbstractState(); // Print the abstract trace for debugging
    }
    

    Input: None

    Output: None (prints the abstract trace)

-----------Var and Value-----------
Var2(b)             :  Value: [3, 7]
Var1(a)             :  Value: [1, 5]
-----------------------------------------

Assignment-3

This section matches the current Assignment-3 skeleton. The authoritative interfaces are in Assignment-3/CPP/AEState.h, Assignment_3.h, AEHelper.cpp, and AEReporter.h.

State ownership

Assignment 3 owns its abstract trace. It does not inherit from or delegate state storage to SVF's AbstractInterpretation.

Map<const ICFGNode*, AEState> preAbsTrace;
Map<const ICFGNode*, AEState> postAbsTrace;

AEState& getAEState(const ICFGNode* node);
  • Each map value is an AEState, not a raw AbstractState.
  • getAEState(node) returns the post-state and creates a default entry when necessary.
  • postAbsTrace is a protected data member. It is indexed as postAbsTrace[node]; it is not called as postAbsTrace().
  • There is no getAbsStateFromTrace() API in Assignment 3.
  • AEState contains one AbstractState. Use raw() only when an SVF domain operation specifically requires the underlying type.

AEState and AbstractState both have value semantics in C++. Copying either one creates an independent state:

AEState& current = getAEState(node);
AEState snapshot = current;
AbstractState rawSnapshot = current.raw();

Feature 1: Statement transfer functions

Starter and helper functions

  • getAEState(node) obtains the current node's post-state.
  • The AbstractExecution facade supplies getAbsValue, updateAbsValue, loadValue, storeValue, getGepObjAddrs, getGepElementIndex, getGepByteOffset, and getAllocaInstByteSize.
  • updateStateOnCall(const CallPE*) is supplied for phi-like actual-to-formal parameter propagation.

Use SVFUtil::isa<T>, SVFUtil::dyn_cast<T>, and SVFUtil::cast<T> to test and cast SVF objects.

State and memory APIs

API Meaning
AEState() Construct an empty Assignment-3 state.
AEState(const AbstractState& state) Copy a raw state into an AEState.
raw() / operator AbstractState&() Access or pass the contained raw state.
state[varId] Read or update the AbstractValue associated with a NodeID.
getAbsValue(var) / updateAbsValue(var, value) Read or update a ValVar, ObjVar, or general SVFVar.
loadValue(pointer) / storeValue(pointer, value) Load or store through an abstract pointer.
load(addr) / store(addr, value) Access one virtual memory address directly.
initObjVar(obj) Initialise an object variable in the state.
getIDFromAddr(addr) Convert a virtual address to its PAG node ID.
inVarToValTable(id) / inVarToAddrsTable(id) Test whether a variable has a value or address entry.
inAddrToValTable(id) / inAddrToAddrsTable(id) Test whether a memory location has a value or address entry.

The pre-implemented facade methods select the appropriate AEState using the ICFG node supplied explicitly or attached to a statement. You may instead obtain one state and call its methods directly:

AEState& state = getAEState(stmt->getICFGNode());
AbstractValue value = state.loadValue(pointer);
state.storeValue(pointer, value);

GEP and allocation APIs

API Meaning
getGepElementIndex(gep) Compute the flattened element-index interval for a GEP.
getGepByteOffset(gep) Compute the byte-offset interval for a GEP.
getGepObjAddrs(pointer, offset) Materialise abstract GEP object addresses.
getAllocaInstByteSize(addr) Compute the byte size represented by an AddrStmt allocation.
getPointeeElement(object) Return the pointee element type when available.

getElementIndex() and getByteOffset() are aliases for getGepElementIndex() and getGepByteOffset().

Statement queries

Statement APIs used by Assignment 3
AddrStmt getICFGNode, getLHSVarID, getRHSVar, getArrSize
CopyStmt getICFGNode, getLHSVarID, getRHSVarID, getCopyKind, isValueCopy, isZext, isSext, isInt2Ptr, isPtr2Int, isBitcast
BinaryOPStmt getResID, getOpVarID(index), getOpcode
CmpStmt getResID, getOpVarID(index), getOpVar(index), getPredicate
LoadStmt getICFGNode, getLHSVarID, getRHSVarID, getRHSVar
StoreStmt getICFGNode, getLHSVarID, getLHSVar, getRHSVarID
GepStmt getICFGNode, getLHSVarID, getRHSVarID, getRHSVar, constant/dynamic offset queries
PhiStmt getResID, getOpVarNum, getOpVarID(index), getOpICFGNode(index)
CallPE getResID, getOpVarNum, getOpVarID(index), getOpCallICFGNode(index)
RetPE getLHSVarID, getRHSVarID
SelectStmt getResID, getCondition, getTrueValue, getFalseValue

PhiStmt, CallPE, and SelectStmt are multi-operand statements. Do not assume that they use the two-operand AssignStmt interface.

Abstract domains

Type Common APIs
AbstractValue constructors, getInterval, getAddrs, isInterval, isAddr, join_with, equals
IntervalValue constructors, top, lb, ub, isBottom, is_numeral, is_zero, getIntNumeral, arithmetic operators, meet_with
AddressValue constructors, insert, hasIntersect
AbstractState joinWith, widening, narrowing, equality, load, store, clear

Important address constants and predicates include IRGraph::NullPtr, NullMemAddr, AbstractState::isNullMem(addr), AbstractState::isBlackHoleObjAddr(addr), AbstractState::isVirtualMemAddress(addr), and AbstractState::getVirtualMemAddress(id).

Mini example

Copy and load/store transfers can be expressed directly through the node's AEState:

const ICFGNode* node = stmt->getICFGNode();
AEState& state = getAEState(node);
state[lhsId] = state[rhsId];
const ValVar* loadPointer = SVFUtil::cast<ValVar>(load->getRHSVar());
state[load->getLHSVarID()] = state.loadValue(loadPointer);

const ValVar* storePointer = SVFUtil::cast<ValVar>(store->getLHSVar());
state.storeValue(storePointer, state[store->getRHSVarID()]);

Feature 2: Branch feasibility

Starter and helper functions

  • node->getInEdges() and edge->getSrcNode() enumerate incoming states.
  • IntraCFGEdge condition and successor-value queries identify the branch represented by an edge.
  • Refine a copied predecessor state according to the edge condition before joining it. The organisation of that refinement is part of your solution.
  • postAbsTrace supplies predecessor post-states; preAbsTrace holds function-entry input.

Control-flow and lattice APIs

API Meaning
node->getInEdges() Enumerate predecessor edges.
edge->getSrcNode() Obtain the predecessor node.
IntraCFGEdge::getCondition() Obtain the branch condition, if any.
IntraCFGEdge::getSuccessorCondValue() Obtain the edge's branch or switch value.
condition->getInEdges() Find statements defining a condition variable.
icfg->getICFGEdge(src, dst, kind) Query a specific ICFG edge.
AEState::joinWith(other) Join another incoming state in place.
IntervalValue::meet_with(value) Refine an interval by intersection.

Mini example

Copy and refine one predecessor state before joining it:

AEState candidate = postAbsTrace.at(edge->getSrcNode());
const IntraCFGEdge* intra = SVFUtil::cast<IntraCFGEdge>(edge);
const SVFVar* condition = intra->getCondition();
s64_t successor = intra->getSuccessorCondValue();
// Intersect the condition or comparison-operand values in candidate with the
// constraint represented by successor, then write each refined value back,
// for example: candidate[refinedVarId] = AbstractValue(refinedInterval).
// If a required meet is bottom, this edge is infeasible and candidate must
// not be joined. The line below represents the feasible, non-bottom case.
incoming.joinWith(candidate);

Comparison predicates are queried through CmpStmt::getPredicate(). The AbstractValue and IntervalValue operations listed under Feature 1 can be used to compare, meet, and join refined values.

Feature 3: Loop and recursion fixpoint

Starter and helper functions

  • initWTO, funcToWTO, ICFGSingletonWTO, and ICFGCycleWTO provide the interprocedural WTO.
  • Options::WidenDelay() controls the number of precise iterations before widening.
  • ander->getCallGraphSCC() and ander->inSameCallGraphSCC(caller, callee) provide call-graph SCC operations and identify recursive calls.
  • getAEState, preAbsTrace, and postAbsTrace hold cycle snapshots and results.

WTO and fixpoint APIs

Area APIs
WTO traversal funcToWTO, ICFGWTO::getWTOComponents, ICFGSingletonWTO::getICFGNode, ICFGCycleWTO::head, ICFGCycleWTO::getWTOComponents
Fixpoint control Options::WidenDelay, AEState::widening, AEState::narrowing, equals, operator==
Recursion CallICFGNode::getCaller, ander->getCallGraphSCC(), ander->inSameCallGraphSCC(caller, callee)

Mini example

Widening and narrowing return raw AbstractState values. Assign the result back to an AEState when updating the trace:

AEState& headState = getAEState(head);
AbstractState next = headState.widening(candidate);
headState = next;

Feature 4: External-API value summaries

Starter and helper functions

  • handleStubFunctions, handleCheckpointStubs, isExternalCallForAssignment, SVFUtil::isExtCall, updateStateOnCall, and inSameCallGraphSCC are supplied by the harness.
  • State operations from Feature 1 model the values and memory changed by a summary.

Call and summary APIs

Area APIs
Call dispatch getCalledFunction, getCaller, getArgument(index), arg_size, getRetICFGNode, RetICFGNode::getActualRet
Call classification SVFUtil::isExtCall, isExternalCallForAssignment, ander->inSameCallGraphSCC
Harness dispatch handleStubFunctions, handleCheckpointStubs, updateStateOnCall
Value and memory effects getAbsValue, updateAbsValue, loadValue, storeValue, getGepObjAddrs, interval arithmetic

isExternalCallForAssignment() recognises the assignment's supported API families. Use SVFUtil::isExtCall(function) for ordinary external-call routing. When propagating a completed call, use getRetICFGNode() and getActualRet() to update the caller-side return value.

Mini example

For an external call with a return value, a summary can set that value to top and propagate the state to the return node:

AEState summary = getAEState(call);
const RetICFGNode* ret = call->getRetICFGNode();
summary.updateAbsValue(
    ret->getActualRet(), AbstractValue(IntervalValue::top()));
postAbsTrace[ret] = summary;

Feature 5: Buffer-overflow checker

Starter and helper functions

  • getGepByteOffset, getGepElementIndex, getGepObjAddrs, and getAllocaInstByteSize recover access offsets and sizes.
  • svfir->getBaseObject(id) and svfir->getGNode(id) resolve objects.
  • BaseObjVar exposes size and black-hole metadata.
  • reportBufOverflow(node) records a finding.

Buffer and object APIs

API Use
svfir->getBaseObject(id) Resolve a PAG object to its base object.
svfir->getGNode(id) Resolve a PAG node ID.
BaseObjVar::isConstantByteSize() Test whether an object's byte size is known.
BaseObjVar::getByteSizeOfObj() Read the known object byte size.
BaseObjVar::isBlackHoleObj() Test for an unknown or black-hole object.
getGepByteOffset(gep) Compute the current GEP's byte-offset interval.
getGepElementIndex(gep) Compute the current GEP's element-index interval.
getGepObjAddrs(pointer, offset) Materialise addresses for a base pointer and offset.
getAllocaInstByteSize(addr) Obtain an allocation's byte size.
reportBufOverflow(node) Record a buffer-overflow finding.

GepObjVar::getConstantFieldIdx() is a field index, not a general accumulated byte offset. Use getGepByteOffset() and maintain any required offset across GEP chains explicitly.

Mini example

Given a resolved base-object ID, compare a direct GEP's byte-offset interval with the object size. A complete checker must additionally account for chained GEPs and external memory operations.

AEState& state = getAEState(gep->getICFGNode());
IntervalValue offset = state.getGepByteOffset(gep);
const BaseObjVar* base = svfir->getBaseObject(objectId);
u32_t size = base->getByteSizeOfObj();
if (offset.lb().getIntNumeral() < 0 ||
    offset.ub().getIntNumeral() >= static_cast<s64_t>(size))
    reportBufOverflow(gep->getICFGNode());

Feature 6: Null-pointer-dereference checker

Starter and helper functions

  • IRGraph::NullPtr and NullMemAddr identify explicit null values.
  • AbstractState::isNullMem, isBlackHoleObjAddr, and AEState::isFreedMem classify addresses.
  • Load, store, GEP, and call statement queries expose dereferenced pointers.
  • reportNullDeref(node) records a finding.

Null and address APIs

API Use
AbstractValue::getAddrs() Enumerate the addresses represented by a pointer value.
AbstractState::isNullMem(addr) Test whether an address is null.
AbstractState::isBlackHoleObjAddr(addr) Test whether an address denotes an unknown object.
AEState::isFreedMem(addr) Test whether an address denotes freed memory.
LoadStmt::getRHSVar() Obtain the pointer read by a load.
StoreStmt::getLHSVar() Obtain the pointer written by a store.
GepStmt::getRHSVar() Obtain the base pointer used by a GEP.
CallICFGNode::getArgument(index) Obtain pointer arguments used by an external operation.
reportNullDeref(node) Record a null-pointer-dereference finding.

Mini example

For a load, inspect the abstract addresses represented by its pointer operand:

const ValVar* pointer = SVFUtil::cast<ValVar>(load->getRHSVar());
AEState& state = getAEState(load->getICFGNode());
const AbstractValue& value = state.getAbsValue(pointer);
if (value.isAddr()) {
    for (u32_t addr : value.getAddrs())
        if (AbstractState::isNullMem(addr) || state.isFreedMem(addr))
            reportNullDeref(load->getICFGNode());
}

The helper names added privately by a complete solution are implementation choices, not required APIs. Students may organise these features differently or implement additional features.

⚠️ **GitHub.com Fallback** ⚠️