diff options
| author | Daniel Schemmel <daniel@schemmel.net> | 2023-03-15 00:13:28 +0000 |
|---|---|---|
| committer | Frank Busse <f.busse@imperial.ac.uk> | 2023-03-16 11:57:59 +0000 |
| commit | 9d0e072e3b40b720a26265f0d9b2b99f2d3a954e (patch) | |
| tree | 23bbe4dca4432acbe1b52ae0861310ad1eec818f /lib/Core/Executor.cpp | |
| parent | 51655c601b3246457e27cf296284c049641c470c (diff) | |
| download | klee-9d0e072e3b40b720a26265f0d9b2b99f2d3a954e.tar.gz | |
Integrate KDAlloc into KLEE
Diffstat (limited to 'lib/Core/Executor.cpp')
| -rw-r--r-- | lib/Core/Executor.cpp | 141 |
1 files changed, 111 insertions, 30 deletions
diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 1187654d..013f01c2 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -9,6 +9,7 @@ #include "Executor.h" +#include "AddressSpace.h" #include "Context.h" #include "CoreStats.h" #include "ExecutionState.h" @@ -35,6 +36,7 @@ #include "klee/Expr/ExprPPrinter.h" #include "klee/Expr/ExprSMTLIBPrinter.h" #include "klee/Expr/ExprUtil.h" +#include "klee/KDAlloc/kdalloc.h" #include "klee/Module/Cell.h" #include "klee/Module/InstructionInfoTable.h" #include "klee/Module/KCallable.h" @@ -81,6 +83,7 @@ typedef unsigned TypeSize; #include <algorithm> #include <cassert> #include <cerrno> +#include <cstdint> #include <cstring> #include <cxxabi.h> #include <fstream> @@ -90,6 +93,7 @@ typedef unsigned TypeSize; #include <sstream> #include <string> #include <sys/mman.h> +#include <sys/resource.h> #include <vector> using namespace llvm; @@ -208,6 +212,14 @@ cl::opt<bool> AllExternalWarnings( "as opposed to once per function (default=false)"), cl::cat(ExtCallsCat)); +cl::opt<std::size_t> ExternalPageThreshold( + "kdalloc-external-page-threshold", cl::init(1024), + cl::desc( + "Threshold for garbage collecting pages used by external calls. If " + "there is a significant number of infrequently used pages resident in " + "memory, these will only be cleaned up if the total number of pages " + "used for external calls is above the given threshold (default=1024)."), + cl::cat(ExtCallsCat)); /*** Seeding options ***/ @@ -684,7 +696,7 @@ void Executor::allocateGlobalObjects(ExecutionState &state) { // We allocate an object to represent each function, // its address can be used for function pointers. // TODO: Check whether the object is accessed? - auto mo = memory->allocate(8, false, true, &f, 8); + auto mo = memory->allocate(8, false, true, &state, &f, 8); addr = Expr::createPointer(mo->address); legalFunctions.emplace(mo->address, &f); } @@ -763,7 +775,8 @@ void Executor::allocateGlobalObjects(ExecutionState &state) { } MemoryObject *mo = memory->allocate(size, /*isLocal=*/false, - /*isGlobal=*/true, /*allocSite=*/&v, + /*isGlobal=*/true, /*state=*/nullptr, + /*allocSite=*/&v, /*alignment=*/globalObjectAlignment); if (!mo) klee_error("out of memory"); @@ -813,9 +826,6 @@ void Executor::initializeGlobalAliases() { void Executor::initializeGlobalObjects(ExecutionState &state) { const Module *m = kmodule->module.get(); - // remember constant objects to initialise their counter part for external - // calls - std::vector<ObjectState *> constantObjects; for (const GlobalVariable &v : m->globals()) { MemoryObject *mo = globalObjects.find(&v)->second; ObjectState *os = bindObjectInState(state, mo, false); @@ -838,22 +848,15 @@ void Executor::initializeGlobalObjects(ExecutionState &state) { } } else if (v.hasInitializer()) { initializeGlobalObject(state, os, v.getInitializer(), 0); - if (v.isConstant()) - constantObjects.emplace_back(os); + if (v.isConstant()) { + os->setReadOnly(true); + // initialise constant memory that may be used with external calls + state.addressSpace.copyOutConcrete(mo, os); + } } else { os->initializeToRandom(); } } - - // initialise constant memory that is potentially used with external calls - if (!constantObjects.empty()) { - // initialise the actual memory with constant values - state.addressSpace.copyOutConcretes(); - - // mark constant objects as read-only - for (auto obj : constantObjects) - obj->setReadOnly(true); - } } @@ -1537,7 +1540,7 @@ MemoryObject *Executor::serializeLandingpad(ExecutionState &state, } MemoryObject *mo = - memory->allocate(serialized.size(), true, false, nullptr, 1); + memory->allocate(serialized.size(), true, false, &state, nullptr, 1); ObjectState *os = bindObjectInState(state, mo, false); for (unsigned i = 0; i < serialized.size(); i++) { os->write8(i, serialized[i]); @@ -1987,7 +1990,7 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f, StackFrame &sf = state.stack.back(); MemoryObject *mo = sf.varargs = - memory->allocate(size, true, false, state.prevPC->inst, + memory->allocate(size, true, false, &state, state.prevPC->inst, (requires16ByteAlignment ? 16 : 8)); if (!mo && size) { terminateStateOnExecError(state, "out of memory (varargs)"); @@ -2054,7 +2057,7 @@ void Executor::transferToBasicBlock(BasicBlock *dst, BasicBlock *src, /// Compute the true target of a function call, resolving LLVM aliases /// and bitcasts. -Function* Executor::getTargetFunction(Value *calledVal, ExecutionState &state) { +Function *Executor::getTargetFunction(Value *calledVal) { SmallPtrSet<const GlobalValue*, 3> Visited; Constant *c = dyn_cast<Constant>(calledVal); @@ -2418,7 +2421,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { const CallBase &cb = cast<CallBase>(*i); Value *fp = cb.getCalledOperand(); unsigned numArgs = cb.arg_size(); - Function *f = getTargetFunction(fp, state); + Function *f = getTargetFunction(fp); // evaluate arguments std::vector< ref<Expr> > arguments; @@ -3873,7 +3876,35 @@ void Executor::callExternalFunction(ExecutionState &state, } // Prepare external memory for invoking the function - state.addressSpace.copyOutConcretes(); + static std::size_t residentPages = 0; + double avgNeededPages = 0; + if (MemoryManager::isDeterministic) { + auto const minflt = [] { + struct rusage ru = {}; + int ret = getrusage(RUSAGE_SELF, &ru); + assert(!ret && "getrusage failed"); + assert(ru.ru_minflt >= 0); + return ru.ru_minflt; + }; + + auto tmp = minflt(); + std::size_t neededPages = state.addressSpace.copyOutConcretes(); + auto newPages = minflt() - tmp; + assert(newPages >= 0); + residentPages += newPages; + assert(residentPages >= neededPages && + "allocator too full, assumption that each object occupies its own " + "page is no longer true"); + + // average of pages needed for an external function call + static double avgNeededPages_ = residentPages; + // exponential moving average with alpha = 1/3 + avgNeededPages_ = (3.0 * avgNeededPages_ + neededPages) / 4.0; + avgNeededPages = avgNeededPages_; + } else { + state.addressSpace.copyOutConcretes(); + } + #ifndef WINDOWS // Update external errno state with local state value int *errno_addr = getErrnoLocation(state); @@ -3926,6 +3957,13 @@ void Executor::callExternalFunction(ExecutionState &state, return; } + if (MemoryManager::isDeterministic && residentPages > ExternalPageThreshold && + residentPages > 2 * avgNeededPages) { + if (memory->markMappingsAsUnneeded()) { + residentPages = 0; + } + } + #ifndef WINDOWS // Update errno memory object with the errno value from the call int error = externalDispatcher->getLastErrno(); @@ -4002,7 +4040,7 @@ void Executor::executeAlloc(ExecutionState &state, } MemoryObject *mo = memory->allocate(CE->getZExtValue(), isLocal, /*isGlobal=*/false, - allocSite, allocationAlignment); + &state, allocSite, allocationAlignment); if (!mo) { bindLocal(target, state, ConstantExpr::alloc(0, Context::get().getPointerWidth())); @@ -4019,7 +4057,9 @@ void Executor::executeAlloc(ExecutionState &state, unsigned count = std::min(reallocFrom->size, os->size); for (unsigned i=0; i<count; i++) os->write(i, reallocFrom->read8(i)); - state.addressSpace.unbindObject(reallocFrom->getObject()); + const MemoryObject *reallocObject = reallocFrom->getObject(); + state.deallocate(reallocObject); + state.addressSpace.unbindObject(reallocObject); } } } else { @@ -4134,6 +4174,7 @@ void Executor::executeFree(ExecutionState &state, StateTerminationType::Free, getAddressInfo(*it->second, address)); } else { + it->second->deallocate(mo); it->second->addressSpace.unbindObject(mo); if (target) bindLocal(target, *it->second, Expr::createPointer(0)); @@ -4168,6 +4209,19 @@ void Executor::resolveExact(ExecutionState &state, } if (unbound) { + auto CE = dyn_cast<ConstantExpr>(p); + if (MemoryManager::isDeterministic && CE) { + using kdalloc::LocationInfo; + auto ptr = reinterpret_cast<void *>(CE->getZExtValue()); + auto locinfo = unbound->heapAllocator.location_info(ptr, 1); + if (locinfo == LocationInfo::LI_AllocatedOrQuarantined && + locinfo.getBaseAddress() == ptr && name == "free") { + terminateStateOnError(*unbound, "memory error: double free", + StateTerminationType::Ptr, + getAddressInfo(*unbound, p)); + return; + } + } terminateStateOnError(*unbound, "memory error: invalid pointer: " + name, StateTerminationType::Ptr, getAddressInfo(*unbound, p)); } @@ -4293,6 +4347,32 @@ void Executor::executeMemoryOperation(ExecutionState &state, if (incomplete) { terminateStateOnSolverError(*unbound, "Query timed out (resolve)."); } else { + if (auto CE = dyn_cast<ConstantExpr>(address)) { + std::uintptr_t ptrval = CE->getZExtValue(); + auto ptr = reinterpret_cast<void *>(ptrval); + if (ptrval < MemoryManager::pageSize) { + terminateStateOnError(*unbound, "memory error: null page access", + StateTerminationType::Ptr, + getAddressInfo(*unbound, address)); + return; + } else if (MemoryManager::isDeterministic) { + using kdalloc::LocationInfo; + auto li = unbound->heapAllocator.location_info(ptr, bytes); + if (li == LocationInfo::LI_AllocatedOrQuarantined) { + // In case there is no size mismatch (checked by resolving for base + // address), the object is quarantined. + auto base = reinterpret_cast<std::uintptr_t>(li.getBaseAddress()); + auto baseExpr = Expr::createPointer(base); + ObjectPair op; + if (!unbound->addressSpace.resolveOne(baseExpr, op)) { + terminateStateOnError(*unbound, "memory error: use after free", + StateTerminationType::Ptr, + getAddressInfo(*unbound, address)); + return; + } + } + } + } terminateStateOnError(*unbound, "memory error: out of bound pointer", StateTerminationType::Ptr, getAddressInfo(*unbound, address)); @@ -4404,10 +4484,10 @@ void Executor::runFunctionAsMain(Function *f, arguments.push_back(ConstantExpr::alloc(argc, Expr::Int32)); if (++ai!=ae) { Instruction *first = &*(f->begin()->begin()); - argvMO = - memory->allocate((argc + 1 + envc + 1 + 1) * NumPtrBytes, - /*isLocal=*/false, /*isGlobal=*/true, - /*allocSite=*/first, /*alignment=*/8); + argvMO = memory->allocate((argc + 1 + envc + 1 + 1) * NumPtrBytes, + /*isLocal=*/false, /*isGlobal=*/true, + /*state=*/nullptr, /*allocSite=*/first, + /*alignment=*/8); if (!argvMO) klee_error("Could not allocate memory for function arguments"); @@ -4424,7 +4504,7 @@ void Executor::runFunctionAsMain(Function *f, } } - ExecutionState *state = new ExecutionState(kmodule->functionMap[f]); + ExecutionState *state = new ExecutionState(kmodule->functionMap[f], memory); if (pathWriter) state->pathOS = pathWriter->open(); @@ -4452,7 +4532,8 @@ void Executor::runFunctionAsMain(Function *f, MemoryObject *arg = memory->allocate(len + 1, /*isLocal=*/false, /*isGlobal=*/true, - /*allocSite=*/state->pc->inst, /*alignment=*/8); + state, /*allocSite=*/state->pc->inst, + /*alignment=*/8); if (!arg) klee_error("Could not allocate memory for function arguments"); ObjectState *os = bindObjectInState(*state, arg, false); |
