Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nullptr dereference detector #1541

Draft
wants to merge 23 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6c0914d
initial implementation of NullPtrDerefDetector
ethanLin520 Aug 26, 2024
4dcf995
added cmd line option for nullptrdereference
ethanLin520 Aug 28, 2024
fcccab9
implemented correct bug reporting
ethanLin520 Aug 28, 2024
8b3b350
refactor bug recording
ethanLin520 Aug 28, 2024
a6cc771
improve error message
ethanLin520 Aug 29, 2024
6916726
refactor nullptr vaddr to 0x7f000000
ethanLin520 Sep 1, 2024
188ad3f
remove unused code
ethanLin520 Sep 1, 2024
1c5293e
improve comment
ethanLin520 Sep 1, 2024
5f8db38
Merge remote-tracking branch 'root/master' into nullderef
ethanLin520 Sep 2, 2024
1296dba
modified recordBug in NullPtrDerefDetector
ethanLin520 Sep 9, 2024
4ef473b
added stub function for UNSAFE_LOAD & SAFE_LOAD
ethanLin520 Sep 11, 2024
69cf9c3
added isUninit function
ethanLin520 Sep 11, 2024
59d0663
added canSafelyDerefPtr for the stub functions
ethanLin520 Sep 11, 2024
1174630
handle nullptr in printAbstractState
Oct 8, 2024
497d2d9
simplify option naming
Oct 10, 2024
5e9104e
Merge remote-tracking branch 'upstream/master' into nullderef
ethanLin520 Oct 11, 2024
aeeccec
refactor nullptr detector accordingly
ethanLin520 Oct 11, 2024
9db0b03
refactor nullptr detector
ethanLin520 Oct 11, 2024
4812c9b
simplfied detect
ethanLin520 Oct 14, 2024
dbd5194
refactor nullptr to use blackhole address instead of 0
ethanLin520 Oct 14, 2024
af7460b
simplified canSafelyDerefPtr
ethanLin520 Oct 14, 2024
6db9e72
Merge branch 'upstream/master' into nullderef
ethanLin520 Oct 19, 2024
47be4ab
refactored getGepObjAddrs: remove if statement for nullptr
ethanLin520 Oct 27, 2024
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
2 changes: 2 additions & 0 deletions svf-llvm/tools/AE/ae.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,8 @@ int main(int argc, char** argv)
AbstractInterpretation& ae = AbstractInterpretation::getAEInstance();
if (Options::BufferOverflowCheck())
ae.addDetector(std::make_unique<BufOverflowDetector>());
if (Options::NullPtrDereference())
ae.addDetector(std::make_unique<NullPtrDerefDetector>());
ae.runOnModule(pag->getICFG());

LLVMModuleSet::releaseLLVMModuleSet();
Expand Down
2 changes: 1 addition & 1 deletion svf/include/AE/Core/AddressValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

#define AddressMask 0x7f000000
#define FlippedAddressMask (AddressMask^0xffffffff)
// the address of the black hole, getVirtualMemAddress(2);
// the address of the black hole, getVirtualMemAddress(2); Also used to represent nullptr.
#define BlackHoleAddr 0x7f000000 + 2

#include "Util/GeneralType.h"
Expand Down
117 changes: 116 additions & 1 deletion svf/include/AE/Svfexe/AEDetector.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ class AEDetector
enum DetectorKind
{
BUF_OVERFLOW, ///< Detector for buffer overflow issues.
UNKNOWN, ///< Default type if the kind is not specified.
NULLPTR_DEREF, ///< Detector for null pointer dereference issues.
UNKNOWN, ///< Default type if the kind is not specified.
};

