From c0d4d5861cfbc2b15d38123b03c037eaac29e49f Mon Sep 17 00:00:00 2001 From: Sergey Morozov Date: Mon, 19 Feb 2024 18:13:18 +0100 Subject: [PATCH 1/4] [feat] Added error classification on 'confident' and 'possible'. --- include/klee/Core/TerminationTypes.h | 2 + lib/Core/ExecutionState.h | 3 + lib/Core/Executor.cpp | 112 ++++++++++++++++++++------- lib/Core/Executor.h | 9 ++- lib/Core/SpecialFunctionHandler.cpp | 39 +++++++--- lib/Support/ErrorHandling.cpp | 9 ++- 6 files changed, 130 insertions(+), 44 deletions(-) diff --git a/include/klee/Core/TerminationTypes.h b/include/klee/Core/TerminationTypes.h index a08b3585e7..8f8ca41c2f 100644 --- a/include/klee/Core/TerminationTypes.h +++ b/include/klee/Core/TerminationTypes.h @@ -85,6 +85,8 @@ enum class StateTerminationType : std::uint8_t { /// \endcond }; +enum StateTerminationConfidenceCategory { CONFIDENT, PROBABLY }; + namespace HaltExecution { enum Reason { NotHalt = 0, diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index 3dc0b3bff7..c3191d5ca8 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -317,6 +317,9 @@ class ExecutionState { // Overall state of the state - Data specific + /// @brief: TODO: + bool lastBrConfidently = true; + /// @brief Exploration depth, i.e., number of times KLEE branched for this /// state std::uint32_t depth = 0; diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index eca2e57d1e..778998681e 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -40,6 +40,7 @@ #include "klee/Config/config.h" #include "klee/Core/Interpreter.h" #include "klee/Core/MockBuilder.h" +#include "klee/Core/TerminationTypes.h" #include "klee/Expr/ArrayExprOptimizer.h" #include "klee/Expr/ArrayExprVisitor.h" #include "klee/Expr/Assignment.h" @@ -2724,6 +2725,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { if (bi->getMetadata("md.ret")) { state.stack.forceReturnLocation(locationOf(state)); } + state.lastBrConfidently = true; transferToBasicBlock(bi->getSuccessor(0), bi->getParent(), state); } else { @@ -2743,6 +2745,11 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { maxNewStateStackSize = std::max(maxNewStateStackSize, branches.first->stack.stackRegisterSize() * 8); + branches.first->lastBrConfidently = false; + branches.second->lastBrConfidently = false; + } else { + (branches.first ? branches.first : branches.second)->lastBrConfidently = + true; } // NOTE: There is a hidden dependency here, markBranchVisited @@ -4014,9 +4021,11 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { if (iIdx >= vt->getNumElements()) { // Out of bounds write terminateStateOnProgramError( - state, new ErrorEvent(locationOf(state), - StateTerminationType::BadVectorAccess, - "Out of bounds write when inserting element")); + state, + new ErrorEvent(locationOf(state), + StateTerminationType::BadVectorAccess, + "Out of bounds write when inserting element"), + StateTerminationConfidenceCategory::CONFIDENT); return; } @@ -4057,9 +4066,11 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { if (iIdx >= vt->getNumElements()) { // Out of bounds read terminateStateOnProgramError( - state, new ErrorEvent(locationOf(state), - StateTerminationType::BadVectorAccess, - "Out of bounds read when extracting element")); + state, + new ErrorEvent(locationOf(state), + StateTerminationType::BadVectorAccess, + "Out of bounds read when extracting element"), + StateTerminationConfidenceCategory::CONFIDENT); return; } @@ -4963,28 +4974,41 @@ void Executor::terminateStateOnTargetError(ExecutionState &state, terminationType = StateTerminationType::User; } terminateStateOnProgramError( - state, new ErrorEvent(locationOf(state), terminationType, messaget)); + state, new ErrorEvent(locationOf(state), terminationType, messaget), + StateTerminationConfidenceCategory::CONFIDENT); } -void Executor::terminateStateOnError(ExecutionState &state, - const llvm::Twine &messaget, - StateTerminationType terminationType, - const llvm::Twine &info, - const char *suffix) { +void Executor::terminateStateOnError( + ExecutionState &state, const llvm::Twine &messaget, + StateTerminationType terminationType, + StateTerminationConfidenceCategory confidence, const llvm::Twine &info, + const char *suffix) { std::string message = messaget.str(); - static std::set> emittedErrors; + static std::set< + std::pair>> + emittedErrors; const KInstruction *ki = getLastNonKleeInternalInstruction(state); Instruction *lastInst = ki->inst(); - if ((EmitAllErrors || - emittedErrors.insert(std::make_pair(lastInst, message)).second) && + if ((EmitAllErrors || emittedErrors + .insert(std::make_pair( + lastInst, std::make_pair(confidence, message))) + .second) && shouldWriteTest(state, true)) { std::string filepath = ki->getSourceFilepath(); + + std::string prefix = + (confidence == StateTerminationConfidenceCategory::CONFIDENT + ? "ERROR" + : "POSSIBLE ERROR"); + if (!filepath.empty()) { - klee_message("ERROR: %s:%zu: %s", filepath.c_str(), ki->getLine(), - message.c_str()); + klee_message((prefix + ": %s:%zu: %s").c_str(), filepath.c_str(), + ki->getLine(), message.c_str()); } else { - klee_message("ERROR: (location information missing) %s", message.c_str()); + klee_message((prefix + ": (location information missing) %s").c_str(), + message.c_str()); } if (!EmitAllErrors) klee_message("NOTE: now ignoring this error at this location"); @@ -5030,13 +5054,14 @@ void Executor::terminateStateOnExecError(ExecutionState &state, assert(reason > StateTerminationType::USERERR && reason <= StateTerminationType::EXECERR); ++stats::terminationExecutionError; - terminateStateOnError(state, message, reason, ""); + terminateStateOnError(state, message, reason, + StateTerminationConfidenceCategory::CONFIDENT, ""); } -void Executor::terminateStateOnProgramError(ExecutionState &state, - const ref &reason, - const llvm::Twine &info, - const char *suffix) { +void Executor::terminateStateOnProgramError( + ExecutionState &state, const ref &reason, + StateTerminationConfidenceCategory confidence, const llvm::Twine &info, + const char *suffix) { assert(reason->ruleID > StateTerminationType::SOLVERERR && reason->ruleID <= StateTerminationType::PROGERR); ++stats::terminationProgramError; @@ -5055,19 +5080,22 @@ void Executor::terminateStateOnProgramError(ExecutionState &state, } state.eventsRecorder.record(reason); - terminateStateOnError(state, reason->message, reason->ruleID, info, suffix); + terminateStateOnError(state, reason->message, reason->ruleID, confidence, + info, suffix); } void Executor::terminateStateOnSolverError(ExecutionState &state, const llvm::Twine &message) { ++stats::terminationSolverError; - terminateStateOnError(state, message, StateTerminationType::Solver, ""); + terminateStateOnError(state, message, StateTerminationType::Solver, + StateTerminationConfidenceCategory::CONFIDENT, ""); } void Executor::terminateStateOnUserError(ExecutionState &state, const llvm::Twine &message) { ++stats::terminationUserError; - terminateStateOnError(state, message, StateTerminationType::User, ""); + terminateStateOnError(state, message, StateTerminationType::User, + StateTerminationConfidenceCategory::CONFIDENT, ""); } // XXX shoot me @@ -5416,13 +5444,20 @@ void Executor::executeFree(ExecutionState &state, ref address, new ErrorEvent(new AllocEvent(mo->allocSite), locationOf(*it->second), StateTerminationType::Free, "free of alloca"), + rl.size() != 1 ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT, getAddressInfo(*it->second, address)); } else if (mo->isGlobal) { + if (rl.size() != 1) { + klee_warning("Following error if likely false positive"); + } terminateStateOnProgramError( *it->second, new ErrorEvent(new AllocEvent(mo->allocSite), locationOf(*it->second), StateTerminationType::Free, "free of global"), + rl.size() != 1 ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT, getAddressInfo(*it->second, address)); } else { it->second->removePointerResolutions(mo); @@ -5518,6 +5553,8 @@ bool Executor::resolveExact(ExecutionState &estate, ref address, *unbound, new ErrorEvent(locationOf(*unbound), StateTerminationType::Ptr, "memory error: invalid pointer: " + name), + !results.empty() ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT, getAddressInfo(*unbound, address)); } } @@ -5607,7 +5644,7 @@ void Executor::concretizeSize(ExecutionState &state, ref size, new ErrorEvent(locationOf(*hugeSize.second), StateTerminationType::Model, "concretized symbolic size"), - info.str()); + StateTerminationConfidenceCategory::CONFIDENT, info.str()); } } } @@ -6309,7 +6346,8 @@ void Executor::executeMemoryOperation( *state, new ErrorEvent(new AllocEvent(mo->allocSite), locationOf(*state), StateTerminationType::ReadOnly, - "memory error: object read only")); + "memory error: object read only"), + StateTerminationConfidenceCategory::CONFIDENT); } else { wos->write(mo->getOffsetExpr(address), value); } @@ -6371,6 +6409,10 @@ void Executor::executeMemoryOperation( return; } + bool mayBeFalsePositive = + resolvedMemoryObjects.size() > 1 || + (resolvedMemoryObjects.size() == 1 && mayBeOutOfBound); + ExecutionState *unbound = nullptr; if (MergedPointerDereference) { ref guard; @@ -6428,7 +6470,10 @@ void Executor::executeMemoryOperation( new ErrorEvent(new AllocEvent(mo->allocSite), locationOf(*branches.first), StateTerminationType::ReadOnly, - "memory error: object read only")); + "memory error: object read only"), + mayBeFalsePositive + ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT); state = branches.second; } else { ref result = SelectExpr::create( @@ -6500,7 +6545,10 @@ void Executor::executeMemoryOperation( *bound, new ErrorEvent(new AllocEvent(mo->allocSite), locationOf(*bound), StateTerminationType::ReadOnly, - "memory error: object read only")); + "memory error: object read only"), + mayBeFalsePositive + ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT); } else { wos->write(mo->getOffsetExpr(address), value); } @@ -6552,6 +6600,8 @@ void Executor::executeMemoryOperation( new ErrorEvent(new AllocEvent(baseObjectPair.first->allocSite), locationOf(*unbound), StateTerminationType::Ptr, "memory error: out of bound pointer"), + mayBeFalsePositive ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT, getAddressInfo(*unbound, address)); return; } @@ -6561,6 +6611,8 @@ void Executor::executeMemoryOperation( *unbound, new ErrorEvent(locationOf(*unbound), StateTerminationType::Ptr, "memory error: out of bound pointer"), + mayBeFalsePositive ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT, getAddressInfo(*unbound, address)); } } diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index a0a645eba3..b6a1b01c0e 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -618,6 +618,7 @@ class Executor : public Interpreter { /// below. void terminateStateOnError(ExecutionState &state, const llvm::Twine &message, StateTerminationType reason, + StateTerminationConfidenceCategory confidence, const llvm::Twine &longMessage = "", const char *suffix = nullptr); @@ -629,10 +630,10 @@ class Executor : public Interpreter { /// Call error handler and terminate state in case of program errors /// (e.g. free()ing globals, out-of-bound accesses) - void terminateStateOnProgramError(ExecutionState &state, - const ref &reason, - const llvm::Twine &longMessage = "", - const char *suffix = nullptr); + void terminateStateOnProgramError( + ExecutionState &state, const ref &reason, + StateTerminationConfidenceCategory confidence, + const llvm::Twine &longMessage = "", const char *suffix = nullptr); void terminateStateOnTerminator(ExecutionState &state); diff --git a/lib/Core/SpecialFunctionHandler.cpp b/lib/Core/SpecialFunctionHandler.cpp index ba762edd0b..a46345fad8 100644 --- a/lib/Core/SpecialFunctionHandler.cpp +++ b/lib/Core/SpecialFunctionHandler.cpp @@ -20,6 +20,7 @@ #include "TypeManager.h" #include "klee/Config/config.h" +#include "klee/Core/TerminationTypes.h" #include "klee/Module/KInstruction.h" #include "klee/Module/KModule.h" #include "klee/Solver/SolverCmdLine.h" @@ -30,6 +31,8 @@ #include "klee/klee.h" #include "klee/Support/CompilerWarning.h" +#include +#include DISABLE_WARNING_PUSH DISABLE_WARNING_DEPRECATED_DECLARATIONS #include "llvm/ADT/APFloat.h" @@ -343,8 +346,10 @@ void SpecialFunctionHandler::handleAbort(ExecutionState &state, std::vector> &arguments) { assert(arguments.size() == 0 && "invalid number of arguments to abort"); executor.terminateStateOnProgramError( - state, new ErrorEvent(executor.locationOf(state), - StateTerminationType::Abort, "abort failure")); + state, + new ErrorEvent(executor.locationOf(state), StateTerminationType::Abort, + "abort failure"), + StateTerminationConfidenceCategory::CONFIDENT); } void SpecialFunctionHandler::handleExit(ExecutionState &state, @@ -361,15 +366,23 @@ void SpecialFunctionHandler::handleSilentExit( executor.terminateStateEarlyUser(state, ""); } +static bool isAssertFailsConfidently(ExecutionState &state) { + return state.lastBrConfidently; +} + void SpecialFunctionHandler::handleAssert(ExecutionState &state, KInstruction *target, std::vector> &arguments) { assert(arguments.size() == 3 && "invalid number of arguments to _assert"); + executor.terminateStateOnProgramError( state, new ErrorEvent(executor.locationOf(state), StateTerminationType::Assert, "ASSERTION FAIL: " + - readStringAtAddress(state, arguments[0]))); + readStringAtAddress(state, arguments[0])), + isAssertFailsConfidently(state) + ? StateTerminationConfidenceCategory::CONFIDENT + : StateTerminationConfidenceCategory::PROBABLY); } void SpecialFunctionHandler::handleAssertFail( @@ -381,7 +394,10 @@ void SpecialFunctionHandler::handleAssertFail( state, new ErrorEvent(executor.locationOf(state), StateTerminationType::Assert, "ASSERTION FAIL: " + - readStringAtAddress(state, arguments[0]))); + readStringAtAddress(state, arguments[0])), + isAssertFailsConfidently(state) + ? StateTerminationConfidenceCategory::CONFIDENT + : StateTerminationConfidenceCategory::PROBABLY); } void SpecialFunctionHandler::handleReportError( @@ -396,7 +412,8 @@ void SpecialFunctionHandler::handleReportError( new ErrorEvent(executor.locationOf(state), StateTerminationType::ReportError, readStringAtAddress(state, arguments[2])), - "", readStringAtAddress(state, arguments[3]).c_str()); + StateTerminationConfidenceCategory::CONFIDENT, "", + readStringAtAddress(state, arguments[3]).c_str()); } void SpecialFunctionHandler::handleNew(ExecutionState &state, @@ -840,6 +857,7 @@ void SpecialFunctionHandler::handleCheckMemoryAccess( state, new ErrorEvent(executor.locationOf(state), StateTerminationType::Ptr, "check_memory_access: memory error"), + StateTerminationConfidenceCategory::CONFIDENT, executor.getAddressInfo(state, address)); } else { const MemoryObject *mo = state.addressSpace.findObject(idObject).first; @@ -851,6 +869,7 @@ void SpecialFunctionHandler::handleCheckMemoryAccess( new ErrorEvent( new AllocEvent(mo->allocSite), executor.locationOf(state), StateTerminationType::Ptr, "check_memory_access: memory error"), + StateTerminationConfidenceCategory::CONFIDENT, executor.getAddressInfo(state, address)); } } @@ -1122,8 +1141,9 @@ void SpecialFunctionHandler::handleSetConcreteRoundingMode( llvm::APFloat::rmNearestTiesToEven; ref roundingModeArg = arguments[0]; if (!isa(roundingModeArg)) { - executor.terminateStateOnError(state, "argument should be concrete", - StateTerminationType::User); + executor.terminateStateOnError( + state, "argument should be concrete", StateTerminationType::User, + StateTerminationConfidenceCategory::CONFIDENT); return; } const ConstantExpr *CE = dyn_cast(roundingModeArg); @@ -1144,8 +1164,9 @@ void SpecialFunctionHandler::handleSetConcreteRoundingMode( newRoundingMode = llvm::APFloat::rmTowardZero; break; default: - executor.terminateStateOnError(state, "Invalid rounding mode", - StateTerminationType::User); + executor.terminateStateOnError( + state, "Invalid rounding mode", StateTerminationType::User, + StateTerminationConfidenceCategory::CONFIDENT); return; } state.roundingMode = newRoundingMode; diff --git a/lib/Support/ErrorHandling.cpp b/lib/Support/ErrorHandling.cpp index 685ac5c52b..2318a78bc7 100644 --- a/lib/Support/ErrorHandling.cpp +++ b/lib/Support/ErrorHandling.cpp @@ -32,6 +32,7 @@ FILE *klee::klee_message_file = NULL; static const char *warningPrefix = "WARNING"; static const char *warningOncePrefix = "WARNING ONCE"; static const char *errorPrefix = "ERROR"; +static const char *possibleErrorPrefix = "POSSIBLE ERROR"; static const char *notePrefix = "NOTE"; namespace klee { @@ -80,12 +81,18 @@ static void klee_vfmessage(FILE *fp, const char *pfx, const char *msg, /*bold=*/true, /*bg=*/false); - // Errors + // True-Postive Errors if (shouldSetColor(pfx, msg, errorPrefix)) fdos.changeColor(llvm::raw_ostream::RED, /*bold=*/true, /*bg=*/false); + // Non True-Positive Errors + if (shouldSetColor(pfx, msg, possibleErrorPrefix)) + fdos.changeColor(llvm::raw_ostream::YELLOW, + /*bold=*/true, + /*bg=*/false); + // Notes if (shouldSetColor(pfx, msg, notePrefix)) fdos.changeColor(llvm::raw_ostream::WHITE, From b79686c336498a8d3849aa205e8cf4fcef5df921 Mon Sep 17 00:00:00 2001 From: Sergey Morozov Date: Tue, 20 Feb 2024 18:39:01 +0300 Subject: [PATCH 2/4] [feat] Distinct kinds of NPE. --- lib/Core/ExecutionState.cpp | 4 +- lib/Core/ExecutionState.h | 3 ++ lib/Core/Executor.cpp | 89 +++++++++++++++++++++++++++++++++++-- lib/Expr/ExprUtil.cpp | 4 ++ 4 files changed, 95 insertions(+), 5 deletions(-) diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index fa14493607..873188506c 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -160,7 +160,9 @@ ExecutionState::~ExecutionState() { ExecutionState::ExecutionState(const ExecutionState &state) : initPC(state.initPC), pc(state.pc), prevPC(state.prevPC), stack(state.stack), stackBalance(state.stackBalance), - incomingBBIndex(state.incomingBBIndex), depth(state.depth), + incomingBBIndex(state.incomingBBIndex), + lastBrConfidently(state.lastBrConfidently), + outOfMemoryMarkers(state.outOfMemoryMarkers), depth(state.depth), level(state.level), addressSpace(state.addressSpace), constraints(state.constraints), eventsRecorder(state.eventsRecorder), targetForest(state.targetForest), pathOS(state.pathOS), diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index c3191d5ca8..c593e2c05e 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -320,6 +320,9 @@ class ExecutionState { /// @brief: TODO: bool lastBrConfidently = true; + /// @brief: TODO: + ImmutableList> outOfMemoryMarkers; + /// @brief Exploration depth, i.e., number of times KLEE branched for this /// state std::uint32_t depth = 0; diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 778998681e..9b7235b6a3 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -62,6 +62,7 @@ #include "klee/Solver/Common.h" #include "klee/Solver/Solver.h" #include "klee/Solver/SolverCmdLine.h" +#include "klee/Solver/SolverUtil.h" #include "klee/Statistics/TimerStatIncrementer.h" #include "klee/Support/Casting.h" #include "klee/Support/ErrorHandling.h" @@ -5398,6 +5399,7 @@ void Executor::executeAlloc(ExecutionState &state, ref size, bool isLocal, makeMockValue(state, "symCheckOutOfMemory", Expr::Bool); address = SelectExpr::create(symCheckOutOfMemoryExpr, Expr::createPointer(0), address); + state.outOfMemoryMarkers.push_back(symCheckOutOfMemoryExpr); } // state.addPointerResolution(address, mo); @@ -6267,12 +6269,91 @@ void Executor::executeMemoryOperation( StatePair branches = forkInternal(estate, Expr::createIsZero(base), BranchType::MemOp); ExecutionState *bound = branches.first; + if (bound) { - auto error = isReadFromSymbolicArray(uniqueBase) - ? ReachWithError::MayBeNullPointerException - : ReachWithError::MustBeNullPointerException; - terminateStateOnTargetError(*bound, error); + // If there are no markers on `malloc` returning nullptr, + // then `confidence` depends on presence of `unbound` state. + if (!bound->outOfMemoryMarkers.empty()) { + // bound constraints already contain `Expr::createIsZero()` + std::vector markersArrays; + markersArrays.reserve(bound->outOfMemoryMarkers.size()); + findSymbolicObjects(bound->outOfMemoryMarkers.begin(), + bound->outOfMemoryMarkers.end(), markersArrays); + + // Do some iterations (2-3) to figure out if error is confident. + ref allExcludedVectorsOfMarkers = Expr::createTrue(); + + bool convinced = false; + for (int tpCheckIteration = 0; tpCheckIteration < 2; ++tpCheckIteration) { + ref isConfidentResponse; + if (!solver->getResponse(bound->constraints.cs(), + allExcludedVectorsOfMarkers, + isConfidentResponse, bound->queryMetaData)) { + terminateStateOnSolverError(*bound, "Query timeout"); + } + + if (isa(isConfidentResponse)) { + reportStateOnTargetError(*bound, + ReachWithError::MustBeNullPointerException); + + terminateStateOnProgramError( + *bound, + new ErrorEvent(locationOf(*bound), StateTerminationType::Ptr, + "memory error: null pointer exception"), + StateTerminationConfidenceCategory::CONFIDENT); + convinced = true; + break; + } + + // Receive current values of markers + std::vector> boundSetSolution; + isConfidentResponse->tryGetInitialValuesFor(markersArrays, + boundSetSolution); + Assignment nonConfidentResponseAssignment(markersArrays, + boundSetSolution); + + // Exclude this combinations of markers + + ref conjExcludedVectorOfMarkers = Expr::createTrue(); + for (ref marker : bound->outOfMemoryMarkers) { + conjExcludedVectorOfMarkers = AndExpr::create( + conjExcludedVectorOfMarkers, + EqExpr::create(marker, + nonConfidentResponseAssignment.evaluate(marker))); + } + + allExcludedVectorsOfMarkers = + OrExpr::create(allExcludedVectorsOfMarkers, + NotExpr::create(conjExcludedVectorOfMarkers)); + } + + if (!convinced) { + reportStateOnTargetError(*bound, + ReachWithError::MayBeNullPointerException); + + terminateStateOnProgramError( + *bound, + new ErrorEvent(locationOf(*bound), StateTerminationType::Ptr, + "memory error: null pointer exception"), + StateTerminationConfidenceCategory::PROBABLY); + } + + } else { + auto error = branches.second != nullptr + ? ReachWithError::MayBeNullPointerException + : ReachWithError::MustBeNullPointerException; + reportStateOnTargetError(*bound, error); + + terminateStateOnProgramError( + *bound, + new ErrorEvent(locationOf(*bound), StateTerminationType::Ptr, + "memory error: null pointer exception"), + branches.second != nullptr + ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT); + } } + if (!branches.second) return; ExecutionState *state = branches.second; diff --git a/lib/Expr/ExprUtil.cpp b/lib/Expr/ExprUtil.cpp index 518e922dc8..0d94d022b3 100644 --- a/lib/Expr/ExprUtil.cpp +++ b/lib/Expr/ExprUtil.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "klee/Expr/ExprUtil.h" +#include "klee/ADT/ImmutableList.h" #include "klee/Expr/Expr.h" #include "klee/Expr/ExprHashMap.h" #include "klee/Expr/ExprVisitor.h" @@ -186,6 +187,9 @@ template void klee::findSymbolicObjects(B, B, std::vector &); typedef ExprHashSet::iterator C; template void klee::findSymbolicObjects(C, C, std::vector &); +typedef ImmutableList>::iterator D; +template void klee::findSymbolicObjects(D, D, std::vector &); + typedef std::vector>::iterator A; template void klee::findObjects(A, A, std::vector &); From a863e3957f0a0b38eaf489f3ac26ef981cdf0720 Mon Sep 17 00:00:00 2001 From: Sergey Morozov Date: Tue, 20 Feb 2024 19:15:26 +0300 Subject: [PATCH 3/4] [feat] Handling of NPE moved to separate function. --- lib/Core/Executor.cpp | 204 +++++++++++++++++++++--------------------- lib/Core/Executor.h | 3 + 2 files changed, 104 insertions(+), 103 deletions(-) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 9b7235b6a3..2dee00e49c 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -5471,6 +5471,100 @@ void Executor::executeFree(ExecutionState &state, ref address, } } +ExecutionState *Executor::handleNullPointerException(ExecutionState &state, + ref base) { + StatePair branches = + forkInternal(state, Expr::createIsZero(base), BranchType::MemOp); + ExecutionState *bound = branches.first; + if (!bound) { + return branches.second; + } + + // If there are no markers on `malloc` returning nullptr, + // then `confidence` depends on presence of `unbound` state. + if (!bound->outOfMemoryMarkers.empty()) { + // bound constraints already contain `Expr::createIsZero()` + std::vector markersArrays; + markersArrays.reserve(bound->outOfMemoryMarkers.size()); + findSymbolicObjects(bound->outOfMemoryMarkers.begin(), + bound->outOfMemoryMarkers.end(), markersArrays); + + // Do some iterations (2-3) to figure out if error is confident. + ref allExcludedVectorsOfMarkers = Expr::createTrue(); + + bool convinced = false; + for (int tpCheckIteration = 0; tpCheckIteration < 2; ++tpCheckIteration) { + ref isConfidentResponse; + if (!solver->getResponse(bound->constraints.cs(), + allExcludedVectorsOfMarkers, isConfidentResponse, + bound->queryMetaData)) { + terminateStateOnSolverError(*bound, "Query timeout"); + } + + if (isa(isConfidentResponse)) { + reportStateOnTargetError(*bound, + ReachWithError::MustBeNullPointerException); + + terminateStateOnProgramError( + *bound, + new ErrorEvent(locationOf(*bound), StateTerminationType::Ptr, + "memory error: null pointer exception"), + StateTerminationConfidenceCategory::CONFIDENT); + convinced = true; + break; + } + + // Receive current values of markers + std::vector> boundSetSolution; + isConfidentResponse->tryGetInitialValuesFor(markersArrays, + boundSetSolution); + Assignment nonConfidentResponseAssignment(markersArrays, + boundSetSolution); + + // Exclude this combinations of markers + + ref conjExcludedVectorOfMarkers = Expr::createTrue(); + for (ref marker : bound->outOfMemoryMarkers) { + conjExcludedVectorOfMarkers = AndExpr::create( + conjExcludedVectorOfMarkers, + EqExpr::create(marker, + nonConfidentResponseAssignment.evaluate(marker))); + } + + allExcludedVectorsOfMarkers = + OrExpr::create(allExcludedVectorsOfMarkers, + NotExpr::create(conjExcludedVectorOfMarkers)); + } + + if (!convinced) { + reportStateOnTargetError(*bound, + ReachWithError::MayBeNullPointerException); + + terminateStateOnProgramError( + *bound, + new ErrorEvent(locationOf(*bound), StateTerminationType::Ptr, + "memory error: null pointer exception"), + StateTerminationConfidenceCategory::PROBABLY); + } + + } else { + auto error = branches.second != nullptr + ? ReachWithError::MayBeNullPointerException + : ReachWithError::MustBeNullPointerException; + reportStateOnTargetError(*bound, error); + + terminateStateOnProgramError( + *bound, + new ErrorEvent(locationOf(*bound), StateTerminationType::Ptr, + "memory error: null pointer exception"), + branches.second != nullptr + ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT); + } + + return branches.second; +} + bool Executor::resolveExact(ExecutionState &estate, ref address, KType *type, ExactResolutionList &results, const std::string &name) { @@ -5496,20 +5590,12 @@ bool Executor::resolveExact(ExecutionState &estate, ref address, Simplificator::simplifyExpr(estate.constraints.cs(), base).simplified; uniqueBase = toUnique(estate, uniqueBase); - StatePair branches = - forkInternal(estate, Expr::createIsZero(base), BranchType::MemOp); - ExecutionState *bound = branches.first; - if (bound) { - auto error = isReadFromSymbolicArray(uniqueBase) - ? ReachWithError::MayBeNullPointerException - : ReachWithError::MustBeNullPointerException; - terminateStateOnTargetError(*bound, error); - } - if (!branches.second) { - address = Expr::createPointer(0); + ExecutionState *handledNPEState = handleNullPointerException(estate, base); + if (!handledNPEState) { + return false; } - ExecutionState &state = *branches.second; + ExecutionState &state = *handledNPEState; ResolutionList rl; bool mayBeOutOfBound = true; @@ -6266,97 +6352,9 @@ void Executor::executeMemoryOperation( ref uniqueBase = toUnique(estate, base); - StatePair branches = - forkInternal(estate, Expr::createIsZero(base), BranchType::MemOp); - ExecutionState *bound = branches.first; - - if (bound) { - // If there are no markers on `malloc` returning nullptr, - // then `confidence` depends on presence of `unbound` state. - if (!bound->outOfMemoryMarkers.empty()) { - // bound constraints already contain `Expr::createIsZero()` - std::vector markersArrays; - markersArrays.reserve(bound->outOfMemoryMarkers.size()); - findSymbolicObjects(bound->outOfMemoryMarkers.begin(), - bound->outOfMemoryMarkers.end(), markersArrays); - - // Do some iterations (2-3) to figure out if error is confident. - ref allExcludedVectorsOfMarkers = Expr::createTrue(); - - bool convinced = false; - for (int tpCheckIteration = 0; tpCheckIteration < 2; ++tpCheckIteration) { - ref isConfidentResponse; - if (!solver->getResponse(bound->constraints.cs(), - allExcludedVectorsOfMarkers, - isConfidentResponse, bound->queryMetaData)) { - terminateStateOnSolverError(*bound, "Query timeout"); - } - - if (isa(isConfidentResponse)) { - reportStateOnTargetError(*bound, - ReachWithError::MustBeNullPointerException); - - terminateStateOnProgramError( - *bound, - new ErrorEvent(locationOf(*bound), StateTerminationType::Ptr, - "memory error: null pointer exception"), - StateTerminationConfidenceCategory::CONFIDENT); - convinced = true; - break; - } - - // Receive current values of markers - std::vector> boundSetSolution; - isConfidentResponse->tryGetInitialValuesFor(markersArrays, - boundSetSolution); - Assignment nonConfidentResponseAssignment(markersArrays, - boundSetSolution); - - // Exclude this combinations of markers - - ref conjExcludedVectorOfMarkers = Expr::createTrue(); - for (ref marker : bound->outOfMemoryMarkers) { - conjExcludedVectorOfMarkers = AndExpr::create( - conjExcludedVectorOfMarkers, - EqExpr::create(marker, - nonConfidentResponseAssignment.evaluate(marker))); - } - - allExcludedVectorsOfMarkers = - OrExpr::create(allExcludedVectorsOfMarkers, - NotExpr::create(conjExcludedVectorOfMarkers)); - } - - if (!convinced) { - reportStateOnTargetError(*bound, - ReachWithError::MayBeNullPointerException); - - terminateStateOnProgramError( - *bound, - new ErrorEvent(locationOf(*bound), StateTerminationType::Ptr, - "memory error: null pointer exception"), - StateTerminationConfidenceCategory::PROBABLY); - } - - } else { - auto error = branches.second != nullptr - ? ReachWithError::MayBeNullPointerException - : ReachWithError::MustBeNullPointerException; - reportStateOnTargetError(*bound, error); - - terminateStateOnProgramError( - *bound, - new ErrorEvent(locationOf(*bound), StateTerminationType::Ptr, - "memory error: null pointer exception"), - branches.second != nullptr - ? StateTerminationConfidenceCategory::PROBABLY - : StateTerminationConfidenceCategory::CONFIDENT); - } - } - - if (!branches.second) + ExecutionState *state = handleNullPointerException(estate, base); + if (!state) return; - ExecutionState *state = branches.second; // fast path: single in-bounds resolution IDType idFastResult; @@ -6542,7 +6540,7 @@ void Executor::executeMemoryOperation( maxNewWriteableOSSize = std::max(maxNewWriteableOSSize, wos->getSparseStorageEntries()); if (wos->readOnly) { - branches = + StatePair branches = forkInternal(*state, Expr::createIsZero(unboundConditions[i]), BranchType::MemOp); assert(branches.first); diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index b6a1b01c0e..8780de8aef 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -413,6 +413,9 @@ class Executor : public Interpreter { const std::vector &resolveConcretizations, std::vector> &results); + ExecutionState *handleNullPointerException(ExecutionState &state, + ref base); + // do address resolution / object binding / out of bounds checking // and perform the operation void executeMemoryOperation(ExecutionState &state, bool isWrite, From 2538e1eb8afc7e9b64ed8aad0880f0de02e3fb0d Mon Sep 17 00:00:00 2001 From: Sergey Morozov Date: Wed, 21 Feb 2024 19:44:45 +0300 Subject: [PATCH 4/4] [feat] Added warning reports in SARIF. --- lib/Core/CodeEvent.h | 17 ++- lib/Core/ExecutionState.cpp | 2 +- lib/Core/ExecutionState.h | 2 +- lib/Core/Executor.cpp | 155 +++++++++++++++------------- lib/Core/Executor.h | 8 +- lib/Core/SpecialFunctionHandler.cpp | 39 +++---- 6 files changed, 122 insertions(+), 101 deletions(-) diff --git a/lib/Core/CodeEvent.h b/lib/Core/CodeEvent.h index f266208629..31962875df 100644 --- a/lib/Core/CodeEvent.h +++ b/lib/Core/CodeEvent.h @@ -152,6 +152,10 @@ struct ErrorEvent : public CodeEvent { /// @brief ID for this error. const StateTerminationType ruleID; + /// @brief Confidence of this error: may be considered + /// as a flag of `true positive` error for instance. + const StateTerminationConfidenceCategory confidence; + /// @brief Message describing this error. const std::string message; @@ -161,14 +165,17 @@ struct ErrorEvent : public CodeEvent { const std::optional> source; ErrorEvent(const ref &source, const ref &sink, - StateTerminationType ruleID, const std::string &message) - : CodeEvent(EventKind::ERR, sink), ruleID(ruleID), message(message), - source(source) {} + StateTerminationType ruleID, + StateTerminationConfidenceCategory confidence, + const std::string &message) + : CodeEvent(EventKind::ERR, sink), ruleID(ruleID), confidence(confidence), + message(message), source(source) {} ErrorEvent(const ref &sink, StateTerminationType ruleID, + StateTerminationConfidenceCategory confidence, const std::string &message) - : CodeEvent(EventKind::ERR, sink), ruleID(ruleID), message(message), - source(std::nullopt) {} + : CodeEvent(EventKind::ERR, sink), ruleID(ruleID), confidence(confidence), + message(message), source(std::nullopt) {} std::string description() const override { return message; } diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index 873188506c..352058f707 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -162,7 +162,7 @@ ExecutionState::ExecutionState(const ExecutionState &state) stack(state.stack), stackBalance(state.stackBalance), incomingBBIndex(state.incomingBBIndex), lastBrConfidently(state.lastBrConfidently), - outOfMemoryMarkers(state.outOfMemoryMarkers), depth(state.depth), + nullPointerMarkers(state.nullPointerMarkers), depth(state.depth), level(state.level), addressSpace(state.addressSpace), constraints(state.constraints), eventsRecorder(state.eventsRecorder), targetForest(state.targetForest), pathOS(state.pathOS), diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index c593e2c05e..76107ca2f2 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -321,7 +321,7 @@ class ExecutionState { bool lastBrConfidently = true; /// @brief: TODO: - ImmutableList> outOfMemoryMarkers; + ImmutableList> nullPointerMarkers; /// @brief Exploration depth, i.e., number of times KLEE branched for this /// state diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 2dee00e49c..a439c14d59 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -2749,8 +2749,12 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { branches.first->lastBrConfidently = false; branches.second->lastBrConfidently = false; } else { - (branches.first ? branches.first : branches.second)->lastBrConfidently = - true; + if (branches.first) { + branches.first->lastBrConfidently = true; + } + if (branches.second) { + branches.second->lastBrConfidently = true; + } } // NOTE: There is a hidden dependency here, markBranchVisited @@ -4022,11 +4026,10 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { if (iIdx >= vt->getNumElements()) { // Out of bounds write terminateStateOnProgramError( - state, - new ErrorEvent(locationOf(state), - StateTerminationType::BadVectorAccess, - "Out of bounds write when inserting element"), - StateTerminationConfidenceCategory::CONFIDENT); + state, new ErrorEvent(locationOf(state), + StateTerminationType::BadVectorAccess, + StateTerminationConfidenceCategory::CONFIDENT, + "Out of bounds write when inserting element")); return; } @@ -4067,11 +4070,10 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { if (iIdx >= vt->getNumElements()) { // Out of bounds read terminateStateOnProgramError( - state, - new ErrorEvent(locationOf(state), - StateTerminationType::BadVectorAccess, - "Out of bounds read when extracting element"), - StateTerminationConfidenceCategory::CONFIDENT); + state, new ErrorEvent(locationOf(state), + StateTerminationType::BadVectorAccess, + StateTerminationConfidenceCategory::CONFIDENT, + "Out of bounds read when extracting element")); return; } @@ -4975,8 +4977,9 @@ void Executor::terminateStateOnTargetError(ExecutionState &state, terminationType = StateTerminationType::User; } terminateStateOnProgramError( - state, new ErrorEvent(locationOf(state), terminationType, messaget), - StateTerminationConfidenceCategory::CONFIDENT); + state, + new ErrorEvent(locationOf(state), terminationType, + StateTerminationConfidenceCategory::CONFIDENT, messaget)); } void Executor::terminateStateOnError( @@ -5059,10 +5062,10 @@ void Executor::terminateStateOnExecError(ExecutionState &state, StateTerminationConfidenceCategory::CONFIDENT, ""); } -void Executor::terminateStateOnProgramError( - ExecutionState &state, const ref &reason, - StateTerminationConfidenceCategory confidence, const llvm::Twine &info, - const char *suffix) { +void Executor::terminateStateOnProgramError(ExecutionState &state, + const ref &reason, + const llvm::Twine &info, + const char *suffix) { assert(reason->ruleID > StateTerminationType::SOLVERERR && reason->ruleID <= StateTerminationType::PROGERR); ++stats::terminationProgramError; @@ -5081,8 +5084,8 @@ void Executor::terminateStateOnProgramError( } state.eventsRecorder.record(reason); - terminateStateOnError(state, reason->message, reason->ruleID, confidence, - info, suffix); + terminateStateOnError(state, reason->message, reason->ruleID, + reason->confidence, info, suffix); } void Executor::terminateStateOnSolverError(ExecutionState &state, @@ -5274,6 +5277,7 @@ void Executor::callExternalFunction(ExecutionState &state, KInstruction *target, e->getWidth() == Context::get().getPointerWidth()) { ref symExternCallsCanReturnNullExpr = makeMockValue(state, "symExternCallsCanReturnNull", Expr::Bool); + state.nullPointerMarkers.push_back(symExternCallsCanReturnNullExpr); e = SelectExpr::create( symExternCallsCanReturnNullExpr, ConstantExpr::alloc(0, Context::get().getPointerWidth()), e); @@ -5399,7 +5403,7 @@ void Executor::executeAlloc(ExecutionState &state, ref size, bool isLocal, makeMockValue(state, "symCheckOutOfMemory", Expr::Bool); address = SelectExpr::create(symCheckOutOfMemoryExpr, Expr::createPointer(0), address); - state.outOfMemoryMarkers.push_back(symCheckOutOfMemoryExpr); + state.nullPointerMarkers.push_back(symCheckOutOfMemoryExpr); } // state.addPointerResolution(address, mo); @@ -5445,9 +5449,10 @@ void Executor::executeFree(ExecutionState &state, ref address, *it->second, new ErrorEvent(new AllocEvent(mo->allocSite), locationOf(*it->second), StateTerminationType::Free, + rl.size() != 1 + ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT, "free of alloca"), - rl.size() != 1 ? StateTerminationConfidenceCategory::PROBABLY - : StateTerminationConfidenceCategory::CONFIDENT, getAddressInfo(*it->second, address)); } else if (mo->isGlobal) { if (rl.size() != 1) { @@ -5457,9 +5462,10 @@ void Executor::executeFree(ExecutionState &state, ref address, *it->second, new ErrorEvent(new AllocEvent(mo->allocSite), locationOf(*it->second), StateTerminationType::Free, + rl.size() != 1 + ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT, "free of global"), - rl.size() != 1 ? StateTerminationConfidenceCategory::PROBABLY - : StateTerminationConfidenceCategory::CONFIDENT, getAddressInfo(*it->second, address)); } else { it->second->removePointerResolutions(mo); @@ -5482,12 +5488,12 @@ ExecutionState *Executor::handleNullPointerException(ExecutionState &state, // If there are no markers on `malloc` returning nullptr, // then `confidence` depends on presence of `unbound` state. - if (!bound->outOfMemoryMarkers.empty()) { + if (!bound->nullPointerMarkers.empty()) { // bound constraints already contain `Expr::createIsZero()` std::vector markersArrays; - markersArrays.reserve(bound->outOfMemoryMarkers.size()); - findSymbolicObjects(bound->outOfMemoryMarkers.begin(), - bound->outOfMemoryMarkers.end(), markersArrays); + markersArrays.reserve(bound->nullPointerMarkers.size()); + findSymbolicObjects(bound->nullPointerMarkers.begin(), + bound->nullPointerMarkers.end(), markersArrays); // Do some iterations (2-3) to figure out if error is confident. ref allExcludedVectorsOfMarkers = Expr::createTrue(); @@ -5508,8 +5514,8 @@ ExecutionState *Executor::handleNullPointerException(ExecutionState &state, terminateStateOnProgramError( *bound, new ErrorEvent(locationOf(*bound), StateTerminationType::Ptr, - "memory error: null pointer exception"), - StateTerminationConfidenceCategory::CONFIDENT); + StateTerminationConfidenceCategory::CONFIDENT, + "memory error: null pointer exception")); convinced = true; break; } @@ -5524,7 +5530,7 @@ ExecutionState *Executor::handleNullPointerException(ExecutionState &state, // Exclude this combinations of markers ref conjExcludedVectorOfMarkers = Expr::createTrue(); - for (ref marker : bound->outOfMemoryMarkers) { + for (ref marker : bound->nullPointerMarkers) { conjExcludedVectorOfMarkers = AndExpr::create( conjExcludedVectorOfMarkers, EqExpr::create(marker, @@ -5541,10 +5547,9 @@ ExecutionState *Executor::handleNullPointerException(ExecutionState &state, ReachWithError::MayBeNullPointerException); terminateStateOnProgramError( - *bound, - new ErrorEvent(locationOf(*bound), StateTerminationType::Ptr, - "memory error: null pointer exception"), - StateTerminationConfidenceCategory::PROBABLY); + *bound, new ErrorEvent(locationOf(*bound), StateTerminationType::Ptr, + StateTerminationConfidenceCategory::PROBABLY, + "memory error: null pointer exception")); } } else { @@ -5556,10 +5561,10 @@ ExecutionState *Executor::handleNullPointerException(ExecutionState &state, terminateStateOnProgramError( *bound, new ErrorEvent(locationOf(*bound), StateTerminationType::Ptr, - "memory error: null pointer exception"), - branches.second != nullptr - ? StateTerminationConfidenceCategory::PROBABLY - : StateTerminationConfidenceCategory::CONFIDENT); + branches.second != nullptr + ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT, + "memory error: null pointer exception")); } return branches.second; @@ -5640,9 +5645,10 @@ bool Executor::resolveExact(ExecutionState &estate, ref address, terminateStateOnProgramError( *unbound, new ErrorEvent(locationOf(*unbound), StateTerminationType::Ptr, + !results.empty() + ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT, "memory error: invalid pointer: " + name), - !results.empty() ? StateTerminationConfidenceCategory::PROBABLY - : StateTerminationConfidenceCategory::CONFIDENT, getAddressInfo(*unbound, address)); } } @@ -5731,8 +5737,9 @@ void Executor::concretizeSize(ExecutionState &state, ref size, *hugeSize.second, new ErrorEvent(locationOf(*hugeSize.second), StateTerminationType::Model, + StateTerminationConfidenceCategory::CONFIDENT, "concretized symbolic size"), - StateTerminationConfidenceCategory::CONFIDENT, info.str()); + info.str()); } } } @@ -6425,8 +6432,8 @@ void Executor::executeMemoryOperation( *state, new ErrorEvent(new AllocEvent(mo->allocSite), locationOf(*state), StateTerminationType::ReadOnly, - "memory error: object read only"), - StateTerminationConfidenceCategory::CONFIDENT); + StateTerminationConfidenceCategory::CONFIDENT, + "memory error: object read only")); } else { wos->write(mo->getOffsetExpr(address), value); } @@ -6546,13 +6553,13 @@ void Executor::executeMemoryOperation( assert(branches.first); terminateStateOnProgramError( *branches.first, - new ErrorEvent(new AllocEvent(mo->allocSite), - locationOf(*branches.first), - StateTerminationType::ReadOnly, - "memory error: object read only"), - mayBeFalsePositive - ? StateTerminationConfidenceCategory::PROBABLY - : StateTerminationConfidenceCategory::CONFIDENT); + new ErrorEvent( + new AllocEvent(mo->allocSite), locationOf(*branches.first), + StateTerminationType::ReadOnly, + mayBeFalsePositive + ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT, + "memory error: object read only")); state = branches.second; } else { ref result = SelectExpr::create( @@ -6621,13 +6628,13 @@ void Executor::executeMemoryOperation( ConstantExpr::alloc(size, Context::get().getPointerWidth()), true); if (wos->readOnly) { terminateStateOnProgramError( - *bound, - new ErrorEvent(new AllocEvent(mo->allocSite), locationOf(*bound), - StateTerminationType::ReadOnly, - "memory error: object read only"), - mayBeFalsePositive - ? StateTerminationConfidenceCategory::PROBABLY - : StateTerminationConfidenceCategory::CONFIDENT); + *bound, new ErrorEvent( + new AllocEvent(mo->allocSite), locationOf(*bound), + StateTerminationType::ReadOnly, + mayBeFalsePositive + ? StateTerminationConfidenceCategory::PROBABLY + : StateTerminationConfidenceCategory::CONFIDENT, + "memory error: object read only")); } else { wos->write(mo->getOffsetExpr(address), value); } @@ -6678,9 +6685,10 @@ void Executor::executeMemoryOperation( *unbound, new ErrorEvent(new AllocEvent(baseObjectPair.first->allocSite), locationOf(*unbound), StateTerminationType::Ptr, - "memory error: out of bound pointer"), - mayBeFalsePositive ? StateTerminationConfidenceCategory::PROBABLY + mayBeFalsePositive + ? StateTerminationConfidenceCategory::PROBABLY : StateTerminationConfidenceCategory::CONFIDENT, + "memory error: out of bound pointer"), getAddressInfo(*unbound, address)); return; } @@ -6689,9 +6697,10 @@ void Executor::executeMemoryOperation( terminateStateOnProgramError( *unbound, new ErrorEvent(locationOf(*unbound), StateTerminationType::Ptr, - "memory error: out of bound pointer"), - mayBeFalsePositive ? StateTerminationConfidenceCategory::PROBABLY + mayBeFalsePositive + ? StateTerminationConfidenceCategory::PROBABLY : StateTerminationConfidenceCategory::CONFIDENT, + "memory error: out of bound pointer"), getAddressInfo(*unbound, address)); } } @@ -7343,21 +7352,25 @@ void Executor::getConstraintLog(const ExecutionState &state, std::string &res, } void Executor::addSARIFReport(const ExecutionState &state) { - ResultJson result{}; - - CodeFlowJson codeFlow = state.eventsRecorder.serialize(); - if (ref lastEvent = llvm::dyn_cast(state.eventsRecorder.last())) { + + ResultJson result{}; + + CodeFlowJson codeFlow = state.eventsRecorder.serialize(); + result.locations.push_back(lastEvent->serialize()); result.message = {Message{lastEvent->message}}; result.ruleId = {terminationTypeName(lastEvent->ruleID)}; - result.level = {"error"}; - } + result.level = {lastEvent->confidence == + StateTerminationConfidenceCategory::CONFIDENT + ? "error" + : "warning"}; - result.codeFlows.push_back(std::move(codeFlow)); + result.codeFlows.push_back(std::move(codeFlow)); - sarifReport.runs.back().results.push_back(std::move(result)); + sarifReport.runs.back().results.push_back(std::move(result)); + } } SarifReportJson Executor::getSARIFReport() const { return sarifReport; } diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index 8780de8aef..37d7852488 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -633,10 +633,10 @@ class Executor : public Interpreter { /// Call error handler and terminate state in case of program errors /// (e.g. free()ing globals, out-of-bound accesses) - void terminateStateOnProgramError( - ExecutionState &state, const ref &reason, - StateTerminationConfidenceCategory confidence, - const llvm::Twine &longMessage = "", const char *suffix = nullptr); + void terminateStateOnProgramError(ExecutionState &state, + const ref &reason, + const llvm::Twine &longMessage = "", + const char *suffix = nullptr); void terminateStateOnTerminator(ExecutionState &state); diff --git a/lib/Core/SpecialFunctionHandler.cpp b/lib/Core/SpecialFunctionHandler.cpp index a46345fad8..104f4fee8a 100644 --- a/lib/Core/SpecialFunctionHandler.cpp +++ b/lib/Core/SpecialFunctionHandler.cpp @@ -31,8 +31,8 @@ #include "klee/klee.h" #include "klee/Support/CompilerWarning.h" -#include -#include +#include +#include DISABLE_WARNING_PUSH DISABLE_WARNING_DEPRECATED_DECLARATIONS #include "llvm/ADT/APFloat.h" @@ -348,8 +348,8 @@ void SpecialFunctionHandler::handleAbort(ExecutionState &state, executor.terminateStateOnProgramError( state, new ErrorEvent(executor.locationOf(state), StateTerminationType::Abort, - "abort failure"), - StateTerminationConfidenceCategory::CONFIDENT); + StateTerminationConfidenceCategory::CONFIDENT, + "abort failure")); } void SpecialFunctionHandler::handleExit(ExecutionState &state, @@ -378,11 +378,11 @@ void SpecialFunctionHandler::handleAssert(ExecutionState &state, executor.terminateStateOnProgramError( state, new ErrorEvent(executor.locationOf(state), StateTerminationType::Assert, + isAssertFailsConfidently(state) + ? StateTerminationConfidenceCategory::CONFIDENT + : StateTerminationConfidenceCategory::PROBABLY, "ASSERTION FAIL: " + - readStringAtAddress(state, arguments[0])), - isAssertFailsConfidently(state) - ? StateTerminationConfidenceCategory::CONFIDENT - : StateTerminationConfidenceCategory::PROBABLY); + readStringAtAddress(state, arguments[0]))); } void SpecialFunctionHandler::handleAssertFail( @@ -393,11 +393,11 @@ void SpecialFunctionHandler::handleAssertFail( executor.terminateStateOnProgramError( state, new ErrorEvent(executor.locationOf(state), StateTerminationType::Assert, + isAssertFailsConfidently(state) + ? StateTerminationConfidenceCategory::CONFIDENT + : StateTerminationConfidenceCategory::PROBABLY, "ASSERTION FAIL: " + - readStringAtAddress(state, arguments[0])), - isAssertFailsConfidently(state) - ? StateTerminationConfidenceCategory::CONFIDENT - : StateTerminationConfidenceCategory::PROBABLY); + readStringAtAddress(state, arguments[0]))); } void SpecialFunctionHandler::handleReportError( @@ -411,9 +411,9 @@ void SpecialFunctionHandler::handleReportError( state, new ErrorEvent(executor.locationOf(state), StateTerminationType::ReportError, + StateTerminationConfidenceCategory::CONFIDENT, readStringAtAddress(state, arguments[2])), - StateTerminationConfidenceCategory::CONFIDENT, "", - readStringAtAddress(state, arguments[3]).c_str()); + "", readStringAtAddress(state, arguments[3]).c_str()); } void SpecialFunctionHandler::handleNew(ExecutionState &state, @@ -856,8 +856,8 @@ void SpecialFunctionHandler::handleCheckMemoryAccess( executor.terminateStateOnProgramError( state, new ErrorEvent(executor.locationOf(state), StateTerminationType::Ptr, + StateTerminationConfidenceCategory::CONFIDENT, "check_memory_access: memory error"), - StateTerminationConfidenceCategory::CONFIDENT, executor.getAddressInfo(state, address)); } else { const MemoryObject *mo = state.addressSpace.findObject(idObject).first; @@ -866,10 +866,11 @@ void SpecialFunctionHandler::handleCheckMemoryAccess( if (!chk->isTrue()) { executor.terminateStateOnProgramError( state, - new ErrorEvent( - new AllocEvent(mo->allocSite), executor.locationOf(state), - StateTerminationType::Ptr, "check_memory_access: memory error"), - StateTerminationConfidenceCategory::CONFIDENT, + new ErrorEvent(new AllocEvent(mo->allocSite), + executor.locationOf(state), + StateTerminationType::Ptr, + StateTerminationConfidenceCategory::CONFIDENT, + "check_memory_access: memory error"), executor.getAddressInfo(state, address)); } }