diff --git a/svf/include/AE/Svfexe/AEDetector.h b/svf/include/AE/Svfexe/AEDetector.h index 425844cfe..7930d444e 100644 --- a/svf/include/AE/Svfexe/AEDetector.h +++ b/svf/include/AE/Svfexe/AEDetector.h @@ -72,11 +72,27 @@ class AEDetector /** * @brief Pure virtual function for detecting issues within a node. - * @param as Reference to the abstract state. + * @param as Reference to the abstract state interface. * @param node Pointer to the ICFG node. + * @note This is the legacy node-level detection method, kept for backward compatibility. */ virtual void detect(AbstractState& as, const ICFGNode* node) = 0; + /** + * @brief Check for issues in a single statement. + * + * This method is called during statement processing to detect issues + * in the same loop as state updates, eliminating redundant traversals. + * Subclasses should override this to implement statement-level detection. + * + * @param stmt Pointer to the SVF statement to check. + * @param as Reference to the abstract state interface. + */ + virtual void checkStatement(const SVFStmt* stmt, AbstractState& as) + { + // Default implementation does nothing - subclasses override as needed + } + /** * @brief Pure virtual function for handling stub external API calls. (e.g. UNSAFE_BUFACCESS) * @param call Pointer to the ext call ICFG node. @@ -135,6 +151,7 @@ class AEException : public std::exception class BufOverflowDetector : public AEDetector { friend class AbstractInterpretation; + public: /** * @brief Constructor initializes the detector kind to BUF_OVERFLOW and sets up external API buffer overflow rules. @@ -160,71 +177,36 @@ class BufOverflowDetector : public AEDetector return detector->getKind() == AEDetector::BUF_OVERFLOW; } - /** - * @brief Updates the offset of a GEP object from its base. - * @param as Reference to the abstract state. - * @param gepAddrs Address value for GEP. - * @param objAddrs Address value for the object. - * @param offset The interval value of the offset. - */ - void updateGepObjOffsetFromBase(AbstractState& as, - AddressValue gepAddrs, - AddressValue objAddrs, - IntervalValue offset); /** * @brief Detect buffer overflow issues within a node. - * @param as Reference to the abstract state. + * @param as Reference to the abstract state interface. * @param node Pointer to the ICFG node. + * @note This is the legacy node-level detection, kept for external API handling. */ - void detect(AbstractState& as, const ICFGNode*); - + void detect(AbstractState& as, const ICFGNode*) override; /** - * @brief Handles external API calls related to buffer overflow detection. - * @param call Pointer to the call ICFG node. + * @brief Check for buffer overflow in a single statement. + * + * This method performs buffer overflow detection for a single GEP statement + * in the same loop as state updates, eliminating redundant traversals. + * + * @param stmt Pointer to the SVF statement to check. + * @param as Reference to the abstract state interface. */ - void handleStubFunctions(const CallICFGNode*); + void checkStatement(const SVFStmt* stmt, AbstractState& as) override; /** - * @brief Adds an offset to a GEP object. - * @param obj Pointer to the GEP object. - * @param offset The interval value of the offset. + * @brief Handles external API calls related to buffer overflow detection. + * @param call Pointer to the call ICFG node. */ - void addToGepObjOffsetFromBase(const GepObjVar* obj, const IntervalValue& offset) - { - gepObjOffsetFromBase[obj] = offset; - } + void handleStubFunctions(const CallICFGNode*) override; - /** - * @brief Checks if a GEP object has an associated offset. - * @param obj Pointer to the GEP object. - * @return True if the GEP object has an offset, false otherwise. - */ - bool hasGepObjOffsetFromBase(const GepObjVar* obj) const - { - return gepObjOffsetFromBase.find(obj) != gepObjOffsetFromBase.end(); - } - - /** - * @brief Retrieves the offset of a GEP object from its base. - * @param obj Pointer to the GEP object. - * @return The interval value of the offset. - */ - IntervalValue getGepObjOffsetFromBase(const GepObjVar* obj) const - { - if (hasGepObjOffsetFromBase(obj)) - return gepObjOffsetFromBase.at(obj); - else - { - assert(false && "GepObjVar not found in gepObjOffsetFromBase"); - abort(); - } - } /** * @brief Retrieves the access offset for a given object and GEP statement. - * @param as Reference to the abstract state. + * @param as Reference to the abstract state interface. * @param objId The ID of the object. * @param gep Pointer to the GEP statement. * @return The interval value of the access offset. @@ -268,7 +250,7 @@ class BufOverflowDetector : public AEDetector /** * @brief Reports all detected buffer overflow bugs. */ - void reportBug() + void reportBug() override { if (!nodeToBugInfo.empty()) { @@ -282,6 +264,54 @@ class BufOverflowDetector : public AEDetector } } + /** + * @brief Updates the offset of a GEP object from its base. + * @param as Reference to the abstract state interface. + * @param gepAddrs Address value for GEP. + * @param objAddrs Address value for the object. + * @param offset The interval value of the offset. + */ + void updateGepObjOffsetFromBase(AbstractState& as, + AddressValue gepAddrs, + AddressValue objAddrs, + IntervalValue offset); + + /** + * @brief Adds an offset to a GEP object. + * @param obj Pointer to the GEP object. + * @param offset The interval value of the offset. + */ + void addToGepObjOffsetFromBase(const GepObjVar* obj, const IntervalValue& offset) + { + gepObjOffsetFromBase[obj] = offset; + } + + /** + * @brief Checks if a GEP object has an associated offset. + * @param obj Pointer to the GEP object. + * @return True if the GEP object has an offset, false otherwise. + */ + bool hasGepObjOffsetFromBase(const GepObjVar* obj) const + { + return gepObjOffsetFromBase.find(obj) != gepObjOffsetFromBase.end(); + } + + /** + * @brief Retrieves the offset of a GEP object from its base. + * @param obj Pointer to the GEP object. + * @return The interval value of the offset. + */ + IntervalValue getGepObjOffsetFromBase(const GepObjVar* obj) const + { + if (hasGepObjOffsetFromBase(obj)) + return gepObjOffsetFromBase.at(obj); + else + { + assert(false && "GepObjVar not found in gepObjOffsetFromBase"); + abort(); + } + } + /** * @brief Initializes external API buffer overflow check rules. */ @@ -289,14 +319,14 @@ class BufOverflowDetector : public AEDetector /** * @brief Handles external API calls related to buffer overflow detection. - * @param as Reference to the abstract state. + * @param as Reference to the abstract state interface. * @param call Pointer to the call ICFG node. */ void detectExtAPI(AbstractState& as, const CallICFGNode *call); /** * @brief Checks if memory can be safely accessed. - * @param as Reference to the abstract state. + * @param as Reference to the abstract state interface. * @param value Pointer to the SVF var. * @param len The interval value representing the length of the memory access. * @return True if the memory access is safe, false otherwise. @@ -306,7 +336,7 @@ class BufOverflowDetector : public AEDetector private: /** * @brief Detects buffer overflow in 'strcat' function calls. - * @param as Reference to the abstract state. + * @param as Reference to the abstract state interface. * @param call Pointer to the call ICFG node. * @return True if a buffer overflow is detected, false otherwise. */ @@ -314,19 +344,20 @@ class BufOverflowDetector : public AEDetector /** * @brief Detects buffer overflow in 'strcpy' function calls. - * @param as Reference to the abstract state. + * @param as Reference to the abstract state interface. * @param call Pointer to the call ICFG node. * @return True if a buffer overflow is detected, false otherwise. */ bool detectStrcpy(AbstractState& as, const CallICFGNode *call); private: - Map gepObjOffsetFromBase; ///< Maps GEP objects to their offsets from the base. + Map gepObjOffsetFromBase; ///< Maps GEP objects to their offsets from their base. Map>> extAPIBufOverflowCheckRules; ///< Rules for checking buffer overflows in external APIs. Set bugLoc; ///< Set of locations where bugs have been reported. SVFBugReport recoder; ///< Recorder for abstract execution bugs. Map nodeToBugInfo; ///< Maps ICFG nodes to bug information. }; + class NullptrDerefDetector : public AEDetector { friend class AbstractInterpretation; @@ -345,16 +376,16 @@ class NullptrDerefDetector : public AEDetector /** * @brief Detects nullptr dereferences issues within a node. - * @param as Reference to the abstract state. + * @param as Reference to the abstract state interface. * @param node Pointer to the ICFG node. */ - void detect(AbstractState& as, const ICFGNode* node); + void detect(AbstractState& as, 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 +432,7 @@ class NullptrDerefDetector : public AEDetector /** * @brief Reports all detected nullptr dereference bugs. */ - void reportBug() + void reportBug() override { if (!nodeToBugInfo.empty()) { @@ -417,7 +448,7 @@ class NullptrDerefDetector : public AEDetector /** * @brief Handle external API calls related to nullptr dereferences. - * @param as Reference to the abstract state. + * @param as Reference to the abstract state interface. * @param call Pointer to the call ICFG node. */ void detectExtAPI(AbstractState& as, const CallICFGNode* call); diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index 53a76becf..99449f822 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -35,7 +35,9 @@ #include "AE/Svfexe/AbsExtAPI.h" #include "Util/SVFBugReport.h" #include "Util/SVFStat.h" +#include "Util/Options.h" #include "Graphs/SCC.h" +#include namespace SVF { @@ -176,6 +178,27 @@ class AbstractInterpretation } } + /* + * @brief Retrieves the abstract value from the trace for a given ICFG node and variable ID. + * @param node Pointer to the ICFG node. + * @param varId ID of the variable. + * @return Abstract value. + * @throws Assertion if no trace exists for the node. + */ + AbstractValue getAbstractValueFromTrace(const ICFGNode* node, NodeID varId) + { + if (abstractTrace.count(node) == 0) + { + assert(false && "No preAbsTrace for this node"); + abort(); + } + else + { + //TODO: support sparse abstract state + return abstractTrace[node][varId]; + } + } + private: /// Global ICFGNode is handled at the entry of the program, virtual void handleGlobalNode(); diff --git a/svf/include/Util/Options.h b/svf/include/Util/Options.h index 0fa17910d..5ce8cfe14 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; + /// Use sparse abstract state representation (TODO: to implement) + //static const Option UseSparseState; /// 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 ae9d0bb3c..d6dc9bbcd 100644 --- a/svf/lib/AE/Svfexe/AEDetector.cpp +++ b/svf/lib/AE/Svfexe/AEDetector.cpp @@ -33,69 +33,81 @@ using namespace SVF; /** - * @brief Detects buffer overflow issues within a given ICFG node. + * @brief Check for buffer overflow in a single statement. * - * This function handles both non-call nodes, where it analyzes GEP (GetElementPtr) - * instructions for potential buffer overflows, and call nodes, where it checks - * for external API calls that may cause overflows. + * This method performs buffer overflow detection for a single GEP statement + * in the same loop as state updates, eliminating redundant traversals. + * GEP offset tracking is now handled in AbstractState during GEP processing. * - * @param as Reference to the abstract state. - * @param node Pointer to the ICFG node. + * @param stmt Pointer to the SVF statement to check. + * @param as Reference to the abstract state interface. */ -void BufOverflowDetector::detect(AbstractState& as, const ICFGNode* node) +void BufOverflowDetector::checkStatement(const SVFStmt* stmt, AbstractState& as) { - if (!SVFUtil::isa(node)) + if (SVFUtil::isa(stmt->getICFGNode())) { + return; + } + // Only check GEP statements for buffer overflow + if (const GepStmt* gep = SVFUtil::dyn_cast(stmt)) { - // Handle non-call nodes by analyzing GEP instructions - for (const SVFStmt* stmt : node->getSVFStmts()) - { - if (const GepStmt* gep = SVFUtil::dyn_cast(stmt)) - { - SVFIR* svfir = PAG::getPAG(); - NodeID lhs = gep->getLHSVarID(); - NodeID rhs = gep->getRHSVarID(); + SVFIR* svfir = PAG::getPAG(); + NodeID lhs = gep->getLHSVarID(); + NodeID rhs = gep->getRHSVarID(); - // Update the GEP object offset from its base - updateGepObjOffsetFromBase(as, as[lhs].getAddrs(), as[rhs].getAddrs(), as.getByteOffset(gep)); + // Update the GEP object offset from its base (same as Python version) + updateGepObjOffsetFromBase(as, as[lhs].getAddrs(), as[rhs].getAddrs(), as.getByteOffset(gep)); - IntervalValue baseObjSize = IntervalValue::bottom(); - AddressValue objAddrs = as[gep->getRHSVarID()].getAddrs(); - for (const auto& addr : objAddrs) - { - NodeID objId = as.getIDFromAddr(addr); - u32_t size = 0; - // like `int arr[10]` which has constant size before runtime - if (svfir->getBaseObject(objId)->isConstantByteSize()) - { - size = svfir->getBaseObject(objId)->getByteSizeOfObj(); - } - else - { - // like `int len = ***; int arr[len]`, whose size can only be known in runtime - const ICFGNode* addrNode = svfir->getBaseObject(objId)->getICFGNode(); - for (const SVFStmt* stmt2 : addrNode->getSVFStmts()) - { - if (const AddrStmt* addrStmt = SVFUtil::dyn_cast(stmt2)) - { - size = as.getAllocaInstByteSize(addrStmt); - } - } - } + AddressValue objAddrs = as[rhs].getAddrs(); + for (const auto& addr : objAddrs) + { + NodeID objId = as.getIDFromAddr(addr); + u32_t size = 0; - // Calculate access offset and check for potential overflow - IntervalValue accessOffset = getAccessOffset(as, objId, gep); - if (accessOffset.ub().getIntNumeral() >= size) + // Get object size - constant or runtime-determined + if (svfir->getBaseObject(objId)->isConstantByteSize()) + { + size = svfir->getBaseObject(objId)->getByteSizeOfObj(); + } + else + { + // Runtime-determined size (e.g., VLA) + const ICFGNode* addrNode = svfir->getBaseObject(objId)->getICFGNode(); + for (const SVFStmt* stmt2 : addrNode->getSVFStmts()) + { + if (const AddrStmt* addrStmt = SVFUtil::dyn_cast(stmt2)) { - AEException bug(stmt->toString()); - addBugToReporter(bug, stmt->getICFGNode()); + size = as.getAllocaInstByteSize(addrStmt); } } } + + // Calculate access offset and check for potential overflow + IntervalValue accessOffset = getAccessOffset(as, objId, gep); + if (accessOffset.ub().getIntNumeral() >= size) + { + AEException bug(stmt->toString()); + addBugToReporter(bug, stmt->getICFGNode()); + } } } - else +} + +/** + * @brief Detects buffer overflow issues within a given ICFG node. + * + * This legacy method is kept for handling external API calls at call nodes. + * GEP statement detection is now handled by checkStatement() which is called + * in the same loop as state updates, eliminating redundant traversals. + * + * @param as Reference to the abstract state. + * @param node Pointer to the ICFG node. + */ +void BufOverflowDetector::detect(AbstractState& as, const ICFGNode* node) +{ + // GEP statement detection is now handled by checkStatement() in the main loop + // This method only handles external API calls at call nodes + if (SVFUtil::isa(node)) { - // Handle call nodes by checking for external API calls const CallICFGNode* callNode = SVFUtil::cast(node); if (SVFUtil::isExtCall(callNode->getCalledFunction())) { @@ -104,6 +116,77 @@ void BufOverflowDetector::detect(AbstractState& as, const ICFGNode* node) } } +/** + * @brief Updates the offset of a GEP object from its base. + * + * This function calculates and stores the offset of a GEP object from its base object + * using the addresses and offsets provided. + * + * @param as Reference to the abstract state. + * @param gepAddrs The addresses of the GEP objects. + * @param objAddrs The addresses of the base objects. + * @param offset The interval value of the offset. + */ +void BufOverflowDetector::updateGepObjOffsetFromBase(AbstractState& as, SVF::AddressValue gepAddrs, SVF::AddressValue objAddrs, SVF::IntervalValue offset) +{ + SVFIR* svfir = PAG::getPAG(); + + for (const auto& objAddr : objAddrs) + { + NodeID objId = as.getIDFromAddr(objAddr); + auto obj = svfir->getGNode(objId); + + if (SVFUtil::isa(obj)) + { + // if the object is a BaseObjVar, add the offset directly + // like llvm bc `arr = alloc i8 12; p = gep arr, 4` + // we write key value pair {gep, 4} + for (const auto& gepAddr : gepAddrs) + { + NodeID gepObj = as.getIDFromAddr(gepAddr); + if (const GepObjVar* gepObjVar = SVFUtil::dyn_cast(svfir->getGNode(gepObj))) + { + addToGepObjOffsetFromBase(gepObjVar, offset); + } + else + { + assert(AbstractState::isInvalidMem(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address"); + } + } + } + else if (SVFUtil::isa(obj)) + { + // if the object is a GepObjVar, add the offset from the base object + // like llvm bc `arr = alloc i8 12; p = gep arr, 4; q = gep p, 6` + // we retreive {p, 4} and write {q, 4+6} + const GepObjVar* objVar = SVFUtil::cast(obj); + for (const auto& gepAddr : gepAddrs) + { + NodeID gepObj = as.getIDFromAddr(gepAddr); + if (const GepObjVar* gepObjVar = SVFUtil::dyn_cast(svfir->getGNode(gepObj))) + { + if (hasGepObjOffsetFromBase(objVar)) + { + IntervalValue objOffsetFromBase = + getGepObjOffsetFromBase(objVar); + if (!hasGepObjOffsetFromBase(gepObjVar)) + addToGepObjOffsetFromBase( + gepObjVar, objOffsetFromBase + offset); + } + else + { + assert(false && + "GEP RHS object has no offset from base"); + } + } + else + { + assert(AbstractState::isInvalidMem(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address"); + } + } + } + } +} /** * @brief Handles stub functions within the ICFG node. @@ -152,7 +235,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().getAbsStateFromTrace(callNode); u32_t size_id = callNode->getArgument(1)->getId(); IntervalValue val = as[size_id].getInterval(); if (val.isBottom()) @@ -304,6 +387,7 @@ void BufOverflowDetector::detectExtAPI(AbstractState& as, * * This function calculates the access offset for a base object or a sub-object of an * aggregate object (using GEP). If the object is a dummy object, it returns a top interval value. + * GEP offset information is now retrieved from AbstractState rather than the detector. * * @param as Reference to the abstract state. * @param objId The ID of the object. @@ -317,14 +401,15 @@ IntervalValue BufOverflowDetector::getAccessOffset(SVF::AbstractState& as, SVF:: if (SVFUtil::isa(obj)) { - // if the object is a BaseObjVar, return the byte offset directly - // like `int arr[10]; arr[5] = 1;` arr is the baseObjVar + // If the object is a BaseObjVar, return the byte offset directly + // Example: `int arr[10]; arr[5] = 1;` arr is the baseObjVar return as.getByteOffset(gep); } else if (SVFUtil::isa(obj)) { - // if the object is a GepObjVar, return the offset from the base object - // like `int arr[10]; int* p=arr+5; p[3] = 1`, p is the GepObjVar from arr. + // If the object is a GepObjVar, return the offset from the base object + // Example: `int arr[10]; int* p=arr+5; p[3] = 1`, p is the GepObjVar from arr. + // Use the detector's gepObjOffsetFromBase map return getGepObjOffsetFromBase(SVFUtil::cast(obj)) + as.getByteOffset(gep); } else @@ -334,76 +419,6 @@ IntervalValue BufOverflowDetector::getAccessOffset(SVF::AbstractState& as, SVF:: } } -/** - * @brief Updates the offset of a GEP object from its base. - * - * This function calculates and stores the offset of a GEP object from its base object - * using the addresses and offsets provided. - * - * @param gepAddrs The addresses of the GEP objects. - * @param objAddrs The addresses of the base objects. - * @param offset The interval value of the offset. - */ -void BufOverflowDetector::updateGepObjOffsetFromBase(AbstractState& as, SVF::AddressValue gepAddrs, SVF::AddressValue objAddrs, SVF::IntervalValue offset) -{ - SVFIR* svfir = PAG::getPAG(); - - for (const auto& objAddr : objAddrs) - { - NodeID objId = as.getIDFromAddr(objAddr); - auto obj = svfir->getGNode(objId); - - if (SVFUtil::isa(obj)) - { - // if the object is a BaseObjVar, add the offset directly - // like llvm bc `arr = alloc i8 12; p = gep arr, 4` - // we write key value pair {gep, 4} - for (const auto& gepAddr : gepAddrs) - { - NodeID gepObj = as.getIDFromAddr(gepAddr); - if (const GepObjVar* gepObjVar = SVFUtil::dyn_cast(svfir->getGNode(gepObj))) - { - addToGepObjOffsetFromBase(gepObjVar, offset); - } - else - { - assert(AbstractState::isInvalidMem(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address"); - } - } - } - else if (SVFUtil::isa(obj)) - { - // if the object is a GepObjVar, add the offset from the base object - // like llvm bc `arr = alloc i8 12; p = gep arr, 4; q = gep p, 6` - // we retreive {p, 4} and write {q, 4+6} - const GepObjVar* objVar = SVFUtil::cast(obj); - for (const auto& gepAddr : gepAddrs) - { - NodeID gepObj = as.getIDFromAddr(gepAddr); - if (const GepObjVar* gepObjVar = SVFUtil::dyn_cast(svfir->getGNode(gepObj))) - { - if (hasGepObjOffsetFromBase(objVar)) - { - IntervalValue objOffsetFromBase = - getGepObjOffsetFromBase(objVar); - if (!hasGepObjOffsetFromBase(gepObjVar)) - addToGepObjOffsetFromBase( - gepObjVar, objOffsetFromBase + offset); - } - else - { - assert(false && - "GEP RHS object has no offset from base"); - } - } - else - { - assert(AbstractState::isInvalidMem(gepAddr) && "GEP object is neither a GepObjVar nor an invalid memory address"); - } - } - } - } -} /** * @brief Detects buffer overflow in 'strcpy' function calls. @@ -419,6 +434,7 @@ bool BufOverflowDetector::detectStrcpy(AbstractState& as, const CallICFGNode *ca { const SVFVar* arg0Val = call->getArgument(0); const SVFVar* arg1Val = call->getArgument(1); + // Cast to dense AbstractState for AbsExtAPI compatibility IntervalValue strLen = AbstractInterpretation::getAEInstance().getUtils()->getStrlen(as, arg1Val); return canSafelyAccessMemory(as, arg0Val, strLen); } @@ -467,7 +483,8 @@ bool BufOverflowDetector::detectStrcat(AbstractState& as, const CallICFGNode *ca * @brief Checks if a memory access is safe given a specific buffer length. * * This function ensures that a given memory access, starting at a specific value, - * does not exceed the allocated size of the buffer. + * does not exceed the allocated size of the buffer. GEP offset information is now + * retrieved from AbstractState rather than the detector. * * @param as Reference to the abstract state. * @param value Pointer to the SVF var. @@ -503,7 +520,7 @@ bool BufOverflowDetector::canSafelyAccessMemory(AbstractState& as, const SVF::SV } IntervalValue offset(0); - // if the object is a GepObjVar, get the offset from the base object + // if the object is a GepObjVar, get the offset from the base object (using detector's map) if (SVFUtil::isa(svfir->getGNode(objId))) { offset = getGepObjOffsetFromBase(SVFUtil::cast(svfir->getGNode(objId))) + len; @@ -568,20 +585,23 @@ void NullptrDerefDetector::detect(AbstractState& as, const ICFGNode* node) void NullptrDerefDetector::handleStubFunctions(const CallICFGNode* callNode) { - std::string funcName = callNode->getCalledFunction()->getName(); - if (funcName == "UNSAFE_LOAD") - { + std::string funcName; + if (callNode->getCalledFunction() == nullptr) { + return; + } else { + funcName = callNode->getCalledFunction()->getName(); + } + + if (funcName == "UNSAFE_LOAD") { // void UNSAFE_LOAD(void* ptr); AbstractInterpretation::getAEInstance().checkpoints.erase(callNode); - if (callNode->arg_size() < 1) + if (callNode->arg_size() < 1) { return; + } AbstractState& as = AbstractInterpretation::getAEInstance().getAbsStateFromTrace(callNode); - const SVFVar* arg0Val = callNode->getArgument(0); - // opt may directly dereference a null pointer and call UNSAFE_LOAD(null) bool isSafe = canSafelyDerefPtr(as, arg0Val) && arg0Val->getId() != 0; - if (!isSafe) - { + if (!isSafe) { SVFUtil::outs() << SVFUtil::sucMsg("success: expected null dereference at UNSAFE_LOAD") << " — " << callNode->toString() << "\n"; return; @@ -598,7 +618,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().getAbsStateFromTrace(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; diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index b3aed674f..28661ac53 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -561,18 +561,35 @@ void AbstractInterpretation::handleSingletonWTO(const ICFGSingletonWTO *icfgSing std::deque worklist; stat->getICFGNodeTrace()++; - // handle SVF Stmt + + // SINGLE TRAVERSAL: Update state and detect together! + // This eliminates redundant statement traversals that were previously + // happening in both the main loop and each detector's detect() method. for (const SVFStmt *stmt: node->getSVFStmts()) { + // 1. Update abstract state handleSVFStatement(stmt); + + // 2. Immediately detect issues in the same loop + for (auto& detector: detectors) + detector->checkStatement(stmt, getAbsStateFromTrace(node)); } - // inlining the callee by calling handleFunc for the callee function + + // Handle call sites (inlining the callee) if (const CallICFGNode* callnode = SVFUtil::dyn_cast(node)) { handleCallSite(callnode); + + // Check stub functions (e.g., SAFE_BUFACCESS, UNSAFE_BUFACCESS) + for (auto& detector : detectors) + detector->handleStubFunctions(callnode); } + + // Handle external API calls at call nodes (kept for backward compatibility) + // The detect() method now only handles external API calls, not GEP statements for (auto& detector: detectors) detector->detect(getAbsStateFromTrace(node), node); + stat->countStateSize(); } diff --git a/svf/lib/Util/Options.cpp b/svf/lib/Util/Options.cpp index a783ce4e9..8fe5b265a 100644 --- a/svf/lib/Util/Options.cpp +++ b/svf/lib/Util/Options.cpp @@ -802,6 +802,9 @@ const OptionMap Options::HandleRecur( } } ); +// TODO: to implement +// const Option Options::UseSparseState( +// "use-sparse", "Use sparse abstract state representation (experimental)", false); const Option Options::Timeout( "timeout", "time out (seconds), set -1 (no timeout), default 14400s",14400); const Option Options::OutputName(