/**
Expand Down Expand Up @@ -320,4 +321,118 @@ class BufOverflowDetector : public AEDetector
SVFBugReport recoder; ///< Recorder for abstract execution bugs.
Map<const ICFGNode*, std::string> nodeToBugInfo; ///< Maps ICFG nodes to bug information.
};


/**
* @class NullPtrDerefDetector
* @brief Detector for identifying null pointer dereference issues.
*/
class NullPtrDerefDetector: public AEDetector {
friend class AbstractInterpretation;
public:
/**
* @brief Constructor initializes the detector kind to NULLPTR_DEREF.
*/
NullPtrDerefDetector()
{
kind = NULLPTR_DEREF;
}

/**
* @brief Destructor.
*/
~NullPtrDerefDetector() = default;

/**
* @brief Check if the detector is of the NULLPTR_DEREF kind.
* @param detector Pointer to the detector.
* @return True if the detector is of type NULLPTR_DEREF, false otherwise.
*/
static bool classof(const AEDetector* detector)
{
return detector->getKind() == AEDetector::NULLPTR_DEREF;
}


/**
* @brief Detect null pointer dereferece issues within a node.
* @param as Reference to the abstract state.
* @param node Pointer to the ICFG node.
*/
void detect(AbstractState& as, const ICFGNode* node);

/**
* @brief Handles external API calls related to null pointer dereference detection.
* @param call Pointer to the call ICFG node.
*/
void handleStubFunctions(const CallICFGNode*);

/**
* @brief Check if an Abstract Value is NULL (or uninitialized).
*
* @param v An Abstract Value of loaded from an address in an Abstract State.
*/
bool isNull(AbstractValue v) {
bool is = (v.getInterval().isBottom() && v.getAddrs().isBottom()) ||
v.getAddrs().contains(BlackHoleAddr);
return is;
}

/**
* @brief Check if an Abstract Value is uninitialized.
*
* @param v An Abstract Value in an Abstract State.
*/
bool isUninit(AbstractValue v) {
bool is = (v.getInterval().isBottom() && v.getAddrs().isBottom());
return is;
}

/**
* @brief Reports all detected null pointer dereference bugs.
*/
void reportBug()
{
std::cerr << "###################### Null Pointer Dereference (" + std::to_string(stmtToBugInfo.size())
+ " found) ######################\n";
std::cerr << "---------------------------------------------\n";
for (const auto& it : stmtToBugInfo) {
const SVFStmt *s = it.first;
std::cerr << "Location: " << s->toString() << "\n---------------------------------------------\n";
}
}

/**
* @brief record a bug.
* @param stmt The SVF statement that triggers the null pointer dereference.
*/
void recordBug(const SVFStmt *stmt)
{
const ICFGNode *eventInst = stmt->getICFGNode();
SVFBugEvent sourceInstEvent(SVFBugEvent::EventType::SourceInst, eventInst);

std::string loc = sourceInstEvent.getEventLoc(); // Get the location of the last event in the stack

if (loc == "") {
stmtToBugInfo[stmt] = loc;
} else if (bugLoc.find(loc) == bugLoc.end()) {
bugLoc.insert(loc);
stmtToBugInfo[stmt] = loc;
}
}

/**
* @brief Checks if pointer can be safely dereferenced.
* @param as Reference to the abstract state.
* @param value Pointer to the SVF value.
* @return True if the pointer dereference is safe, false otherwise.
*/
bool canSafelyDerefPtr(AbstractState& as, const SVF::SVFVar* value);


private:
Set<std::string> bugLoc; ///< Set of locations where bugs have been reported.
Map<const SVFStmt*, std::string> stmtToBugInfo; ///< Maps SVF stmt to bug information.
};

}
2 changes: 1 addition & 1 deletion svf/include/AE/Svfexe/AbstractInterpretation.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class AbstractInterpretation
friend class AEStat;
friend class AEAPI;
friend class BufOverflowDetector;

friend class NullPtrDerefDetector;
public:
typedef SCCDetection<CallGraph*> CallGraphSCC;
/// Constructor
Expand Down
2 changes: 2 additions & 0 deletions svf/include/Util/Options.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,8 @@ class Options
static const Option<std::string> OutputName;
/// buffer overflow checker, Default: false
static const Option<bool> BufferOverflowCheck;
/// null pointer dereference checker, Default: false
static const Option<bool> NullPtrDereference;
/// memory leak check, Default: false
static const Option<bool> MemoryLeakCheck;
/// file open close checker, Default: false
Expand Down
94 changes: 94 additions & 0 deletions svf/lib/AE/Svfexe/AEDetector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -502,3 +502,97 @@ bool BufOverflowDetector::canSafelyAccessMemory(AbstractState& as, const SVF::SV
}
return true;
}

/**
* @brief Detects null pointer dereference issues within a given ICFG node.
*
* @param as Reference to the abstract state.
* @param node Pointer to the ICFG node.
*/
void NullPtrDerefDetector::detect(AbstractState& as, const ICFGNode* node) {
for (const SVFStmt* stmt : node->getSVFStmts()) {
if (const LoadStmt* load = SVFUtil::dyn_cast<LoadStmt>(stmt)) {
if (!canSafelyDerefPtr(as, load->getRHSVar())) {
recordBug(load);
}
} else if (const StoreStmt* store = SVFUtil::dyn_cast<StoreStmt>(stmt)) {
if (!canSafelyDerefPtr(as, store->getLHSVar())) {
recordBug(store);
}
}
}
}

