AE CPP APIs - SVF-tools/Software-Security-Analysis GitHub Wiki
| 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. |
-
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)
-
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)
-
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]
-----------------------------------------
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.
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 rawAbstractState. -
getAEState(node)returns the post-state and creates a default entry when necessary. -
postAbsTraceis a protected data member. It is indexed aspostAbsTrace[node]; it is not called aspostAbsTrace(). - There is no
getAbsStateFromTrace()API in Assignment 3. -
AEStatecontains oneAbstractState. Useraw()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();-
getAEState(node)obtains the current node's post-state. - The
AbstractExecutionfacade suppliesgetAbsValue,updateAbsValue,loadValue,storeValue,getGepObjAddrs,getGepElementIndex,getGepByteOffset, andgetAllocaInstByteSize. -
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.
| 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);| 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 | 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.
| 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).
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()]);-
node->getInEdges()andedge->getSrcNode()enumerate incoming states. -
IntraCFGEdgecondition 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.
-
postAbsTracesupplies predecessor post-states;preAbsTraceholds function-entry input.
| 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. |
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.
-
initWTO,funcToWTO,ICFGSingletonWTO, andICFGCycleWTOprovide the interprocedural WTO. -
Options::WidenDelay()controls the number of precise iterations before widening. -
ander->getCallGraphSCC()andander->inSameCallGraphSCC(caller, callee)provide call-graph SCC operations and identify recursive calls. -
getAEState,preAbsTrace, andpostAbsTracehold cycle snapshots and results.
| 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)
|
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;-
handleStubFunctions,handleCheckpointStubs,isExternalCallForAssignment,SVFUtil::isExtCall,updateStateOnCall, andinSameCallGraphSCCare supplied by the harness. - State operations from Feature 1 model the values and memory changed by a summary.
| 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.
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;-
getGepByteOffset,getGepElementIndex,getGepObjAddrs, andgetAllocaInstByteSizerecover access offsets and sizes. -
svfir->getBaseObject(id)andsvfir->getGNode(id)resolve objects. -
BaseObjVarexposes size and black-hole metadata. -
reportBufOverflow(node)records a finding.
| 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.
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());-
IRGraph::NullPtrandNullMemAddridentify explicit null values. -
AbstractState::isNullMem,isBlackHoleObjAddr, andAEState::isFreedMemclassify addresses. - Load, store, GEP, and call statement queries expose dereferenced pointers.
-
reportNullDeref(node)records a finding.
| 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. |
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.