From 25ea1e3bbb2ff2abf57dd348af62837425dba8a7 Mon Sep 17 00:00:00 2001 From: Nguyễn Gia Phong Date: Fri, 10 Nov 2023 12:23:37 +0900 Subject: Implement detection of implicit return via pointer --- lib/Core/Executor.cpp | 78 ++++++++++++++++++++++++++++++++++++++++----------- lib/Core/Executor.h | 11 ++++---- 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 4763fdb5..d26e5d04 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -2125,11 +2125,17 @@ Function *Executor::getTargetFunction(Value *calledVal) { } } +bool patchLocal(llvm::Function *fn) { + if (PatchedFile.empty() + || fn->getSubprogram()->getFilename() != PatchedFile) + return false; + auto const name = fn->getName(); + return name != "__choose" && name.substr(0, 7) != "__klee_"; +} + void Executor::registerOutput(ExecutionState &state, Function *fn, std::string var, Expr::Width bits, ref val) { - if (state.patchLocs == 0 || fn->getName() == "__choose" - || (!PatchedFile.empty() - && fn->getSubprogram()->getFilename() != PatchedFile)) + if (state.patchLocs == 0 || !patchLocal(fn)) return; size_t bytes = bits == Expr::Bool ? 1u : bits >> 3; auto const inst = state.prevPC->inst; @@ -2168,6 +2174,33 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { assert(!caller && "caller set on initial stack frame"); terminateStateOnExit(state); } else { + std::vector> arguments; + auto const fp = cast(*caller).getCalledOperand(); + if (auto const fn = getTargetFunction(fp)) { + auto const kf = kmodule->functionMap[fn]; + auto const ft = fn->getFunctionType(); + auto k = fn->arg_size(); + while (k--) { + auto const t = ft->getParamType(k); + if (!t->isPointerTy()) + continue; + auto const addr = getArgumentCell(state, kf, k).value; + // FIXME: changed to getPointerElementType in LLVM 14 + // and removed (!!!) in favor of opaque pointers in LLVM 15 + auto const pt = cast(t)->getElementType(); + if (pt->isFunctionTy() || !patchLocal(fn)) + continue; + auto const size = getWidthForLLVMType(pt); + auto const& val = executeMemoryOperation(state, false, addr, + 0, nullptr, size); + if (val.isNull()) + klee_warning("failed to make argument %s:%s#%zu symbolic", + fn->getSubprogram()->getFilename().data(), + fn->getName().data(), k); + else + registerOutput(state, fn, "parg" + llvm::utostr(k), size, val); + } + } state.popFrame(); if (statsTracker) @@ -4635,13 +4668,18 @@ void Executor::resolveExact(ExecutionState &state, } } -void Executor::executeMemoryOperation(ExecutionState &state, - bool isWrite, - ref address, - ref value /* undef if read */, - KInstruction *target /* undef if write */) { - Expr::Width type = (isWrite ? value->getWidth() : - getWidthForLLVMType(target->inst->getType())); +ref Executor::executeMemoryOperation(ExecutionState &state, + bool isWrite, + ref address, + ref value /* undef if read */, + KInstruction *target, + Expr::Width type) { + if (isWrite) + type = value->getWidth(); + else if (target) + type = getWidthForLLVMType(target->inst->getType()); + else + assert(type != Expr::InvalidWidth); unsigned bytes = Expr::getMinBytesForWidth(type); if (SimplifySymIndices) { @@ -4711,7 +4749,7 @@ void Executor::executeMemoryOperation(ExecutionState &state, if (!success) { state.pc = state.prevPC; terminateStateOnSolverError(state, "Query timed out (bounds check)."); - return; + return {}; } if (inBounds) { @@ -4729,11 +4767,13 @@ void Executor::executeMemoryOperation(ExecutionState &state, if (interpreterOpts.MakeConcreteSymbolic) result = replaceReadWithSymbolic(state, result); - - bindLocal(target, state, result); + if (target) + bindLocal(target, state, result); + else + return result; } - return; + return {}; } } } @@ -4776,7 +4816,10 @@ void Executor::executeMemoryOperation(ExecutionState &state, } } else { ref result = os->read(mo->getOffsetExpr(address), type); - bindLocal(target, *bound, result); + if (target) + bindLocal(target, *bound, result); + else + return result; } } @@ -4789,6 +4832,8 @@ void Executor::executeMemoryOperation(ExecutionState &state, if (unbound) { if (incomplete) { terminateStateOnSolverError(*unbound, "Query timed out (resolve)."); + } else if (!isWrite && !target) { + return {}; } else { if (auto CE = dyn_cast(address)) { std::uintptr_t ptrval = CE->getZExtValue(); @@ -4797,7 +4842,6 @@ void Executor::executeMemoryOperation(ExecutionState &state, terminateStateOnProgramError( *unbound, "memory error: null page access", StateTerminationType::Ptr, getAddressInfo(*unbound, address)); - return; } else if (MemoryManager::isDeterministic) { using kdalloc::LocationInfo; auto li = unbound->heapAllocator.locationInfo(ptr, bytes); @@ -4811,7 +4855,6 @@ void Executor::executeMemoryOperation(ExecutionState &state, terminateStateOnProgramError( *unbound, "memory error: use after free", StateTerminationType::Ptr, getAddressInfo(*unbound, address)); - return; } } } @@ -4821,6 +4864,7 @@ void Executor::executeMemoryOperation(ExecutionState &state, StateTerminationType::Ptr, getAddressInfo(*unbound, address)); } } + return {}; } void Executor::executeMakeSymbolic(ExecutionState &state, diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index a179c66d..870e5801 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -354,11 +354,12 @@ private: // do address resolution / object binding / out of bounds checking // and perform the operation - void executeMemoryOperation(ExecutionState &state, - bool isWrite, - ref address, - ref value /* undef if read */, - KInstruction *target /* undef if write */); + ref executeMemoryOperation(ExecutionState &state, + bool isWrite, + ref address, + ref value /* undef if read */, + KInstruction *target, + Expr::Width type = Expr::InvalidWidth); void executeMakeSymbolic(ExecutionState &state, const MemoryObject *mo, const std::string &name); -- cgit 1.4.1