/**
* @brief Checks if pointer can be safely dereferenced.
* @param as Reference to the abstract state.
* @param value Pointer to the SVF value of the pointer being dereferenced.
* @return True if the pointer dereference is safe, false otherwise.
*/
bool NullPtrDerefDetector::canSafelyDerefPtr(AbstractState& as, const SVF::SVFVar* value) {
NodeID value_id = value->getId();
if (isUninit(as[value_id])) return false;
if (!as[value_id].isAddr()) return true; // Loading an Interval Value
AbstractValue &addrs = as[value_id];
for (const auto &addr: addrs.getAddrs()) {
if (isNull(as.load(addr)))
return false;
}
return true;
}


/**
* @brief Handles stub functions within the ICFG node.
*
* @param callNode Pointer to the ICFG node.
*/
void NullPtrDerefDetector::handleStubFunctions(const SVF::CallICFGNode* callNode)
{
// get function name
std::string funcName = callNode->getCalledFunction()->getName();
if (funcName == "UNSAFE_LOAD")
{
// void UNSAFE_LOAD(void* ptr);
AbstractInterpretation::getAEInstance().checkpoints.erase(callNode);
if (callNode->arg_size() < 1)
return;
AbstractState& as =
AbstractInterpretation::getAEInstance().getAbsStateFromTrace(
callNode);

const SVFVar* arg0Val = callNode->getArgument(0);
bool isSafe = canSafelyDerefPtr(as, arg0Val);
if (!isSafe) {
std::cout << "detect null pointer deference success: " << callNode->toString() << std::endl;
return;
}
else
{
std::string err_msg = "this UNSAFE_LOAD should be a null pointer dereference but not detected. Pos: ";
err_msg += callNode->getSourceLoc();
std::cerr << err_msg << std::endl;
assert(false);
}
}
else if (funcName == "SAFE_LOAD")
{
// void SAFE_LOAD(void* ptr);
AbstractInterpretation::getAEInstance().checkpoints.erase(callNode);
if (callNode->arg_size() < 1) return;
AbstractState&as = AbstractInterpretation::getAEInstance().getAbsStateFromTrace(callNode);
const SVFVar* arg0Val = callNode->getArgument(0);
bool isSafe = canSafelyDerefPtr(as, arg0Val);
if (isSafe) {
std::cout << "safe load pointer success: " << callNode->toString() << std::endl;
return;
}
else
{
std::string err_msg = "this SAFE_LOAD should be a safe but a null pointer dereference detected. Pos: ";
err_msg += callNode->getSourceLoc();
std::cerr << err_msg << std::endl;
assert(false);
}
}
}
11 changes: 10 additions & 1 deletion svf/lib/AE/Svfexe/AbstractInterpretation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ void AbstractInterpretation::handleGlobalNode()
{
const ICFGNode* node = icfg->getGlobalICFGNode();
abstractTrace[node] = AbstractState();
abstractTrace[node][SymbolTableInfo::NullPtr] = AddressValue();
abstractTrace[node][SymbolTableInfo::NullPtr] = AddressValue(BlackHoleAddr); // Use BlackHoleAddr to represent nullptr
// Global Node, we just need to handle addr, load, store, copy and gep
for (const SVFStmt *stmt: node->getSVFStmts())
{
Expand Down Expand Up @@ -917,6 +917,7 @@ void AbstractInterpretation::collectCheckPoint()
// traverse every ICFGNode
Set<std::string> ae_checkpoint_names = {"svf_assert"};
Set<std::string> buf_checkpoint_names = {"UNSAFE_BUFACCESS", "SAFE_BUFACCESS"};
Set<std::string> nullptr_checkpoint_names = {"UNSAFE_LOAD", "SAFE_LOAD"};

for (auto it = svfir->getICFG()->begin(); it != svfir->getICFG()->end(); ++it)
{
Expand All @@ -938,6 +939,14 @@ void AbstractInterpretation::collectCheckPoint()
checkpoints.insert(call);
}
}
if (Options::NullPtrDereference())
{
if (nullptr_checkpoint_names.find(fun->getName()) !=
nullptr_checkpoint_names.end())
{
checkpoints.insert(call);
}
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions svf/lib/Util/Options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,8 @@ const Option<std::string> Options::OutputName(
"output","output db file","output.db");
const Option<bool> Options::BufferOverflowCheck(
"overflow","Buffer Overflow Detection",false);
const Option<bool> Options::NullPtrDereference(
"nullptr","Null Pointer Dereference Detection",false);
const Option<bool> Options::MemoryLeakCheck(
"leak", "Memory Leak Detection",false);
const Option<bool> Options::FileCheck(
Expand Down