diff options
author | Daniel Dunbar <daniel@zuster.org> | 2009-05-21 04:36:41 +0000 |
---|---|---|
committer | Daniel Dunbar <daniel@zuster.org> | 2009-05-21 04:36:41 +0000 |
commit | 6f290d8f9e9d7faac295cb51fc96884a18f4ded4 (patch) | |
tree | 46e7d426abc0c9f06ac472ac6f7f9e661b5d78cb /lib/Core | |
parent | a55960edd4dcd7535526de8d2277642522aa0209 (diff) | |
download | klee-6f290d8f9e9d7faac295cb51fc96884a18f4ded4.tar.gz |
Initial KLEE checkin.
- Lots more tweaks, documentation, and web page content is needed, but this should compile & work on OS X & Linux. git-svn-id: https://llvm.org/svn/llvm-project/klee/trunk@72205 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Core')
37 files changed, 10553 insertions, 0 deletions
diff --git a/lib/Core/AddressSpace.cpp b/lib/Core/AddressSpace.cpp new file mode 100644 index 00000000..fb032fd5 --- /dev/null +++ b/lib/Core/AddressSpace.cpp @@ -0,0 +1,334 @@ +//===-- AddressSpace.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "AddressSpace.h" +#include "CoreStats.h" +#include "Memory.h" +#include "TimingSolver.h" + +#include "klee/Expr.h" +#include "klee/TimerStatIncrementer.h" + +using namespace klee; + +/// + +void AddressSpace::bindObject(const MemoryObject *mo, ObjectState *os) { + assert(os->copyOnWriteOwner==0 && "object already has owner"); + os->copyOnWriteOwner = cowKey; + objects = objects.replace(std::make_pair(mo, os)); +} + +void AddressSpace::unbindObject(const MemoryObject *mo) { + objects = objects.remove(mo); +} + +const ObjectState *AddressSpace::findObject(const MemoryObject *mo) const { + const MemoryMap::value_type *res = objects.lookup(mo); + + return res ? res->second : 0; +} + +ObjectState *AddressSpace::getWriteable(const MemoryObject *mo, + const ObjectState *os) { + assert(!os->readOnly); + + if (cowKey==os->copyOnWriteOwner) { + return const_cast<ObjectState*>(os); + } else { + ObjectState *n = new ObjectState(*os); + n->copyOnWriteOwner = cowKey; + objects = objects.replace(std::make_pair(mo, n)); + return n; + } +} + +/// + +bool AddressSpace::resolveOne(uint64_t addr64, ObjectPair &result) { + unsigned address = (unsigned) addr64; + MemoryObject hack(address); + + if (const MemoryMap::value_type *res = objects.lookup_previous(&hack)) { + const MemoryObject *mo = res->first; + if ((mo->size==0 && address==mo->address) || + (address - mo->address < mo->size)) { + result = *res; + return true; + } + } + + return false; +} + +bool AddressSpace::resolveOne(ExecutionState &state, + TimingSolver *solver, + ref<Expr> address, + ObjectPair &result, + bool &success) { + if (address.isConstant()) { + success = resolveOne(address.getConstantValue(), result); + return true; + } else { + TimerStatIncrementer timer(stats::resolveTime); + + // try cheap search, will succeed for any inbounds pointer + + ref<Expr> cex(0); + if (!solver->getValue(state, address, cex)) + return false; + unsigned example = (unsigned) cex.getConstantValue(); + MemoryObject hack(example); + const MemoryMap::value_type *res = objects.lookup_previous(&hack); + + if (res) { + const MemoryObject *mo = res->first; + if (example - mo->address < mo->size) { + result = *res; + success = true; + return true; + } + } + + // didn't work, now we have to search + + MemoryMap::iterator oi = objects.upper_bound(&hack); + MemoryMap::iterator begin = objects.begin(); + MemoryMap::iterator end = objects.end(); + + MemoryMap::iterator start = oi; + while (oi!=begin) { + --oi; + const MemoryObject *mo = oi->first; + + bool mayBeTrue; + if (!solver->mayBeTrue(state, + mo->getBoundsCheckPointer(address), mayBeTrue)) + return false; + if (mayBeTrue) { + result = *oi; + success = true; + return true; + } else { + bool mustBeTrue; + if (!solver->mustBeTrue(state, + UgeExpr::create(address, mo->getBaseExpr()), + mustBeTrue)) + return false; + if (mustBeTrue) + break; + } + } + + // search forwards + for (oi=start; oi!=end; ++oi) { + const MemoryObject *mo = oi->first; + + bool mustBeTrue; + if (!solver->mustBeTrue(state, + UltExpr::create(address, mo->getBaseExpr()), + mustBeTrue)) + return false; + if (mustBeTrue) { + break; + } else { + bool mayBeTrue; + + if (!solver->mayBeTrue(state, + mo->getBoundsCheckPointer(address), + mayBeTrue)) + return false; + if (mayBeTrue) { + result = *oi; + success = true; + return true; + } + } + } + + success = false; + return true; + } +} + +bool AddressSpace::resolve(ExecutionState &state, + TimingSolver *solver, + ref<Expr> p, + ResolutionList &rl, + unsigned maxResolutions, + double timeout) { + if (p.isConstant()) { + ObjectPair res; + if (resolveOne(p.getConstantValue(), res)) + rl.push_back(res); + return false; + } else { + TimerStatIncrementer timer(stats::resolveTime); + uint64_t timeout_us = (uint64_t) (timeout*1000000.); + + // XXX in general this isn't exactly what we want... for + // a multiple resolution case (or for example, a \in {b,c,0}) + // we want to find the first object, find a cex assuming + // not the first, find a cex assuming not the second... + // etc. + + // XXX how do we smartly amortize the cost of checking to + // see if we need to keep searching up/down, in bad cases? + // maybe we don't care? + + // XXX we really just need a smart place to start (although + // if its a known solution then the code below is guaranteed + // to hit the fast path with exactly 2 queries). we could also + // just get this by inspection of the expr. + + ref<Expr> cex(0); + if (!solver->getValue(state, p, cex)) + return true; + unsigned example = (unsigned) cex.getConstantValue(); + MemoryObject hack(example); + + MemoryMap::iterator oi = objects.upper_bound(&hack); + MemoryMap::iterator begin = objects.begin(); + MemoryMap::iterator end = objects.end(); + + MemoryMap::iterator start = oi; + + // XXX in the common case we can save one query if we ask + // mustBeTrue before mayBeTrue for the first result. easy + // to add I just want to have a nice symbolic test case first. + + // search backwards, start with one minus because this + // is the object that p *should* be within, which means we + // get write off the end with 4 queries (XXX can be better, + // no?) + while (oi!=begin) { + --oi; + const MemoryObject *mo = oi->first; + if (timeout_us && timeout_us < timer.check()) + return true; + + // XXX I think there is some query wasteage here? + ref<Expr> inBounds = mo->getBoundsCheckPointer(p); + bool mayBeTrue; + if (!solver->mayBeTrue(state, inBounds, mayBeTrue)) + return true; + if (mayBeTrue) { + rl.push_back(*oi); + + // fast path check + unsigned size = rl.size(); + if (size==1) { + bool mustBeTrue; + if (!solver->mustBeTrue(state, inBounds, mustBeTrue)) + return true; + if (mustBeTrue) + return false; + } else if (size==maxResolutions) { + return true; + } + } + + bool mustBeTrue; + if (!solver->mustBeTrue(state, + UgeExpr::create(p, mo->getBaseExpr()), + mustBeTrue)) + return true; + if (mustBeTrue) + break; + } + // search forwards + for (oi=start; oi!=end; ++oi) { + const MemoryObject *mo = oi->first; + if (timeout_us && timeout_us < timer.check()) + return true; + + bool mustBeTrue; + if (!solver->mustBeTrue(state, + UltExpr::create(p, mo->getBaseExpr()), + mustBeTrue)) + return true; + if (mustBeTrue) + break; + + // XXX I think there is some query wasteage here? + ref<Expr> inBounds = mo->getBoundsCheckPointer(p); + bool mayBeTrue; + if (!solver->mayBeTrue(state, inBounds, mayBeTrue)) + return true; + if (mayBeTrue) { + rl.push_back(*oi); + + // fast path check + unsigned size = rl.size(); + if (size==1) { + bool mustBeTrue; + if (!solver->mustBeTrue(state, inBounds, mustBeTrue)) + return true; + if (mustBeTrue) + return false; + } else if (size==maxResolutions) { + return true; + } + } + } + } + + return false; +} + +// These two are pretty big hack so we can sort of pass memory back +// and forth to externals. They work by abusing the concrete cache +// store inside of the object states, which allows them to +// transparently avoid screwing up symbolics (if the byte is symbolic +// then its concrete cache byte isn't being used) but is just a hack. + +void AddressSpace::copyOutConcretes() { + for (MemoryMap::iterator it = objects.begin(), ie = objects.end(); + it != ie; ++it) { + const MemoryObject *mo = it->first; + + if (!mo->isUserSpecified) { + ObjectState *os = it->second; + uint8_t *address = (uint8_t*) (unsigned long) mo->address; + + if (!os->readOnly) + memcpy(address, os->concreteStore, mo->size); + } + } +} + +bool AddressSpace::copyInConcretes() { + for (MemoryMap::iterator it = objects.begin(), ie = objects.end(); + it != ie; ++it) { + const MemoryObject *mo = it->first; + + if (!mo->isUserSpecified) { + const ObjectState *os = it->second; + uint8_t *address = (uint8_t*) (unsigned long) mo->address; + + if (memcmp(address, os->concreteStore, mo->size)!=0) { + if (os->readOnly) { + return false; + } else { + ObjectState *wos = getWriteable(mo, os); + memcpy(wos->concreteStore, address, mo->size); + } + } + } + } + + return true; +} + +/***/ + +bool MemoryObjectLT::operator()(const MemoryObject *a, const MemoryObject *b) const { + return a->address < b->address; +} + diff --git a/lib/Core/AddressSpace.h b/lib/Core/AddressSpace.h new file mode 100644 index 00000000..a281714c --- /dev/null +++ b/lib/Core/AddressSpace.h @@ -0,0 +1,131 @@ +//===-- AddressSpace.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_ADDRESSSPACE_H +#define KLEE_ADDRESSSPACE_H + +#include "ObjectHolder.h" + +#include "klee/Expr.h" +#include "klee/Internal/ADT/ImmutableMap.h" + +namespace klee { + class ExecutionState; + class MemoryObject; + class ObjectState; + class TimingSolver; + + template<class T> class ref; + + typedef std::pair<const MemoryObject*, const ObjectState*> ObjectPair; + typedef std::vector<ObjectPair> ResolutionList; + + /// Function object ordering MemoryObject's by address. + struct MemoryObjectLT { + bool operator()(const MemoryObject *a, const MemoryObject *b) const; + }; + + typedef ImmutableMap<const MemoryObject*, ObjectHolder, MemoryObjectLT> MemoryMap; + + class AddressSpace { + private: + /// Epoch counter used to control ownership of objects. + mutable unsigned cowKey; + + /// Unsupported, use copy constructor + AddressSpace &operator=(const AddressSpace&); + + public: + /// The MemoryObject -> ObjectState map that constitutes the + /// address space. + /// + /// The set of objects where o->copyOnWriteOwner == cowKey are the + /// objects that we own. + /// + /// \invariant forall o in objects, o->copyOnWriteOwner <= cowKey + MemoryMap objects; + + public: + AddressSpace() : cowKey(1) {} + AddressSpace(const AddressSpace &b) : cowKey(++b.cowKey), objects(b.objects) { } + ~AddressSpace() {} + + /// Resolve address to an ObjectPair in result. + /// \return true iff an object was found. + bool resolveOne(uint64_t address, + ObjectPair &result); + + /// Resolve address to an ObjectPair in result. + /// + /// \param state The state this address space is part of. + /// \param solver A solver used to determine possible + /// locations of the \a address. + /// \param address The address to search for. + /// \param[out] result An ObjectPair this address can resolve to + /// (when returning true). + /// \return true iff an object was found at \a address. + bool resolveOne(ExecutionState &state, + TimingSolver *solver, + ref<Expr> address, + ObjectPair &result, + bool &success); + + /// Resolve address to a list of ObjectPairs it can point to. If + /// maxResolutions is non-zero then no more than that many pairs + /// will be returned. + /// + /// \return true iff the resolution is incomplete (maxResolutions + /// is non-zero and the search terminated early, or a query timed out). + bool resolve(ExecutionState &state, + TimingSolver *solver, + ref<Expr> address, + ResolutionList &rl, + unsigned maxResolutions=0, + double timeout=0.); + + /***/ + + /// Add a binding to the address space. + void bindObject(const MemoryObject *mo, ObjectState *os); + + /// Remove a binding from the address space. + void unbindObject(const MemoryObject *mo); + + /// Lookup a binding from a MemoryObject. + const ObjectState *findObject(const MemoryObject *mo) const; + + /// \brief Obtain an ObjectState suitable for writing. + /// + /// This returns a writeable object state, creating a new copy of + /// the given ObjectState if necessary. If the address space owns + /// the ObjectState then this routine effectively just strips the + /// const qualifier it. + /// + /// \param mo The MemoryObject to get a writeable ObjectState for. + /// \param os The current binding of the MemoryObject. + /// \return A writeable ObjectState (\a os or a copy). + ObjectState *getWriteable(const MemoryObject *mo, const ObjectState *os); + + /// Copy the concrete values of all managed ObjectStates into the + /// actual system memory location they were allocated at. + void copyOutConcretes(); + + /// Copy the concrete values of all managed ObjectStates back from + /// the actual system memory location they were allocated + /// at. ObjectStates will only be written to (and thus, + /// potentially copied) if the memory values are different from + /// the current concrete values. + /// + /// \retval true The copy succeeded. + /// \retval false The copy failed because a read-only object was modified. + bool copyInConcretes(); + }; +} // End klee namespace + +#endif diff --git a/lib/Core/CallPathManager.cpp b/lib/Core/CallPathManager.cpp new file mode 100644 index 00000000..d0a61b31 --- /dev/null +++ b/lib/Core/CallPathManager.cpp @@ -0,0 +1,103 @@ +//===-- CallPathManager.cpp -----------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CallPathManager.h" + +#include "klee/Statistics.h" + +#include <map> +#include <vector> +#include "llvm/Function.h" +#include "llvm/Support/Streams.h" + +using namespace llvm; +using namespace klee; + +/// + +CallPathNode::CallPathNode(CallPathNode *_parent, + Instruction *_callSite, + Function *_function) + : parent(_parent), + callSite(_callSite), + function(_function), + count(0) { +} + +void CallPathNode::print() { + llvm::cerr << " (Function: " << this->function->getName() << ", " + << "Callsite: " << callSite << ", " + << "Count: " << this->count << ")"; + if (parent && parent->callSite) { + llvm::cerr << ";\n"; + parent->print(); + } + else llvm::cerr << "\n"; +} + +/// + +CallPathManager::CallPathManager() : root(0, 0, 0) { +} + +CallPathManager::~CallPathManager() { + for (std::vector<CallPathNode*>::iterator it = paths.begin(), + ie = paths.end(); it != ie; ++it) + delete *it; +} + +void CallPathManager::getSummaryStatistics(CallSiteSummaryTable &results) { + results.clear(); + + for (std::vector<CallPathNode*>::iterator it = paths.begin(), + ie = paths.end(); it != ie; ++it) + (*it)->summaryStatistics = (*it)->statistics; + + // compute summary bottom up, while building result table + for (std::vector<CallPathNode*>::reverse_iterator it = paths.rbegin(), + ie = paths.rend(); it != ie; ++it) { + CallPathNode *cp = *it; + cp->parent->summaryStatistics += cp->summaryStatistics; + + CallSiteInfo &csi = results[cp->callSite][cp->function]; + csi.count += cp->count; + csi.statistics += cp->summaryStatistics; + } +} + + +CallPathNode *CallPathManager::computeCallPath(CallPathNode *parent, + Instruction *cs, + Function *f) { + for (CallPathNode *p=parent; p; p=p->parent) + if (cs==p->callSite && f==p->function) + return p; + + CallPathNode *cp = new CallPathNode(parent, cs, f); + paths.push_back(cp); + return cp; +} + +CallPathNode *CallPathManager::getCallPath(CallPathNode *parent, + Instruction *cs, + Function *f) { + std::pair<Instruction*,Function*> key(cs, f); + if (!parent) + parent = &root; + + CallPathNode::children_ty::iterator it = parent->children.find(key); + if (it==parent->children.end()) { + CallPathNode *cp = computeCallPath(parent, cs, f); + parent->children.insert(std::make_pair(key, cp)); + return cp; + } else { + return it->second; + } +} + diff --git a/lib/Core/CallPathManager.h b/lib/Core/CallPathManager.h new file mode 100644 index 00000000..2e16d72b --- /dev/null +++ b/lib/Core/CallPathManager.h @@ -0,0 +1,83 @@ +//===-- CallPathManager.h ---------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __UTIL_CALLPATHMANAGER_H__ +#define __UTIL_CALLPATHMANAGER_H__ + +#include "klee/Statistics.h" + +#include <map> +#include <vector> + +namespace llvm { + class Instruction; + class Function; +} + +namespace klee { + class StatisticRecord; + + struct CallSiteInfo { + unsigned count; + StatisticRecord statistics; + + public: + CallSiteInfo() : count(0) {} + }; + + typedef std::map<llvm::Instruction*, + std::map<llvm::Function*, CallSiteInfo> > CallSiteSummaryTable; + + class CallPathNode { + friend class CallPathManager; + + public: + typedef std::map<std::pair<llvm::Instruction*, + llvm::Function*>, CallPathNode*> children_ty; + + // form list of (callSite,function) path + CallPathNode *parent; + llvm::Instruction *callSite; + llvm::Function *function; + children_ty children; + + StatisticRecord statistics; + StatisticRecord summaryStatistics; + unsigned count; + + public: + CallPathNode(CallPathNode *parent, + llvm::Instruction *callSite, + llvm::Function *function); + + void print(); + }; + + class CallPathManager { + CallPathNode root; + std::vector<CallPathNode*> paths; + + private: + CallPathNode *computeCallPath(CallPathNode *parent, + llvm::Instruction *callSite, + llvm::Function *f); + + public: + CallPathManager(); + ~CallPathManager(); + + void getSummaryStatistics(CallSiteSummaryTable &result); + + CallPathNode *getCallPath(CallPathNode *parent, + llvm::Instruction *callSite, + llvm::Function *f); + }; +} + +#endif diff --git a/lib/Core/Common.cpp b/lib/Core/Common.cpp new file mode 100644 index 00000000..479c4465 --- /dev/null +++ b/lib/Core/Common.cpp @@ -0,0 +1,110 @@ +//===-- Common.cpp --------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <assert.h> +#include <string.h> + +#include <set> + +using namespace klee; + +FILE* klee::klee_warning_file = NULL; +FILE* klee::klee_message_file = NULL; + + +/* Prints a message/warning. + + If pfx is NULL, this is a regular message, and it's sent to + klee_message_file (messages.txt). Otherwise, it is sent to + klee_warning_file (warnings.txt). + + Iff onlyToFile is false, the message is also printed on stderr. +*/ +static void klee_vmessage(const char *pfx, bool onlyToFile, const char *msg, va_list ap) { + FILE *f = stderr; + if (!onlyToFile) { + fprintf(f, "KLEE: "); + if (pfx) fprintf(f, "%s: ", pfx); + vfprintf(f, msg, ap); + fprintf(f, "\n"); + fflush(f); + } + + if (pfx == NULL) + f = klee_message_file; + else f = klee_warning_file; + + if (f) { + fprintf(f, "KLEE: "); + if (pfx) fprintf(f, "%s: ", pfx); + vfprintf(f, msg, ap); + fprintf(f, "\n"); + fflush(f); + } +} + + +void klee::klee_message(const char *msg, ...) { + va_list ap; + va_start(ap, msg); + klee_vmessage(NULL, false, msg, ap); + va_end(ap); +} + +/* Message to be written only to file */ +void klee::klee_message_to_file(const char *msg, ...) { + va_list ap; + va_start(ap, msg); + klee_vmessage(NULL, true, msg, ap); + va_end(ap); +} + +void klee::klee_error(const char *msg, ...) { + va_list ap; + va_start(ap, msg); + klee_vmessage("ERROR", false, msg, ap); + va_end(ap); + exit(1); +} + +void klee::klee_warning(const char *msg, ...) { + va_list ap; + va_start(ap, msg); + klee_vmessage("WARNING", false, msg, ap); + va_end(ap); +} + + +/* Prints a warning once per message. */ +void klee::klee_warning_once(const void *id, const char *msg, ...) { + static std::set< std::pair<const void*, const char*> > keys; + std::pair<const void*, const char*> key; + + + /* "calling external" messages contain the actual arguments with + which we called the external function, so we need to ignore them + when computing the key. */ + if (strncmp(msg, "calling external", strlen("calling external")) != 0) + key = std::make_pair(id, msg); + else key = std::make_pair(id, "calling external"); + + if (!keys.count(key)) { + keys.insert(key); + + va_list ap; + va_start(ap, msg); + klee_vmessage("WARNING", false, msg, ap); + va_end(ap); + } +} diff --git a/lib/Core/Common.h b/lib/Core/Common.h new file mode 100644 index 00000000..ce05b536 --- /dev/null +++ b/lib/Core/Common.h @@ -0,0 +1,56 @@ +//===-- Common.h ------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __KLEE_COMMON_H__ +#define __KLEE_COMMON_H__ + +#ifdef __CYGWIN__ +#ifndef WINDOWS +#define WINDOWS +#endif +#endif + +#include <stdio.h> + +// XXX ugh +namespace klee { + class Solver; + + extern FILE* klee_warning_file; + extern FILE* klee_message_file; + + /// Print "KLEE: ERROR" followed by the msg in printf format and a + /// newline on stderr and to warnings.txt, then exit with an error. + void klee_error(const char *msg, ...) + __attribute__ ((format (printf, 1, 2), noreturn)); + + /// Print "KLEE: " followed by the msg in printf format and a + /// newline on stderr and to messages.txt. + void klee_message(const char *msg, ...) + __attribute__ ((format (printf, 1, 2))); + + /// Print "KLEE: " followed by the msg in printf format and a + /// newline to messages.txt. + void klee_message_to_file(const char *msg, ...) + __attribute__ ((format (printf, 1, 2))); + + /// Print "KLEE: WARNING" followed by the msg in printf format and a + /// newline on stderr and to warnings.txt. + void klee_warning(const char *msg, ...) + __attribute__ ((format (printf, 1, 2))); + + /// Print "KLEE: WARNING" followed by the msg in printf format and a + /// newline on stderr and to warnings.txt. However, the warning is only + /// printed once for each unique (id, msg) pair (as pointers). + void klee_warning_once(const void *id, + const char *msg, ...) + __attribute__ ((format (printf, 2, 3))); +} + +#endif /* __KLEE_COMMON_H__ */ diff --git a/lib/Core/CoreStats.cpp b/lib/Core/CoreStats.cpp new file mode 100644 index 00000000..ca2ef1c9 --- /dev/null +++ b/lib/Core/CoreStats.cpp @@ -0,0 +1,29 @@ +//===-- CoreStats.cpp -----------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CoreStats.h" + +using namespace klee; + +Statistic stats::allocations("Allocations", "Alloc"); +Statistic stats::coveredInstructions("CoveredInstructions", "Icov"); +Statistic stats::falseBranches("FalseBranches", "Bf"); +Statistic stats::forkTime("ForkTime", "Ftime"); +Statistic stats::forks("Forks", "Forks"); +Statistic stats::instructionRealTime("InstructionRealTimes", "Ireal"); +Statistic stats::instructionTime("InstructionTimes", "Itime"); +Statistic stats::instructions("Instructions", "I"); +Statistic stats::minDistToReturn("MinDistToReturn", "Rdist"); +Statistic stats::minDistToUncovered("MinDistToUncovered", "UCdist"); +Statistic stats::reachableUncovered("ReachableUncovered", "IuncovReach"); +Statistic stats::resolveTime("ResolveTime", "Rtime"); +Statistic stats::solverTime("SolverTime", "Stime"); +Statistic stats::states("States", "States"); +Statistic stats::trueBranches("TrueBranches", "Bt"); +Statistic stats::uncoveredInstructions("UncoveredInstructions", "Iuncov"); diff --git a/lib/Core/CoreStats.h b/lib/Core/CoreStats.h new file mode 100644 index 00000000..09845a89 --- /dev/null +++ b/lib/Core/CoreStats.h @@ -0,0 +1,53 @@ +//===-- CoreStats.h ---------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_CORESTATS_H +#define KLEE_CORESTATS_H + +#include "klee/Statistic.h" + +namespace klee { +namespace stats { + + extern Statistic allocations; + extern Statistic resolveTime; + extern Statistic instructions; + extern Statistic instructionTime; + extern Statistic instructionRealTime; + extern Statistic coveredInstructions; + extern Statistic uncoveredInstructions; + extern Statistic trueBranches; + extern Statistic falseBranches; + extern Statistic forkTime; + extern Statistic solverTime; + + /// The number of process forks. + extern Statistic forks; + + /// Number of states, this is a "fake" statistic used by istats, it + /// isn't normally up-to-date. + extern Statistic states; + + /// Instruction level statistic for tracking number of reachable + /// uncovered instructions. + extern Statistic reachableUncovered; + + /// Instruction level statistic tracking the minimum intraprocedural + /// distance to an uncovered instruction; this is only periodically + /// updated. + extern Statistic minDistToUncovered; + + /// Instruction level statistic tracking the minimum intraprocedural + /// distance to a function return. + extern Statistic minDistToReturn; + +} +} + +#endif diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp new file mode 100644 index 00000000..dd6d4647 --- /dev/null +++ b/lib/Core/ExecutionState.cpp @@ -0,0 +1,417 @@ +//===-- ExecutionState.cpp ------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/ExecutionState.h" + +#include "klee/Internal/Module/Cell.h" +#include "klee/Internal/Module/InstructionInfoTable.h" +#include "klee/Internal/Module/KInstruction.h" +#include "klee/Internal/Module/KModule.h" + +#include "klee/Expr.h" + +#include "Memory.h" + +#include "llvm/Function.h" +#include "llvm/Support/CommandLine.h" + +#include <iostream> +#include <cassert> +#include <map> +#include <set> +#include <stdarg.h> + +using namespace llvm; +using namespace klee; + +namespace { + cl::opt<bool> + DebugLogStateMerge("debug-log-state-merge"); +} + +/***/ + +StackFrame::StackFrame(KInstIterator _caller, KFunction *_kf) + : caller(_caller), kf(_kf), callPathNode(0), + minDistToUncoveredOnReturn(0), varargs(0) { + locals = new Cell[kf->numRegisters]; +} + +StackFrame::StackFrame(const StackFrame &s) + : caller(s.caller), + kf(s.kf), + callPathNode(s.callPathNode), + allocas(s.allocas), + minDistToUncoveredOnReturn(s.minDistToUncoveredOnReturn), + varargs(s.varargs) { + locals = new Cell[s.kf->numRegisters]; + for (unsigned i=0; i<s.kf->numRegisters; i++) + locals[i] = s.locals[i]; +} + +StackFrame::~StackFrame() { + delete[] locals; +} + +/***/ + +ExecutionState::ExecutionState(KFunction *kf) + : fakeState(false), + underConstrained(false), + depth(0), + pc(kf->instructions), + prevPC(pc), + queryCost(0.), + weight(1), + instsSinceCovNew(0), + coveredNew(false), + forkDisabled(false), + ptreeNode(0) { + pushFrame(0, kf); +} + +ExecutionState::ExecutionState(const std::vector<ref<Expr> > &assumptions) + : fakeState(true), + underConstrained(false), + constraints(assumptions), + queryCost(0.), + ptreeNode(0) { +} + +ExecutionState::~ExecutionState() { + while (!stack.empty()) popFrame(); +} + +ExecutionState *ExecutionState::branch() { + depth++; + + ExecutionState *falseState = new ExecutionState(*this); + falseState->coveredNew = false; + falseState->coveredLines.clear(); + + weight *= .5; + falseState->weight -= weight; + + return falseState; +} + +void ExecutionState::pushFrame(KInstIterator caller, KFunction *kf) { + stack.push_back(StackFrame(caller,kf)); +} + +void ExecutionState::popFrame() { + StackFrame &sf = stack.back(); + for (std::vector<const MemoryObject*>::iterator it = sf.allocas.begin(), + ie = sf.allocas.end(); it != ie; ++it) + addressSpace.unbindObject(*it); + stack.pop_back(); +} + +/// + +std::string ExecutionState::getFnAlias(std::string fn) { + std::map < std::string, std::string >::iterator it = fnAliases.find(fn); + if (it != fnAliases.end()) + return it->second; + else return ""; +} + +void ExecutionState::addFnAlias(std::string old_fn, std::string new_fn) { + fnAliases[old_fn] = new_fn; +} + +void ExecutionState::removeFnAlias(std::string fn) { + fnAliases.erase(fn); +} + +/**/ + +std::ostream &klee::operator<<(std::ostream &os, const MemoryMap &mm) { + os << "{"; + MemoryMap::iterator it = mm.begin(); + MemoryMap::iterator ie = mm.end(); + if (it!=ie) { + os << "MO" << it->first->id << ":" << it->second; + for (++it; it!=ie; ++it) + os << ", MO" << it->first->id << ":" << it->second; + } + os << "}"; + return os; +} + +bool ExecutionState::merge(const ExecutionState &b) { + if (DebugLogStateMerge) + llvm::cerr << "-- attempting merge of A:" + << this << " with B:" << &b << "--\n"; + if (pc != b.pc) + return false; + + // XXX is it even possible for these to differ? does it matter? probably + // implies difference in object states? + if (symbolics!=b.symbolics) + return false; + + { + std::vector<StackFrame>::const_iterator itA = stack.begin(); + std::vector<StackFrame>::const_iterator itB = b.stack.begin(); + while (itA!=stack.end() && itB!=b.stack.end()) { + // XXX vaargs? + if (itA->caller!=itB->caller || itA->kf!=itB->kf) + return false; + ++itA; + ++itB; + } + if (itA!=stack.end() || itB!=b.stack.end()) + return false; + } + + std::set< ref<Expr> > aConstraints(constraints.begin(), constraints.end()); + std::set< ref<Expr> > bConstraints(b.constraints.begin(), + b.constraints.end()); + std::set< ref<Expr> > commonConstraints, aSuffix, bSuffix; + std::set_intersection(aConstraints.begin(), aConstraints.end(), + bConstraints.begin(), bConstraints.end(), + std::inserter(commonConstraints, commonConstraints.begin())); + std::set_difference(aConstraints.begin(), aConstraints.end(), + commonConstraints.begin(), commonConstraints.end(), + std::inserter(aSuffix, aSuffix.end())); + std::set_difference(bConstraints.begin(), bConstraints.end(), + commonConstraints.begin(), commonConstraints.end(), + std::inserter(bSuffix, bSuffix.end())); + if (DebugLogStateMerge) { + llvm::cerr << "\tconstraint prefix: ["; + for (std::set< ref<Expr> >::iterator it = commonConstraints.begin(), + ie = commonConstraints.end(); it != ie; ++it) + llvm::cerr << *it << ", "; + llvm::cerr << "]\n"; + llvm::cerr << "\tA suffix: ["; + for (std::set< ref<Expr> >::iterator it = aSuffix.begin(), + ie = aSuffix.end(); it != ie; ++it) + llvm::cerr << *it << ", "; + llvm::cerr << "]\n"; + llvm::cerr << "\tB suffix: ["; + for (std::set< ref<Expr> >::iterator it = bSuffix.begin(), + ie = bSuffix.end(); it != ie; ++it) + llvm::cerr << *it << ", "; + llvm::cerr << "]\n"; + } + + // We cannot merge if addresses would resolve differently in the + // states. This means: + // + // 1. Any objects created since the branch in either object must + // have been free'd. + // + // 2. We cannot have free'd any pre-existing object in one state + // and not the other + + if (DebugLogStateMerge) { + llvm::cerr << "\tchecking object states\n"; + llvm::cerr << "A: " << addressSpace.objects << "\n"; + llvm::cerr << "B: " << b.addressSpace.objects << "\n"; + } + + std::set<const MemoryObject*> mutated; + MemoryMap::iterator ai = addressSpace.objects.begin(); + MemoryMap::iterator bi = b.addressSpace.objects.begin(); + MemoryMap::iterator ae = addressSpace.objects.end(); + MemoryMap::iterator be = b.addressSpace.objects.end(); + for (; ai!=ae && bi!=be; ++ai, ++bi) { + if (ai->first != bi->first) { + if (DebugLogStateMerge) { + if (ai->first < bi->first) { + llvm::cerr << "\t\tB misses binding for: " << ai->first->id << "\n"; + } else { + llvm::cerr << "\t\tA misses binding for: " << bi->first->id << "\n"; + } + } + return false; + } + if (ai->second != bi->second) { + if (DebugLogStateMerge) + llvm::cerr << "\t\tmutated: " << ai->first->id << "\n"; + mutated.insert(ai->first); + } + } + if (ai!=ae || bi!=be) { + if (DebugLogStateMerge) + llvm::cerr << "\t\tmappings differ\n"; + return false; + } + + // merge stack + + ref<Expr> inA(1, Expr::Bool), inB(1, Expr::Bool); + for (std::set< ref<Expr> >::iterator it = aSuffix.begin(), + ie = aSuffix.end(); it != ie; ++it) + inA = AndExpr::create(inA, *it); + for (std::set< ref<Expr> >::iterator it = bSuffix.begin(), + ie = bSuffix.end(); it != ie; ++it) + inB = AndExpr::create(inB, *it); + + // XXX should we have a preference as to which predicate to use? + // it seems like it can make a difference, even though logically + // they must contradict each other and so inA => !inB + + std::vector<StackFrame>::iterator itA = stack.begin(); + std::vector<StackFrame>::const_iterator itB = b.stack.begin(); + for (; itA!=stack.end(); ++itA, ++itB) { + StackFrame &af = *itA; + const StackFrame &bf = *itB; + for (unsigned i=0; i<af.kf->numRegisters; i++) { + ref<Expr> &av = af.locals[i].value; + const ref<Expr> &bv = bf.locals[i].value; + if (av.isNull() || bv.isNull()) { + // if one is null then by implication (we are at same pc) + // we cannot reuse this local, so just ignore + } else { + av = SelectExpr::create(inA, av, bv); + } + } + } + + for (std::set<const MemoryObject*>::iterator it = mutated.begin(), + ie = mutated.end(); it != ie; ++it) { + const MemoryObject *mo = *it; + const ObjectState *os = addressSpace.findObject(mo); + const ObjectState *otherOS = b.addressSpace.findObject(mo); + assert(os && !os->readOnly && + "objects mutated but not writable in merging state"); + assert(otherOS); + + ObjectState *wos = addressSpace.getWriteable(mo, os); + for (unsigned i=0; i<mo->size; i++) { + ref<Expr> av = wos->read8(i); + ref<Expr> bv = otherOS->read8(i); + wos->write(i, SelectExpr::create(inA, av, bv)); + } + } + + constraints = ConstraintManager(); + for (std::set< ref<Expr> >::iterator it = commonConstraints.begin(), + ie = commonConstraints.end(); it != ie; ++it) + constraints.addConstraint(*it); + constraints.addConstraint(OrExpr::create(inA, inB)); + + return true; +} + +/**/ + +/* + Used for tainting: create a clone of os that we can revirt to with + the behavior that all constraints are preserved, but writes are + discarded. When we revirt it will be at the same address. + */ +ObjectState *ExecutionState::cloneObject(ObjectState *os, + MemoryObject *mo) { + MemoryMap::iterator it = shadowObjects.find(mo); + if (it != shadowObjects.end()) + assert(0 && "Cannot exist already!"); + + llvm::cerr << "DRE: Inserting a cloned object: " << mo << "\n"; + shadowObjects = shadowObjects.replace(std::make_pair(mo, os)); + os = new ObjectState(*os); + addressSpace.bindObject(mo, os); + return os; +} + +/***/ + + +ExecutionTraceEvent::ExecutionTraceEvent(ExecutionState& state, + KInstruction* ki) + : consecutiveCount(1) +{ + file = ki->info->file; + line = ki->info->line; + funcName = state.stack.back().kf->function->getName(); + stackDepth = state.stack.size(); +} + +bool ExecutionTraceEvent::ignoreMe() const { + // ignore all events occurring in certain pesky uclibc files: + if (file.find("libc/stdio/") != std::string::npos) { + return true; + } + + return false; +} + +void ExecutionTraceEvent::print(std::ostream &os) const { + os.width(stackDepth); + os << ' '; + printDetails(os); + os << ' ' << file << ':' << line << ':' << funcName; + if (consecutiveCount > 1) + os << " (" << consecutiveCount << "x)\n"; + else + os << '\n'; +} + + +bool ExecutionTraceEventEquals(ExecutionTraceEvent* e1, ExecutionTraceEvent* e2) { + // first see if their base class members are identical: + if (!((e1->file == e2->file) && + (e1->line == e2->line) && + (e1->funcName == e2->funcName))) + return false; + + // fairly ugly, but i'm no OOP master, so this is the way i'm + // doing it for now ... lemme know if there's a cleaner way: + BranchTraceEvent* be1 = dynamic_cast<BranchTraceEvent*>(e1); + BranchTraceEvent* be2 = dynamic_cast<BranchTraceEvent*>(e2); + if (be1 && be2) { + return ((be1->trueTaken == be2->trueTaken) && + (be1->canForkGoBothWays == be2->canForkGoBothWays)); + } + + // don't tolerate duplicates in anything else: + return false; +} + + +void BranchTraceEvent::printDetails(std::ostream &os) const { + os << "BRANCH " << (trueTaken ? "T" : "F") << ' ' << + (canForkGoBothWays ? "2-way" : "1-way"); +} + +void ExecutionTraceManager::addEvent(ExecutionTraceEvent* evt) { + // don't trace anything before __user_main, except for global events + if (!hasSeenUserMain) { + if (evt->funcName == "__user_main") { + hasSeenUserMain = true; + } + else if (evt->funcName != "global_def") { + return; + } + } + + // custom ignore events: + if (evt->ignoreMe()) + return; + + if (events.size() > 0) { + // compress consecutive duplicates: + ExecutionTraceEvent* last = events.back(); + if (ExecutionTraceEventEquals(last, evt)) { + last->consecutiveCount++; + return; + } + } + + events.push_back(evt); +} + +void ExecutionTraceManager::printAllEvents(std::ostream &os) const { + for (unsigned i = 0; i != events.size(); ++i) + events[i]->print(os); +} + +/***/ diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp new file mode 100644 index 00000000..d3409908 --- /dev/null +++ b/lib/Core/Executor.cpp @@ -0,0 +1,3260 @@ +//===-- Executor.cpp ------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "Executor.h" + +#include "CoreStats.h" +#include "ExternalDispatcher.h" +#include "ImpliedValue.h" +#include "Memory.h" +#include "MemoryManager.h" +#include "PTree.h" +#include "Searcher.h" +#include "SeedInfo.h" +#include "SpecialFunctionHandler.h" +#include "StatsTracker.h" +#include "TimingSolver.h" +#include "UserSearcher.h" +#include "../Solver/SolverStats.h" + +#include "klee/ExecutionState.h" +#include "klee/Expr.h" +#include "klee/Interpreter.h" +#include "klee/Machine.h" +#include "klee/TimerStatIncrementer.h" +#include "klee/util/Assignment.h" +#include "klee/util/ExprPPrinter.h" +#include "klee/util/ExprUtil.h" +#include "klee/Config/config.h" +#include "klee/Internal/ADT/BOut.h" +#include "klee/Internal/ADT/RNG.h" +#include "klee/Internal/Module/Cell.h" +#include "klee/Internal/Module/InstructionInfoTable.h" +#include "klee/Internal/Module/KInstruction.h" +#include "klee/Internal/Module/KModule.h" +#include "klee/Internal/Support/FloatEvaluation.h" +#include "klee/Internal/System/Time.h" + +#include "llvm/Attributes.h" +#include "llvm/BasicBlock.h" +#include "llvm/Constants.h" +#include "llvm/Function.h" +#include "llvm/Instructions.h" +#include "llvm/IntrinsicInst.h" +#include "llvm/Module.h" +#include "llvm/Support/CallSite.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/GetElementPtrTypeIterator.h" +#include "llvm/System/Process.h" +#include "llvm/Target/TargetData.h" + +#include <cassert> +#include <algorithm> +#include <iostream> +#include <iomanip> +#include <fstream> +#include <sstream> +#include <vector> +#include <string> + +#include <sys/mman.h> + +#include <errno.h> +#include <cxxabi.h> + +using namespace llvm; +using namespace klee; + +// omg really hard to share cl opts across files ... +bool WriteTraces = false; + +namespace { + cl::opt<bool> + DumpStatesOnHalt("dump-states-on-halt", + cl::init(true)); + + cl::opt<bool> + NoPreferCex("no-prefer-cex", + cl::init(false)); + + cl::opt<bool> + UseAsmAddresses("use-asm-addresses", + cl::init(false)); + + cl::opt<bool> + RandomizeFork("randomize-fork", + cl::init(false)); + + cl::opt<bool> + AllowExternalSymCalls("allow-external-sym-calls", + cl::init(false)); + + cl::opt<bool> + DebugPrintInstructions("debug-print-instructions", + cl::desc("Print instructions during execution.")); + + cl::opt<bool> + DebugCheckForImpliedValues("debug-check-for-implied-values"); + + + cl::opt<bool> + SimplifySymIndices("simplify-sym-indices", + cl::init(false)); + + cl::opt<unsigned> + MaxSymArraySize("max-sym-array-size", + cl::init(0)); + + cl::opt<bool> + DebugValidateSolver("debug-validate-solver", + cl::init(false)); + + cl::opt<bool> + SuppressExternalWarnings("suppress-external-warnings"); + + cl::opt<bool> + AllExternalWarnings("all-external-warnings"); + + cl::opt<bool> + OnlyOutputStatesCoveringNew("only-output-states-covering-new", + cl::init(false)); + + cl::opt<bool> + AlwaysOutputSeeds("always-output-seeds", + cl::init(true)); + + cl::opt<bool> + UseFastCexSolver("use-fast-cex-solver", + cl::init(false)); + + cl::opt<bool> + UseIndependentSolver("use-independent-solver", + cl::init(true), + cl::desc("Use constraint independence")); + + cl::opt<bool> + EmitAllErrors("emit-all-errors", + cl::init(false), + cl::desc("Generate tests cases for all errors " + "(default=one per (error,instruction) pair)")); + + cl::opt<bool> + UseCexCache("use-cex-cache", + cl::init(true), + cl::desc("Use counterexample caching")); + + cl::opt<bool> + UseQueryLog("use-query-log", + cl::init(false)); + + cl::opt<bool> + UseQueryPCLog("use-query-pc-log", + cl::init(false)); + + cl::opt<bool> + UseSTPQueryPCLog("use-stp-query-pc-log", + cl::init(false)); + + cl::opt<bool> + NoExternals("no-externals", + cl::desc("Do not allow external functin calls")); + + cl::opt<bool> + UseCache("use-cache", + cl::init(true), + cl::desc("Use validity caching")); + + cl::opt<bool> + OnlyReplaySeeds("only-replay-seeds", + cl::desc("Discard states that do not have a seed.")); + + cl::opt<bool> + OnlySeed("only-seed", + cl::desc("Stop execution after seeding is done without doing regular search.")); + + cl::opt<bool> + AllowSeedExtension("allow-seed-extension", + cl::desc("Allow extra (unbound) values to become symbolic during seeding.")); + + cl::opt<bool> + ZeroSeedExtension("zero-seed-extension"); + + cl::opt<bool> + AllowSeedTruncation("allow-seed-truncation", + cl::desc("Allow smaller buffers than in seeds.")); + + cl::opt<bool> + NamedSeedMatching("named-seed-matching", + cl::desc("Use names to match symbolic objects to inputs.")); + + cl::opt<double> + MaxStaticForkPct("max-static-fork-pct", cl::init(1.)); + cl::opt<double> + MaxStaticSolvePct("max-static-solve-pct", cl::init(1.)); + cl::opt<double> + MaxStaticCPForkPct("max-static-cpfork-pct", cl::init(1.)); + cl::opt<double> + MaxStaticCPSolvePct("max-static-cpsolve-pct", cl::init(1.)); + + cl::opt<double> + MaxInstructionTime("max-instruction-time", + cl::desc("Only allow a single instruction to take this much time (default=0 (off))"), + cl::init(0)); + + cl::opt<double> + SeedTime("seed-time", + cl::desc("Amount of time to dedicate to seeds, before normal search (default=0 (off))"), + cl::init(0)); + + cl::opt<double> + MaxSTPTime("max-stp-time", + cl::desc("Maximum amount of time for a single query (default=120s)"), + cl::init(120.0)); + + cl::opt<unsigned int> + StopAfterNInstructions("stop-after-n-instructions", + cl::desc("Stop execution after specified number of instructions (0=off)"), + cl::init(0)); + + cl::opt<unsigned> + MaxForks("max-forks", + cl::desc("Only fork this many times (-1=off)"), + cl::init(~0u)); + + cl::opt<unsigned> + MaxDepth("max-depth", + cl::desc("Only allow this many symbolic branches (0=off)"), + cl::init(0)); + + cl::opt<unsigned> + MaxMemory("max-memory", + cl::desc("Refuse to fork when more above this about of memory (in MB, 0=off)"), + cl::init(0)); + + cl::opt<bool> + MaxMemoryInhibit("max-memory-inhibit", + cl::desc("Inhibit forking at memory cap (vs. random terminat)"), + cl::init(true)); + + // use 'external storage' because also needed by tools/klee/main.cpp + cl::opt<bool, true> + WriteTracesProxy("write-traces", + cl::desc("Write .trace file for each terminated state"), + cl::location(WriteTraces), + cl::init(false)); + + cl::opt<bool> + UseForkedSTP("use-forked-stp", + cl::desc("Run STP in forked process")); +} + + +static void *theMMap = 0; +static unsigned theMMapSize = 0; + +namespace klee { + RNG theRNG; +} + +Solver *constructSolverChain(STPSolver *stpSolver, + std::string queryLogPath, + std::string stpQueryLogPath, + std::string queryPCLogPath, + std::string stpQueryPCLogPath) { + Solver *solver = stpSolver; + + if (UseSTPQueryPCLog) + solver = createPCLoggingSolver(solver, + stpQueryLogPath); + + if (UseFastCexSolver) + solver = createFastCexSolver(solver); + + if (UseCexCache) + solver = createCexCachingSolver(solver); + + if (UseCache) + solver = createCachingSolver(solver); + + if (UseIndependentSolver) + solver = createIndependentSolver(solver); + + if (DebugValidateSolver) + solver = createValidatingSolver(solver, stpSolver); + + if (UseQueryPCLog) + solver = createPCLoggingSolver(solver, + queryPCLogPath); + + return solver; +} + +Executor::Executor(const InterpreterOptions &opts, + InterpreterHandler *ih) + : Interpreter(opts), + kmodule(0), + interpreterHandler(ih), + searcher(0), + externalDispatcher(new ExternalDispatcher()), + statsTracker(0), + pathWriter(0), + symPathWriter(0), + specialFunctionHandler(0), + processTree(0), + replayOut(0), + replayPath(0), + usingSeeds(0), + atMemoryLimit(false), + inhibitForking(false), + haltExecution(false), + ivcEnabled(false), + stpTimeout(std::min(MaxSTPTime,MaxInstructionTime)) { + STPSolver *stpSolver = new STPSolver(UseForkedSTP); + Solver *solver = + constructSolverChain(stpSolver, + interpreterHandler->getOutputFilename("queries.qlog"), + interpreterHandler->getOutputFilename("stp-queries.qlog"), + interpreterHandler->getOutputFilename("queries.pc"), + interpreterHandler->getOutputFilename("stp-queries.pc")); + + this->solver = new TimingSolver(solver, stpSolver); + + memory = new MemoryManager(); +} + + +const Module *Executor::setModule(llvm::Module *module, + const ModuleOptions &opts) { + assert(!kmodule && module && "can only register one module"); // XXX gross + + kmodule = new KModule(module); + + specialFunctionHandler = new SpecialFunctionHandler(*this); + + specialFunctionHandler->prepare(); + kmodule->prepare(opts, interpreterHandler); + specialFunctionHandler->bind(); + + if (StatsTracker::useStatistics()) { + statsTracker = + new StatsTracker(*this, + interpreterHandler->getOutputFilename("assembly.ll"), + userSearcherRequiresMD2U()); + } + + return module; +} + +Executor::~Executor() { + delete memory; + delete externalDispatcher; + if (processTree) + delete processTree; + if (specialFunctionHandler) + delete specialFunctionHandler; + if (statsTracker) + delete statsTracker; + delete solver; + delete kmodule; +} + +/***/ + +void Executor::initializeGlobalObject(ExecutionState &state, ObjectState *os, + Constant *c, + unsigned offset) { + TargetData *targetData = kmodule->targetData; + if (ConstantVector *cp = dyn_cast<ConstantVector>(c)) { + unsigned elementSize = + targetData->getTypeStoreSize(cp->getType()->getElementType()); + for (unsigned i=0, e=cp->getNumOperands(); i != e; ++i) + initializeGlobalObject(state, os, cp->getOperand(i), + offset + i*elementSize); + } else if (isa<ConstantAggregateZero>(c)) { + unsigned i, size = targetData->getTypeStoreSize(c->getType()); + for (i=0; i<size; i++) + os->write8(offset+i, (uint8_t) 0); + } else if (ConstantArray *ca = dyn_cast<ConstantArray>(c)) { + unsigned elementSize = + targetData->getTypeStoreSize(ca->getType()->getElementType()); + for (unsigned i=0, e=ca->getNumOperands(); i != e; ++i) + initializeGlobalObject(state, os, ca->getOperand(i), + offset + i*elementSize); + } else if (ConstantStruct *cs = dyn_cast<ConstantStruct>(c)) { + const StructLayout *sl = + targetData->getStructLayout(cast<StructType>(cs->getType())); + for (unsigned i=0, e=cs->getNumOperands(); i != e; ++i) + initializeGlobalObject(state, os, cs->getOperand(i), + offset + sl->getElementOffset(i)); + } else { + os->write(offset, evalConstant(c)); + } +} + +MemoryObject * Executor::addExternalObject(ExecutionState &state, + void *addr, unsigned size, + bool isReadOnly) { + MemoryObject *mo = memory->allocateFixed((uint64_t) (unsigned long) addr, + size, 0); + ObjectState *os = bindObjectInState(state, mo, false); + for(unsigned i = 0; i < size; i++) + os->write8(i, ((uint8_t*)addr)[i]); + if(isReadOnly) + os->setReadOnly(true); + return mo; +} + +void Executor::initializeGlobals(ExecutionState &state) { + Module *m = kmodule->module; + + if (m->getModuleInlineAsm() != "") + klee_warning("executable has module level assembly (ignoring)"); + + assert(m->lib_begin() == m->lib_end() && + "XXX do not support dependent libraries"); + + // represent function globals using the address of the actual llvm function + // object. given that we use malloc to allocate memory in states this also + // ensures that we won't conflict. we don't need to allocate a memory object + // since reading/writing via a function pointer is unsupported anyway. + for (Module::iterator i = m->begin(), ie = m->end(); i != ie; ++i) { + Function *f = i; + ref<Expr> addr(0); + + // If the symbol has external weak linkage then it is implicitly + // not defined in this module; if it isn't resolvable then it + // should be null. + if (f->hasExternalWeakLinkage() && + !externalDispatcher->resolveSymbol(f->getName())) { + addr = Expr::createPointer(0); + } else { + addr = Expr::createPointer((unsigned long) (void*) f); + legalFunctions.insert(f); + } + + globalAddresses.insert(std::make_pair(f, addr)); + } + + // Disabled, we don't want to promote use of live externals. +#ifdef HAVE_CTYPE_EXTERNALS +#ifndef WINDOWS +#ifndef DARWIN + /* From /usr/include/errno.h: it [errno] is a per-thread variable. */ + int *errno_addr = __errno_location(); + addExternalObject(state, (void *)errno_addr, sizeof *errno_addr, false); + + /* from /usr/include/ctype.h: + These point into arrays of 384, so they can be indexed by any `unsigned + char' value [0,255]; by EOF (-1); or by any `signed char' value + [-128,-1). ISO C requires that the ctype functions work for `unsigned */ + const uint16_t **addr = __ctype_b_loc(); + addExternalObject(state, (void *)(*addr-128), + 384 * sizeof **addr, true); + addExternalObject(state, addr, 4, true); + + const int32_t **lower_addr = __ctype_tolower_loc(); + addExternalObject(state, (void *)(*lower_addr-128), + 384 * sizeof **lower_addr, true); + addExternalObject(state, lower_addr, 4, true); + + const int32_t **upper_addr = __ctype_toupper_loc(); + addExternalObject(state, (void *)(*upper_addr-128), + 384 * sizeof **upper_addr, true); + addExternalObject(state, upper_addr, 4, true); +#endif +#endif +#endif + + // allocate and initialize globals, done in two passes since we may + // need address of a global in order to initialize some other one. + + // allocate memory objects for all globals + for (Module::const_global_iterator i = m->global_begin(), + e = m->global_end(); + i != e; ++i) { + if (i->isDeclaration()) { + // FIXME: We have no general way of handling unknown external + // symbols. If we really cared about making external stuff work + // better we could support user definition, or use the EXE style + // hack where we check the object file information. + + const Type *ty = i->getType()->getElementType(); + const std::string &name = i->getName(); + uint64_t size = kmodule->targetData->getTypeStoreSize(ty); + + // XXX - DWD - hardcode some things until we decide how to fix. +#ifndef WINDOWS + if (name == "_ZTVN10__cxxabiv117__class_type_infoE") { + size = 0x2C; + } else if (name == "_ZTVN10__cxxabiv120__si_class_type_infoE") { + size = 0x2C; + } else if (name == "_ZTVN10__cxxabiv121__vmi_class_type_infoE") { + size = 0x2C; + } +#endif + + if (size == 0) { + llvm::cerr << "Unable to find size for global variable: " << i->getName() + << " (use will result in out of bounds access)\n"; + } + + MemoryObject *mo = memory->allocate(size, false, true, i); + ObjectState *os = bindObjectInState(state, mo, false); + globalObjects.insert(std::make_pair(i, mo)); + globalAddresses.insert(std::make_pair(i, mo->getBaseExpr())); + + // Program already running = object already initialized. Read + // concrete value and write it to our copy. + if (size) { + void *addr; + if (name=="__dso_handle") { + extern void *__dso_handle __attribute__ ((__weak__)); + addr = &__dso_handle; // wtf ? + } else { + addr = externalDispatcher->resolveSymbol(name); + } + if (!addr) + klee_error("unable to load symbol(%s) while initializing globals.", + name.c_str()); + + for (unsigned offset=0; offset<mo->size; offset++) + os->write8(offset, ((unsigned char*)addr)[offset]); + } + } else { + const std::string &name = i->getName(); + const Type *ty = i->getType()->getElementType(); + uint64_t size = kmodule->targetData->getTypeStoreSize(ty); + MemoryObject *mo = 0; + + if (UseAsmAddresses && name[0]=='\01') { + char *end; + uint64_t address = ::strtoll(name.c_str()+1, &end, 0); + + if (end && *end == '\0') { + klee_message("NOTE: allocated global at asm specified address: %#08llx" + " (%llu bytes)", + address, size); + mo = memory->allocateFixed(address, size, &*i); + mo->isUserSpecified = true; // XXX hack; + } + } + + if (!mo) + mo = memory->allocate(size, false, true, &*i); + assert(mo && "out of memory"); + ObjectState *os = bindObjectInState(state, mo, false); + globalObjects.insert(std::make_pair(i, mo)); + globalAddresses.insert(std::make_pair(i, mo->getBaseExpr())); + + if (!i->hasInitializer()) + os->initializeToRandom(); + } + } + + // link aliases to their definitions (if bound) + for (Module::alias_iterator i = m->alias_begin(), ie = m->alias_end(); + i != ie; ++i) { + // Map the alias to its aliasee's address. This works because we have + // addresses for everything, even undefined functions. + globalAddresses.insert(std::make_pair(i, evalConstant(i->getAliasee()))); + } + + // once all objects are allocated, do the actual initialization + for (Module::const_global_iterator i = m->global_begin(), + e = m->global_end(); + i != e; ++i) { + if (i->hasInitializer()) { + MemoryObject *mo = globalObjects.find(i)->second; + const ObjectState *os = state.addressSpace.findObject(mo); + assert(os); + ObjectState *wos = state.addressSpace.getWriteable(mo, os); + + initializeGlobalObject(state, wos, i->getInitializer(), 0); + // if(i->isConstant()) os->setReadOnly(true); + } + } +} + +void Executor::branch(ExecutionState &state, + const std::vector< ref<Expr> > &conditions, + std::vector<ExecutionState*> &result) { + TimerStatIncrementer timer(stats::forkTime); + unsigned N = conditions.size(); + assert(N); + + stats::forks += N-1; + + // XXX do proper balance or keep random? + result.push_back(&state); + for (unsigned i=1; i<N; ++i) { + ExecutionState *es = result[theRNG.getInt32() % i]; + ExecutionState *ns = es->branch(); + addedStates.insert(ns); + result.push_back(ns); + es->ptreeNode->data = 0; + std::pair<PTree::Node*,PTree::Node*> res = + processTree->split(es->ptreeNode, ns, es); + ns->ptreeNode = res.first; + es->ptreeNode = res.second; + } + + // If necessary redistribute seeds to match conditions, killing + // states if necessary due to OnlyReplaySeeds (inefficient but + // simple). + + std::map< ExecutionState*, std::vector<SeedInfo> >::iterator it = + seedMap.find(&state); + if (it != seedMap.end()) { + std::vector<SeedInfo> seeds = it->second; + seedMap.erase(it); + + // Assume each seed only satisfies one condition (necessarily true + // when conditions are mutually exclusive and their conjunction is + // a tautology). + for (std::vector<SeedInfo>::iterator siit = seeds.begin(), + siie = seeds.end(); siit != siie; ++siit) { + unsigned i; + for (i=0; i<N; ++i) { + ref<Expr> res; + bool success = + solver->getValue(state, siit->assignment.evaluate(conditions[i]), + res); + assert(success && "FIXME: Unhandled solver failure"); + if (res.getConstantValue()) + break; + } + + // If we didn't find a satisfying condition randomly pick one + // (the seed will be patched). + if (i==N) + i = theRNG.getInt32() % N; + + seedMap[result[i]].push_back(*siit); + } + + if (OnlyReplaySeeds) { + for (unsigned i=0; i<N; ++i) { + if (!seedMap.count(result[i])) { + terminateState(*result[i]); + result[i] = NULL; + } + } + } + } + + for (unsigned i=0; i<N; ++i) + if (result[i]) + addConstraint(*result[i], conditions[i]); +} + +Executor::StatePair +Executor::fork(ExecutionState ¤t, ref<Expr> condition, bool isInternal) { + Solver::Validity res; + std::map< ExecutionState*, std::vector<SeedInfo> >::iterator it = + seedMap.find(¤t); + bool isSeeding = it != seedMap.end(); + + if (!isSeeding && + !condition.isConstant() && + (MaxStaticForkPct!=1. || MaxStaticSolvePct != 1. || + MaxStaticCPForkPct!=1. || MaxStaticCPSolvePct != 1.) && + statsTracker->elapsed() > 60.) { + StatisticManager &sm = *theStatisticManager; + CallPathNode *cpn = current.stack.back().callPathNode; + if ((MaxStaticForkPct<1. && + sm.getIndexedValue(stats::forks, sm.getIndex()) > + stats::forks*MaxStaticForkPct) || + (MaxStaticCPForkPct<1. && + cpn && (cpn->statistics.getValue(stats::forks) > + stats::forks*MaxStaticCPForkPct)) || + (MaxStaticSolvePct<1 && + sm.getIndexedValue(stats::solverTime, sm.getIndex()) > + stats::solverTime*MaxStaticSolvePct) || + (MaxStaticCPForkPct<1. && + cpn && (cpn->statistics.getValue(stats::solverTime) > + stats::solverTime*MaxStaticCPSolvePct))) { + ref<Expr> value; + bool success = solver->getValue(current, condition, value); + assert(success && "FIXME: Unhandled solver failure"); + addConstraint(current, EqExpr::create(value, condition)); + condition = value; + } + } + + double timeout = stpTimeout; + if (isSeeding) + timeout *= it->second.size(); + solver->setTimeout(timeout); + bool success = solver->evaluate(current, condition, res); + solver->setTimeout(0); + if (!success) { + current.pc = current.prevPC; + terminateStateEarly(current, "query timed out"); + return StatePair(0, 0); + } + + if (!isSeeding) { + if (replayPath && !isInternal) { + assert(replayPosition<replayPath->size() && + "ran out of branches in replay path mode"); + bool branch = (*replayPath)[replayPosition++]; + + if (res==Solver::True) { + assert(branch && "hit invalid branch in replay path mode"); + } else if (res==Solver::False) { + assert(!branch && "hit invalid branch in replay path mode"); + } else { + // add constraints + if(branch) { + res = Solver::True; + addConstraint(current, condition); + } else { + res = Solver::False; + addConstraint(current, Expr::createNot(condition)); + } + } + } else if (res==Solver::Unknown) { + assert(!replayOut && "in replay mode, only one branch can be true."); + + if ((MaxMemoryInhibit && atMemoryLimit) || + current.forkDisabled || + inhibitForking || + (MaxForks!=~0u && stats::forks >= MaxForks)) { + TimerStatIncrementer timer(stats::forkTime); + if (theRNG.getBool()) { + addConstraint(current, condition); + res = Solver::True; + } else { + addConstraint(current, Expr::createNot(condition)); + res = Solver::False; + } + } + } + } + + // Fix branch in only-replay-seed mode, if we don't have both true + // and false seeds. + if (isSeeding && + (current.forkDisabled || OnlyReplaySeeds) && + res == Solver::Unknown) { + bool trueSeed=false, falseSeed=false; + // Is seed extension still ok here? + for (std::vector<SeedInfo>::iterator siit = it->second.begin(), + siie = it->second.end(); siit != siie; ++siit) { + ref<Expr> res; + bool success = + solver->getValue(current, siit->assignment.evaluate(condition), res); + assert(success && "FIXME: Unhandled solver failure"); + if (res.isConstant()) { + if (res.getConstantValue()) { + trueSeed = true; + } else { + falseSeed = true; + } + if (trueSeed && falseSeed) + break; + } + } + if (!(trueSeed && falseSeed)) { + assert(trueSeed || falseSeed); + + res = trueSeed ? Solver::True : Solver::False; + addConstraint(current, trueSeed ? condition : Expr::createNot(condition)); + } + } + + + // XXX - even if the constraint is provable one way or the other we + // can probably benefit by adding this constraint and allowing it to + // reduce the other constraints. For example, if we do a binary + // search on a particular value, and then see a comparison against + // the value it has been fixed at, we should take this as a nice + // hint to just use the single constraint instead of all the binary + // search ones. If that makes sense. + if (res==Solver::True) { + if (!isInternal) { + if (pathWriter) { + current.pathOS << "1"; + } + } + + return StatePair(¤t, 0); + } else if (res==Solver::False) { + if (!isInternal) { + if (pathWriter) { + current.pathOS << "0"; + } + } + + return StatePair(0, ¤t); + } else { + TimerStatIncrementer timer(stats::forkTime); + ExecutionState *falseState, *trueState = ¤t; + + ++stats::forks; + + falseState = trueState->branch(); + addedStates.insert(falseState); + + if (RandomizeFork && theRNG.getBool()) + std::swap(trueState, falseState); + + if (it != seedMap.end()) { + std::vector<SeedInfo> seeds = it->second; + it->second.clear(); + std::vector<SeedInfo> &trueSeeds = seedMap[trueState]; + std::vector<SeedInfo> &falseSeeds = seedMap[falseState]; + for (std::vector<SeedInfo>::iterator siit = seeds.begin(), + siie = seeds.end(); siit != siie; ++siit) { + ref<Expr> res; + bool success = + solver->getValue(current, siit->assignment.evaluate(condition), res); + assert(success && "FIXME: Unhandled solver failure"); + if (res.getConstantValue()) { + trueSeeds.push_back(*siit); + } else { + falseSeeds.push_back(*siit); + } + } + + bool swapInfo = false; + if (trueSeeds.empty()) { + if (¤t == trueState) swapInfo = true; + seedMap.erase(trueState); + } + if (falseSeeds.empty()) { + if (¤t == falseState) swapInfo = true; + seedMap.erase(falseState); + } + if (swapInfo) { + std::swap(trueState->coveredNew, falseState->coveredNew); + std::swap(trueState->coveredLines, falseState->coveredLines); + } + } + + current.ptreeNode->data = 0; + std::pair<PTree::Node*, PTree::Node*> res = + processTree->split(current.ptreeNode, falseState, trueState); + falseState->ptreeNode = res.first; + trueState->ptreeNode = res.second; + + if (!isInternal) { + if (pathWriter) { + falseState->pathOS = pathWriter->open(current.pathOS); + trueState->pathOS << "1"; + falseState->pathOS << "0"; + } + if (symPathWriter) { + falseState->symPathOS = symPathWriter->open(current.symPathOS); + trueState->symPathOS << "1"; + falseState->symPathOS << "0"; + } + } + + addConstraint(*trueState, condition); + addConstraint(*falseState, Expr::createNot(condition)); + + // Kinda gross, do we even really still want this option? + if (MaxDepth && MaxDepth<=trueState->depth) { + terminateStateEarly(*trueState, "max-depth exceeded"); + terminateStateEarly(*falseState, "max-depth exceeded"); + return StatePair(0, 0); + } + + return StatePair(trueState, falseState); + } +} + +void Executor::addConstraint(ExecutionState &state, ref<Expr> condition) { + if (condition.isConstant()) { + assert(condition.getConstantValue() && + "attempt to add invalid constraint"); + return; + } + + // Check to see if this constraint violates seeds. + std::map< ExecutionState*, std::vector<SeedInfo> >::iterator it = + seedMap.find(&state); + if (it != seedMap.end()) { + bool warn = false; + for (std::vector<SeedInfo>::iterator siit = it->second.begin(), + siie = it->second.end(); siit != siie; ++siit) { + bool res; + bool success = + solver->mustBeFalse(state, siit->assignment.evaluate(condition), res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + siit->patchSeed(state, condition, solver); + warn = true; + } + } + if (warn) + klee_warning("seeds patched for violating constraint"); + } + + state.addConstraint(condition); + if (ivcEnabled) + doImpliedValueConcretization(state, condition, ref<Expr>(1, Expr::Bool)); +} + +ref<Expr> Executor::evalConstant(Constant *c) { + if (llvm::ConstantExpr *ce = dyn_cast<llvm::ConstantExpr>(c)) { + return evalConstantExpr(ce); + } else { + if (const ConstantInt *ci = dyn_cast<ConstantInt>(c)) { + switch(ci->getBitWidth()) { + case 1: return ConstantExpr::create(ci->getZExtValue(), Expr::Bool); + case 8: return ConstantExpr::create(ci->getZExtValue(), Expr::Int8); + case 16: return ConstantExpr::create(ci->getZExtValue(), Expr::Int16); + case 32: return ConstantExpr::create(ci->getZExtValue(), Expr::Int32); + case 64: return ConstantExpr::create(ci->getZExtValue(), Expr::Int64); + default: + assert(0 && "XXX arbitrary bit width constants unhandled"); + } + } else if (const ConstantFP *cf = dyn_cast<ConstantFP>(c)) { + switch(cf->getType()->getTypeID()) { + case Type::FloatTyID: { + float f = cf->getValueAPF().convertToFloat(); + return ConstantExpr::create(floats::FloatAsUInt64(f), Expr::Int32); + } + case Type::DoubleTyID: { + double d = cf->getValueAPF().convertToDouble(); + return ConstantExpr::create(floats::DoubleAsUInt64(d), Expr::Int64); + } + case Type::X86_FP80TyID: { + // FIXME: This is really broken, but for now we just convert + // to a double. This isn't going to work at all in general, + // but we need support for wide constants. + APFloat apf = cf->getValueAPF(); + bool ignored; + APFloat::opStatus r = apf.convert(APFloat::IEEEdouble, + APFloat::rmNearestTiesToAway, + &ignored); + (void) r; + //assert(!(r & APFloat::opOverflow) && !(r & APFloat::opUnderflow) && + // "Overflow/underflow while converting from FP80 (x87) to 64-bit double"); + double d = apf.convertToDouble(); + return ConstantExpr::create(floats::DoubleAsUInt64(d), Expr::Int64); + } + default: + llvm::cerr << "Constant of type " << cf->getType()->getDescription() + << " not supported\n"; + llvm::cerr << "Constant used at "; + KConstant *kc = kmodule->getKConstant((Constant*) cf); + if (kc && kc->ki && kc->ki->info) + llvm::cerr << kc->ki->info->file << ":" << kc->ki->info->line << "\n"; + else llvm::cerr << "<unknown>\n"; + + assert(0 && "Arbitrary bit width floating point constants unsupported"); + } + } else if (const GlobalValue *gv = dyn_cast<GlobalValue>(c)) { + return globalAddresses.find(gv)->second; + } else if (isa<ConstantPointerNull>(c)) { + return Expr::createPointer(0); + } else if (isa<UndefValue>(c)) { + return ConstantExpr::create(0, Expr::getWidthForLLVMType(c->getType())); + } else { + // Constant{AggregateZero,Array,Struct,Vector} + assert(0 && "invalid argument to evalConstant()"); + } + } +} + +ref<Expr> Executor::eval(KInstruction *ki, + unsigned index, + ExecutionState &state) { + assert(index < ki->inst->getNumOperands()); + int vnumber = ki->operands[index]; + + // Determine if this is a constant or not. + if (vnumber < 0) { + unsigned index = -vnumber - 2; + Cell &c = kmodule->constantTable[index]; + return c.value; + } else { + unsigned index = vnumber; + StackFrame &sf = state.stack.back(); + Cell &c = sf.locals[index]; + return c.value; + } +} + +void Executor::bindLocal(KInstruction *target, ExecutionState &state, + ref<Expr> value) { + StackFrame &sf = state.stack.back(); + unsigned reg = target->dest; + Cell &c = sf.locals[reg]; + c.value = value; +} + +void Executor::bindArgument(KFunction *kf, unsigned index, + ExecutionState &state, ref<Expr> value) { + StackFrame &sf = state.stack.back(); + unsigned reg = kf->getArgRegister(index); + Cell &c = sf.locals[reg]; + c.value = value; +} + +ref<Expr> Executor::toUnique(const ExecutionState &state, + ref<Expr> &e) { + ref<Expr> result = e; + + if (!e.isConstant()) { + ref<Expr> value(0); + bool isTrue = false; + + solver->setTimeout(stpTimeout); + if (solver->getValue(state, e, value) && + solver->mustBeTrue(state, EqExpr::create(e, value), isTrue) && + isTrue) + result = value; + solver->setTimeout(0); + } + + return result; +} + + +/* Concretize the given expression, and return a possible constant value. + 'reason' is just a documentation string stating the reason for concretization. */ +ref<Expr> Executor::toConstant(ExecutionState &state, + ref<Expr> e, + const char *reason) { + e = state.constraints.simplifyExpr(e); + if (!e.isConstant()) { + ref<Expr> value; + bool success = solver->getValue(state, e, value); + assert(success && "FIXME: Unhandled solver failure"); + + std::ostringstream os; + os << "silently concretizing (reason: " << reason << ") expression " << e + << " to value " << value + << " (" << (*(state.pc)).info->file << ":" << (*(state.pc)).info->line << ")"; + + if (AllExternalWarnings) + klee_warning(reason, os.str().c_str()); + else + klee_warning_once(reason, "%s", os.str().c_str()); + + addConstraint(state, EqExpr::create(e, value)); + + return value; + } else { + return e; + } +} + +void Executor::executeGetValue(ExecutionState &state, + ref<Expr> e, + KInstruction *target) { + e = state.constraints.simplifyExpr(e); + std::map< ExecutionState*, std::vector<SeedInfo> >::iterator it = + seedMap.find(&state); + if (it==seedMap.end() || e.isConstant()) { + ref<Expr> value; + bool success = solver->getValue(state, e, value); + assert(success && "FIXME: Unhandled solver failure"); + bindLocal(target, state, value); + } else { + std::set< ref<Expr> > values; + for (std::vector<SeedInfo>::iterator siit = it->second.begin(), + siie = it->second.end(); siit != siie; ++siit) { + ref<Expr> value; + bool success = + solver->getValue(state, siit->assignment.evaluate(e), value); + assert(success && "FIXME: Unhandled solver failure"); + values.insert(value); + } + + std::vector< ref<Expr> > conditions; + for (std::set< ref<Expr> >::iterator vit = values.begin(), + vie = values.end(); vit != vie; ++vit) + conditions.push_back(EqExpr::create(e, *vit)); + + std::vector<ExecutionState*> branches; + branch(state, conditions, branches); + + std::vector<ExecutionState*>::iterator bit = branches.begin(); + for (std::set< ref<Expr> >::iterator vit = values.begin(), + vie = values.end(); vit != vie; ++vit) { + ExecutionState *es = *bit; + if (es) + bindLocal(target, *es, *vit); + ++bit; + } + } +} + +void Executor::stepInstruction(ExecutionState &state) { + if (DebugPrintInstructions) { + printFileLine(state, state.pc); + llvm::cerr << std::setw(10) << stats::instructions << " " << *state.pc->inst; + } + + if (statsTracker) + statsTracker->stepInstruction(state); + + ++stats::instructions; + state.prevPC = state.pc; + ++state.pc; + + if (stats::instructions==StopAfterNInstructions) + haltExecution = true; +} + +void Executor::executeCall(ExecutionState &state, + KInstruction *ki, + Function *f, + std::vector< ref<Expr> > &arguments) { + if (WriteTraces) { + // don't print out special debug stop point 'function' calls + if (f->getIntrinsicID() != Intrinsic::dbg_stoppoint) { + const std::string& calleeFuncName = f->getName(); + state.exeTraceMgr.addEvent(new FunctionCallTraceEvent(state, ki, calleeFuncName)); + } + } + + Instruction *i = ki->inst; + if (f && f->isDeclaration()) { + if (f!=kmodule->dbgStopPointFn) { // special case speed hack + switch(f->getIntrinsicID()) { + case Intrinsic::dbg_stoppoint: + case Intrinsic::dbg_region_start: + case Intrinsic::dbg_region_end: + case Intrinsic::dbg_func_start: + case Intrinsic::dbg_declare: + case Intrinsic::not_intrinsic: + // state may be destroyed by this call, cannot touch + callExternalFunction(state, ki, f, arguments); + break; + + // vararg is handled by caller and intrinsic lowering, + // see comment for ExecutionState::varargs + case Intrinsic::vastart: { + StackFrame &sf = state.stack.back(); + assert(sf.varargs && + "vastart called in function with no vararg object"); + executeMemoryOperation(state, true, arguments[0], + sf.varargs->getBaseExpr(), 0); + break; + } + case Intrinsic::vaend: // va_end is a noop for the interpreter + break; + + case Intrinsic::vacopy: // should be lowered + default: + klee_error("unknown intrinsic: %s", f->getName().c_str()); + } + } + + if (InvokeInst *ii = dyn_cast<InvokeInst>(i)) { + transferToBasicBlock(ii->getNormalDest(), i->getParent(), state); + } + } else { + // XXX not really happy about this reliance on prevPC but is ok I + // guess. This just done to avoid having to pass KInstIterator + // everywhere instead of the actual instruction, since we can't + // make a KInstIterator from just an instruction (unlike LLVM). + KFunction *kf = kmodule->functionMap[f]; + state.pushFrame(state.prevPC, kf); + state.pc = kf->instructions; + + if (statsTracker) + statsTracker->framePushed(state, &state.stack[state.stack.size()-2]); + + unsigned callingArgs = arguments.size(); + unsigned funcArgs = f->arg_size(); + if (!f->isVarArg()) { + if (callingArgs > funcArgs) { + klee_warning_once(f, "calling %s with extra arguments.", + f->getName().c_str()); + } else if (callingArgs < funcArgs) { + terminateStateOnError(state, "calling function with too few arguments", + "user.err"); + return; + } + } else { + if (callingArgs < funcArgs) { + terminateStateOnError(state, "calling function with too few arguments", + "user.err"); + return; + } + + StackFrame &sf = state.stack.back(); + unsigned size = 0; + for (unsigned i = funcArgs; i < callingArgs; i++) + size += Expr::getMinBytesForWidth(arguments[i].getWidth()); + + MemoryObject *mo = sf.varargs = memory->allocate(size, true, false, + state.prevPC->inst); + if (!mo) { + terminateStateOnExecError(state, "out of memory (varargs)"); + return; + } + ObjectState *os = bindObjectInState(state, mo, true); + unsigned offset = 0; + for (unsigned i = funcArgs; i < callingArgs; i++) { + // XXX: DRE: i think we bind memory objects here? + os->write(offset, arguments[i]); + offset += Expr::getMinBytesForWidth(arguments[i].getWidth()); + } + } + + unsigned numFormals = f->arg_size(); + for (unsigned i=0; i<numFormals; ++i) + bindArgument(kf, i, state, arguments[i]); + } +} + +void Executor::transferToBasicBlock(BasicBlock *dst, BasicBlock *src, + ExecutionState &state) { + // Note that in general phi nodes can reuse phi values from the same + // block but the incoming value is the eval() result *before* the + // execution of any phi nodes. this is pathological and doesn't + // really seem to occur, but just in case we run the PhiCleanerPass + // which makes sure this cannot happen and so it is safe to just + // eval things in order. The PhiCleanerPass also makes sure that all + // incoming blocks have the same order for each PHINode so we only + // have to compute the index once. + // + // With that done we simply set an index in the state so that PHI + // instructions know which argument to eval, set the pc, and continue. + + // XXX this lookup has to go ? + KFunction *kf = state.stack.back().kf; + unsigned entry = kf->basicBlockEntry[dst]; + state.pc = &kf->instructions[entry]; + if (state.pc->inst->getOpcode() == Instruction::PHI) { + PHINode *first = static_cast<PHINode*>(state.pc->inst); + state.incomingBBIndex = first->getBasicBlockIndex(src); + } +} + +void Executor::printFileLine(ExecutionState &state, KInstruction *ki) { + const InstructionInfo &ii = *ki->info; + if (ii.file != "") + llvm::cerr << " " << ii.file << ":" << ii.line << ":"; + else + llvm::cerr << " [no debug info]:"; +} + + +Function* Executor::getCalledFunction(CallSite &cs, ExecutionState &state) { + Function *f = cs.getCalledFunction(); + + if (f) { + std::string alias = state.getFnAlias(f->getName()); + if (alias != "") { + //llvm::cerr << f->getName() << "() is aliased with " << alias << "()\n"; + llvm::Module* currModule = kmodule->module; + Function* old_f = f; + f = currModule->getFunction(alias); + if (!f) { + llvm::cerr << "Function " << alias << "(), alias for " << old_f->getName() << " not found!\n"; + assert(f && "function alias not found"); + } + } + } + + return f; +} + + +void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { + Instruction *i = ki->inst; + switch (i->getOpcode()) { + // Control flow + case Instruction::Ret: { + ReturnInst *ri = cast<ReturnInst>(i); + KInstIterator kcaller = state.stack.back().caller; + Instruction *caller = kcaller ? kcaller->inst : 0; + bool isVoidReturn = (ri->getNumOperands() == 0); + ref<Expr> result(0,Expr::Bool); + + if (WriteTraces) { + state.exeTraceMgr.addEvent(new FunctionReturnTraceEvent(state, ki)); + } + + if (!isVoidReturn) { + result = eval(ki, 0, state); + } + + if (state.stack.size() <= 1) { + assert(!caller && "caller set on initial stack frame"); + terminateStateOnExit(state); + } else { + state.popFrame(); + + if (statsTracker) + statsTracker->framePopped(state); + + if (InvokeInst *ii = dyn_cast<InvokeInst>(caller)) { + transferToBasicBlock(ii->getNormalDest(), caller->getParent(), state); + } else { + state.pc = kcaller; + ++state.pc; + } + + if (!isVoidReturn) { + const Type *t = caller->getType(); + if (t != Type::VoidTy) { + // may need to do coercion due to bitcasts + Expr::Width from = result.getWidth(); + Expr::Width to = Expr::getWidthForLLVMType(t); + + if (from != to) { + CallSite cs = (isa<InvokeInst>(caller) ? CallSite(cast<InvokeInst>(caller)) : + CallSite(cast<CallInst>(caller))); + + // XXX need to check other param attrs ? + if (cs.paramHasAttr(0, llvm::Attribute::SExt)) { + result = SExtExpr::create(result, to); + } else { + result = ZExtExpr::create(result, to); + } + } + + bindLocal(kcaller, state, result); + } + } else { + // We check that the return value has no users instead of + // checking the type, since C defaults to returning int for + // undeclared functions. + if (!caller->use_empty()) { + terminateStateOnExecError(state, "return void when caller expected a result"); + } + } + } + break; + } + case Instruction::Unwind: { + for (;;) { + KInstruction *kcaller = state.stack.back().caller; + state.popFrame(); + + if (statsTracker) + statsTracker->framePopped(state); + + if (state.stack.empty()) { + terminateStateOnExecError(state, "unwind from initial stack frame"); + break; + } else { + Instruction *caller = kcaller->inst; + if (InvokeInst *ii = dyn_cast<InvokeInst>(caller)) { + transferToBasicBlock(ii->getUnwindDest(), caller->getParent(), state); + break; + } + } + } + break; + } + case Instruction::Br: { + BranchInst *bi = cast<BranchInst>(i); + if (bi->isUnconditional()) { + transferToBasicBlock(bi->getSuccessor(0), bi->getParent(), state); + } else { + // FIXME: Find a way that we don't have this hidden dependency. + assert(bi->getCondition() == bi->getOperand(0) && + "Wrong operand index!"); + ref<Expr> cond = eval(ki, 0, state); + Executor::StatePair branches = fork(state, cond, false); + + if (WriteTraces) { + bool isTwoWay = (branches.first && branches.second); + + if (branches.first) { + branches.first->exeTraceMgr.addEvent( + new BranchTraceEvent(state, ki, true, isTwoWay)); + } + + if (branches.second) { + branches.second->exeTraceMgr.addEvent( + new BranchTraceEvent(state, ki, false, isTwoWay)); + } + } + + // NOTE: There is a hidden dependency here, markBranchVisited + // requires that we still be in the context of the branch + // instruction (it reuses its statistic id). Should be cleaned + // up with convenient instruction specific data. + if (statsTracker && state.stack.back().kf->trackCoverage) + statsTracker->markBranchVisited(branches.first, branches.second); + + if (branches.first) + transferToBasicBlock(bi->getSuccessor(0), bi->getParent(), *branches.first); + if (branches.second) + transferToBasicBlock(bi->getSuccessor(1), bi->getParent(), *branches.second); + } + break; + } + case Instruction::Switch: { + SwitchInst *si = cast<SwitchInst>(i); + ref<Expr> cond = eval(ki, 0, state); + unsigned cases = si->getNumCases(); + BasicBlock *bb = si->getParent(); + + cond = toUnique(state, cond); + if (cond.isConstant()) { + // Somewhat gross to create these all the time, but fine till we + // switch to an internal rep. + ConstantInt *ci = ConstantInt::get(si->getCondition()->getType(), + cond.getConstantValue()); + unsigned index = si->findCaseValue(ci); + transferToBasicBlock(si->getSuccessor(index), si->getParent(), state); + } else { + std::map<BasicBlock*, ref<Expr> > targets; + ref<Expr> isDefault(1,Expr::Bool); + for (unsigned i=1; i<cases; ++i) { + ref<Expr> value = evalConstant(si->getCaseValue(i)); + ref<Expr> match = EqExpr::create(cond, value); + isDefault = AndExpr::create(isDefault, Expr::createNot(match)); + bool result; + bool success = solver->mayBeTrue(state, match, result); + assert(success && "FIXME: Unhandled solver failure"); + if (result) { + std::map<BasicBlock*, ref<Expr> >::iterator it = + targets.insert(std::make_pair(si->getSuccessor(i), + ref<Expr>(0,Expr::Bool))).first; + it->second = OrExpr::create(match, it->second); + } + } + bool res; + bool success = solver->mayBeTrue(state, isDefault, res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) + targets.insert(std::make_pair(si->getSuccessor(0), isDefault)); + + std::vector< ref<Expr> > conditions; + for (std::map<BasicBlock*, ref<Expr> >::iterator it = + targets.begin(), ie = targets.end(); + it != ie; ++it) + conditions.push_back(it->second); + + std::vector<ExecutionState*> branches; + branch(state, conditions, branches); + + std::vector<ExecutionState*>::iterator bit = branches.begin(); + for (std::map<BasicBlock*, ref<Expr> >::iterator it = + targets.begin(), ie = targets.end(); + it != ie; ++it) { + ExecutionState *es = *bit; + if (es) + transferToBasicBlock(it->first, bb, *es); + ++bit; + } + } + break; + } + case Instruction::Unreachable: + // Note that this is not necessarily an internal bug, llvm will + // generate unreachable instructions in cases where it knows the + // program will crash. So it is effectively a SEGV or internal + // error. + terminateStateOnExecError(state, "reached \"unreachable\" instruction"); + break; + + case Instruction::Invoke: + case Instruction::Call: { + CallSite cs; + unsigned argStart; + if (i->getOpcode()==Instruction::Call) { + cs = CallSite(cast<CallInst>(i)); + argStart = 1; + } else { + cs = CallSite(cast<InvokeInst>(i)); + argStart = 3; + } + + unsigned numArgs = cs.arg_size(); + Function *f = getCalledFunction(cs, state); + + // evaluate arguments + std::vector< ref<Expr> > arguments; + arguments.reserve(numArgs); + + for (unsigned j=0; j<numArgs; ++j) + arguments.push_back(eval(ki, argStart+j, state)); + + if (!f) { + // special case the call with a bitcast case + Value *fp = cs.getCalledValue(); + llvm::ConstantExpr *ce = dyn_cast<llvm::ConstantExpr>(fp); + + if (ce && ce->getOpcode()==Instruction::BitCast) { + f = dyn_cast<Function>(ce->getOperand(0)); + assert(f && "XXX unrecognized constant expression in call"); + const FunctionType *fType = + dyn_cast<FunctionType>(cast<PointerType>(f->getType())->getElementType()); + const FunctionType *ceType = + dyn_cast<FunctionType>(cast<PointerType>(ce->getType())->getElementType()); + assert(fType && ceType && "unable to get function type"); + + // XXX check result coercion + + // XXX this really needs thought and validation + unsigned i=0; + for (std::vector< ref<Expr> >::iterator + ai = arguments.begin(), ie = arguments.end(); + ai != ie; ++ai) { + Expr::Width to, from = (*ai).getWidth(); + + if (i<fType->getNumParams()) { + to = Expr::getWidthForLLVMType(fType->getParamType(i)); + + if (from != to) { + // XXX need to check other param attrs ? + if (cs.paramHasAttr(i+1, llvm::Attribute::SExt)) { + arguments[i] = SExtExpr::create(arguments[i], to); + } else { + arguments[i] = ZExtExpr::create(arguments[i], to); + } + } + } + + i++; + } + } else if (isa<InlineAsm>(fp)) { + terminateStateOnExecError(state, "inline assembly is unsupported"); + break; + } + } + + if (f) { + executeCall(state, ki, f, arguments); + } else { + ref<Expr> v = eval(ki, 0, state); + + ExecutionState *free = &state; + bool hasInvalid = false, first = true; + + /* XXX This is wasteful, no need to do a full evaluate since we + have already got a value. But in the end the caches should + handle it for us, albeit with some overhead. */ + do { + ref<Expr> value; + bool success = solver->getValue(*free, v, value); + assert(success && "FIXME: Unhandled solver failure"); + StatePair res = fork(*free, EqExpr::create(v, value), true); + if (res.first) { + void *addr = (void*) (unsigned long) value.getConstantValue(); + std::set<void*>::iterator it = legalFunctions.find(addr); + if (it != legalFunctions.end()) { + f = (Function*) addr; + + // Don't give warning on unique resolution + if (res.second || !first) + klee_warning_once(addr, + "resolved symbolic function pointer to: %s", + f->getName().c_str()); + + executeCall(*res.first, ki, f, arguments); + } else { + if (!hasInvalid) { + terminateStateOnExecError(state, "invalid function pointer"); + hasInvalid = true; + } + } + } + + first = false; + free = res.second; + } while (free); + } + break; + } + case Instruction::PHI: { + ref<Expr> result = eval(ki, state.incomingBBIndex * 2, state); + bindLocal(ki, state, result); + break; + } + + // Special instructions + case Instruction::Select: { + SelectInst *SI = cast<SelectInst>(ki->inst); + assert(SI->getCondition() == SI->getOperand(0) && + "Wrong operand index!"); + ref<Expr> cond = eval(ki, 0, state); + ref<Expr> tExpr = eval(ki, 1, state); + ref<Expr> fExpr = eval(ki, 2, state); + ref<Expr> result = SelectExpr::create(cond, tExpr, fExpr); + bindLocal(ki, state, result); + break; + } + + case Instruction::VAArg: + terminateStateOnExecError(state, "unexpected VAArg instruction"); + break; + + // Arithmetic / logical +#define FP_CONSTANT_BINOP(op, type, l, r, target, state) \ + bindLocal(target, state, \ + ref<Expr>(op(toConstant(state, l, "floating point").getConstantValue(), \ + toConstant(state, r, "floating point").getConstantValue(), \ + type), type)) + case Instruction::Add: { + BinaryOperator *bi = cast<BinaryOperator>(i); + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + + if( bi->getType()->getTypeID() == llvm::Type::IntegerTyID ) { + bindLocal(ki, state, AddExpr::create(left, right)); + } else { + Expr::Width type = Expr::getWidthForLLVMType(bi->getType()); + FP_CONSTANT_BINOP(floats::add, type, left, right, ki, state); + } + + break; + } + + case Instruction::Sub: { + BinaryOperator *bi = cast<BinaryOperator>(i); + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + + if( bi->getType()->getTypeID() == llvm::Type::IntegerTyID ) { + bindLocal(ki, state, SubExpr::create(left, right)); + } else { + Expr::Width type = Expr::getWidthForLLVMType(bi->getType()); + FP_CONSTANT_BINOP(floats::sub, type, left, right, ki, state); + } + + break; + } + + case Instruction::Mul: { + BinaryOperator *bi = cast<BinaryOperator>(i); + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + + if( bi->getType()->getTypeID() == llvm::Type::IntegerTyID ) { + bindLocal(ki, state, MulExpr::create(left, right)); + } else { + Expr::Width type = Expr::getWidthForLLVMType(bi->getType()); + FP_CONSTANT_BINOP(floats::mul, type, left, right, ki, state); + } + + break; + } + + case Instruction::UDiv: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = UDivExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::SDiv: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = SDivExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::URem: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = URemExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::SRem: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = SRemExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::And: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = AndExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::Or: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = OrExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::Xor: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = XorExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::Shl: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = ShlExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::LShr: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = LShrExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case Instruction::AShr: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = AShrExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + // Compare + + case Instruction::ICmp: { + CmpInst *ci = cast<CmpInst>(i); + ICmpInst *ii = cast<ICmpInst>(ci); + + switch(ii->getPredicate()) { + case ICmpInst::ICMP_EQ: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = EqExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_NE: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = NeExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_UGT: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = UgtExpr::create(left, right); + bindLocal(ki, state,result); + break; + } + + case ICmpInst::ICMP_UGE: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = UgeExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_ULT: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = UltExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_ULE: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = UleExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_SGT: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = SgtExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_SGE: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = SgeExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_SLT: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = SltExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + case ICmpInst::ICMP_SLE: { + ref<Expr> left = eval(ki, 0, state); + ref<Expr> right = eval(ki, 1, state); + ref<Expr> result = SleExpr::create(left, right); + bindLocal(ki, state, result); + break; + } + + default: + terminateStateOnExecError(state, "invalid ICmp predicate"); + } + break; + } + + // Memory instructions... + case Instruction::Alloca: + case Instruction::Malloc: { + AllocationInst *ai = cast<AllocationInst>(i); + unsigned elementSize = + kmodule->targetData->getTypeStoreSize(ai->getAllocatedType()); + ref<Expr> size = Expr::createPointer(elementSize); + if (ai->isArrayAllocation()) { + // XXX coerce? + ref<Expr> count = eval(ki, 0, state); + size = MulExpr::create(count, size); + } + bool isLocal = i->getOpcode()==Instruction::Alloca; + executeAlloc(state, size, isLocal, ki); + break; + } + case Instruction::Free: { + executeFree(state, eval(ki, 0, state)); + break; + } + + case Instruction::Load: { + ref<Expr> base = eval(ki, 0, state); + executeMemoryOperation(state, false, base, 0, ki); + break; + } + case Instruction::Store: { + ref<Expr> base = eval(ki, 1, state); + ref<Expr> value = eval(ki, 0, state); + executeMemoryOperation(state, true, base, value, 0); + break; + } + + case Instruction::GetElementPtr: { + KGEPInstruction *kgepi = static_cast<KGEPInstruction*>(ki); + ref<Expr> base = eval(ki, 0, state); + + for (std::vector< std::pair<unsigned, unsigned> >::iterator + it = kgepi->indices.begin(), ie = kgepi->indices.end(); + it != ie; ++it) { + unsigned elementSize = it->second; + ref<Expr> index = eval(ki, it->first, state); + base = AddExpr::create(base, + MulExpr::create(Expr::createCoerceToPointerType(index), + Expr::createPointer(elementSize))); + } + if (kgepi->offset) + base = AddExpr::create(base, + Expr::createPointer(kgepi->offset)); + bindLocal(ki, state, base); + break; + } + + // Conversion + case Instruction::Trunc: { + CastInst *ci = cast<CastInst>(i); + ref<Expr> result = ExtractExpr::createByteOff(eval(ki, 0, state), + 0, + Expr::getWidthForLLVMType(ci->getType())); + bindLocal(ki, state, result); + break; + } + case Instruction::ZExt: { + CastInst *ci = cast<CastInst>(i); + ref<Expr> result = ZExtExpr::create(eval(ki, 0, state), + Expr::getWidthForLLVMType(ci->getType())); + bindLocal(ki, state, result); + break; + } + case Instruction::SExt: { + CastInst *ci = cast<CastInst>(i); + ref<Expr> result = SExtExpr::create(eval(ki, 0, state), + Expr::getWidthForLLVMType(ci->getType())); + bindLocal(ki, state, result); + break; + } + + case Instruction::IntToPtr: { + CastInst *ci = cast<CastInst>(i); + Expr::Width pType = Expr::getWidthForLLVMType(ci->getType()); + ref<Expr> arg = eval(ki, 0, state); + bindLocal(ki, state, ZExtExpr::create(arg, pType)); + break; + } + case Instruction::PtrToInt: { + CastInst *ci = cast<CastInst>(i); + Expr::Width iType = Expr::getWidthForLLVMType(ci->getType()); + ref<Expr> arg = eval(ki, 0, state); + bindLocal(ki, state, ZExtExpr::create(arg, iType)); + break; + } + + case Instruction::BitCast: { + ref<Expr> result = eval(ki, 0, state); + bindLocal(ki, state, result); + break; + } + + // Floating Point specific instructions + case Instruction::FPTrunc: { + FPTruncInst *fi = cast<FPTruncInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> arg = toConstant(state, eval(ki, 0, state), + "floating point"); + uint64_t value = floats::trunc(arg.getConstantValue(), + resultType, + arg.getWidth()); + ref<Expr> result(value, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::FPExt: { + FPExtInst *fi = cast<FPExtInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> arg = toConstant(state, eval(ki, 0, state), + "floating point"); + uint64_t value = floats::ext(arg.getConstantValue(), + resultType, + arg.getWidth()); + ref<Expr> result(value, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::FPToUI: { + FPToUIInst *fi = cast<FPToUIInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> arg = toConstant(state, eval(ki, 0, state), + "floating point"); + uint64_t value = floats::toUnsignedInt(arg.getConstantValue(), + resultType, + arg.getWidth()); + ref<Expr> result(value, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::FPToSI: { + FPToSIInst *fi = cast<FPToSIInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> arg = toConstant(state, eval(ki, 0, state), + "floating point"); + uint64_t value = floats::toSignedInt(arg.getConstantValue(), + resultType, + arg.getWidth()); + ref<Expr> result(value, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::UIToFP: { + UIToFPInst *fi = cast<UIToFPInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> arg = toConstant(state, eval(ki, 0, state), + "floating point"); + uint64_t value = floats::UnsignedIntToFP(arg.getConstantValue(), + resultType); + ref<Expr> result(value, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::SIToFP: { + SIToFPInst *fi = cast<SIToFPInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> arg = toConstant(state, eval(ki, 0, state), + "floating point"); + uint64_t value = floats::SignedIntToFP(arg.getConstantValue(), + resultType, + arg.getWidth()); + ref<Expr> result(value, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::FCmp: { + FCmpInst *fi = cast<FCmpInst>(i); + Expr::Width resultType = Expr::getWidthForLLVMType(fi->getType()); + ref<Expr> left = toConstant(state, eval(ki, 0, state), + "floating point"); + ref<Expr> right = toConstant(state, eval(ki, 1, state), + "floating point"); + uint64_t leftVal = left.getConstantValue(); + uint64_t rightVal = right.getConstantValue(); + + //determine whether the operands are NANs + unsigned inWidth = left.getWidth(); + bool leftIsNaN = floats::isNaN( leftVal, inWidth ); + bool rightIsNaN = floats::isNaN( rightVal, inWidth ); + + //handle NAN based on whether the predicate is "ordered" or "unordered" + uint64_t ret = (uint64_t)-1; + bool done = false; + switch( fi->getPredicate() ) { + //predicates which only care about whether or not the operands are NaNs + case FCmpInst::FCMP_ORD: + done = true; + ret = !leftIsNaN && !rightIsNaN; + break; + + case FCmpInst::FCMP_UNO: + done = true; + ret = leftIsNaN || rightIsNaN; + break; + + //ordered comparisons return false if either operand is NaN + case FCmpInst::FCMP_OEQ: + case FCmpInst::FCMP_OGT: + case FCmpInst::FCMP_OGE: + case FCmpInst::FCMP_OLT: + case FCmpInst::FCMP_OLE: + case FCmpInst::FCMP_ONE: + if( !leftIsNaN && !rightIsNaN) //only fall through and return false if there are NaN(s) + break; + + case FCmpInst::FCMP_FALSE: { //always return false for this predicate + done = true; + ret = false; + break; + } + + //unordered comparisons return true if either operand is NaN + case FCmpInst::FCMP_UEQ: + case FCmpInst::FCMP_UGT: + case FCmpInst::FCMP_UGE: + case FCmpInst::FCMP_ULT: + case FCmpInst::FCMP_ULE: + case FCmpInst::FCMP_UNE: + if( !leftIsNaN && !rightIsNaN) //only fall through and return true if there are NaN(s) + break; + + case FCmpInst::FCMP_TRUE: //always return true for this predicate + done = true; + ret = true; + + default: + case FCmpInst::BAD_FCMP_PREDICATE: /* will fall through and trigger fatal in the next switch */ + break; + } + + //if not done, then we need to actually do a comparison to get the result + if( !done ) { + switch( fi->getPredicate() ) { + //ordered comparisons return false if either operand is NaN + case FCmpInst::FCMP_OEQ: + case FCmpInst::FCMP_UEQ: + ret = floats::eq( leftVal, rightVal, inWidth ); + break; + + case FCmpInst::FCMP_OGT: + case FCmpInst::FCMP_UGT: + ret = floats::gt( leftVal, rightVal, inWidth ); + break; + + case FCmpInst::FCMP_OGE: + case FCmpInst::FCMP_UGE: + ret = floats::ge( leftVal, rightVal, inWidth ); + break; + + case FCmpInst::FCMP_OLT: + case FCmpInst::FCMP_ULT: + ret = floats::lt( leftVal, rightVal, inWidth ); + break; + + case FCmpInst::FCMP_OLE: + case FCmpInst::FCMP_ULE: + ret = floats::le( leftVal, rightVal, inWidth ); + break; + + case FCmpInst::FCMP_ONE: + case FCmpInst::FCMP_UNE: + ret = floats::ne( leftVal, rightVal, inWidth ); + break; + + default: + terminateStateOnExecError(state, "invalid FCmp predicate"); + } + } + + ref<Expr> result(ret, resultType); + bindLocal(ki, state, result); + break; + } + + case Instruction::FDiv: { + BinaryOperator *bi = cast<BinaryOperator>(i); + + ref<Expr> dividend = eval(ki, 0, state); + ref<Expr> divisor = eval(ki, 1, state); + Expr::Width type = Expr::getWidthForLLVMType(bi->getType()); + FP_CONSTANT_BINOP(floats::div, type, dividend, divisor, ki, state); + break; + } + + case Instruction::FRem: { + BinaryOperator *bi = cast<BinaryOperator>(i); + + ref<Expr> dividend = eval(ki, 0, state); + ref<Expr> divisor = eval(ki, 1, state); + Expr::Width type = Expr::getWidthForLLVMType(bi->getType()); + FP_CONSTANT_BINOP(floats::mod, type, dividend, divisor, ki, state); + break; + } + + + // Other instructions... + // Unhandled + case Instruction::ExtractElement: + case Instruction::InsertElement: + case Instruction::ShuffleVector: + terminateStateOnError(state, "XXX vector instructions unhandled", + "xxx.err"); + break; + + default: + terminateStateOnExecError(state, "invalid instruction"); + break; + } +} + +void Executor::updateStates(ExecutionState *current) { + if (searcher) { + searcher->update(current, addedStates, removedStates); + } + + states.insert(addedStates.begin(), addedStates.end()); + addedStates.clear(); + + for (std::set<ExecutionState*>::iterator + it = removedStates.begin(), ie = removedStates.end(); + it != ie; ++it) { + ExecutionState *es = *it; + std::set<ExecutionState*>::iterator it2 = states.find(es); + assert(it2!=states.end()); + states.erase(it2); + std::map<ExecutionState*, std::vector<SeedInfo> >::iterator it3 = + seedMap.find(es); + if (it3 != seedMap.end()) + seedMap.erase(it3); + processTree->remove(es->ptreeNode); + delete es; + } + removedStates.clear(); +} + +void Executor::bindInstructionConstants(KInstruction *KI) { + GetElementPtrInst *gepi = dyn_cast<GetElementPtrInst>(KI->inst); + if (!gepi) + return; + + KGEPInstruction *kgepi = static_cast<KGEPInstruction*>(KI); + ref<Expr> constantOffset = Expr::createPointer(0); + unsigned index = 1; + for (gep_type_iterator ii = gep_type_begin(gepi), ie = gep_type_end(gepi); + ii != ie; ++ii) { + if (const StructType *st = dyn_cast<StructType>(*ii)) { + const StructLayout *sl = + kmodule->targetData->getStructLayout(st); + const ConstantInt *ci = cast<ConstantInt>(ii.getOperand()); + ref<Expr> addend = Expr::createPointer(sl->getElementOffset((unsigned) + ci->getZExtValue())); + constantOffset = AddExpr::create(constantOffset, addend); + } else { + const SequentialType *st = cast<SequentialType>(*ii); + unsigned elementSize = + kmodule->targetData->getTypeStoreSize(st->getElementType()); + Value *operand = ii.getOperand(); + if (Constant *c = dyn_cast<Constant>(operand)) { + ref<Expr> index = evalConstant(c); + ref<Expr> addend = MulExpr::create(Expr::createCoerceToPointerType(index), + Expr::createPointer(elementSize)); + constantOffset = AddExpr::create(constantOffset, addend); + } else { + kgepi->indices.push_back(std::make_pair(index, elementSize)); + } + } + index++; + } + assert(constantOffset.isConstant()); + kgepi->offset = constantOffset.getConstantValue(); +} + +void Executor::bindModuleConstants() { + for (std::vector<KFunction*>::iterator it = kmodule->functions.begin(), + ie = kmodule->functions.end(); it != ie; ++it) { + KFunction *kf = *it; + for (unsigned i=0; i<kf->numInstructions; ++i) + bindInstructionConstants(kf->instructions[i]); + } + + kmodule->constantTable = new Cell[kmodule->constants.size()]; + for (unsigned i=0; i<kmodule->constants.size(); ++i) { + Cell &c = kmodule->constantTable[i]; + c.value = evalConstant(kmodule->constants[i]); + } +} + +void Executor::run(ExecutionState &initialState) { + bindModuleConstants(); + + // Delay init till now so that ticks don't accrue during + // optimization and such. + initTimers(); + + states.insert(&initialState); + + if (usingSeeds) { + std::vector<SeedInfo> &v = seedMap[&initialState]; + + for (std::vector<BOut*>::const_iterator it = usingSeeds->begin(), + ie = usingSeeds->end(); it != ie; ++it) + v.push_back(SeedInfo(*it)); + + int lastNumSeeds = usingSeeds->size()+10; + double lastTime, startTime = lastTime = util::getWallTime(); + ExecutionState *lastState = 0; + while (!seedMap.empty()) { + if (haltExecution) goto dump; + + std::map<ExecutionState*, std::vector<SeedInfo> >::iterator it = + seedMap.upper_bound(lastState); + if (it == seedMap.end()) + it = seedMap.begin(); + lastState = it->first; + unsigned numSeeds = it->second.size(); + ExecutionState &state = *lastState; + KInstruction *ki = state.pc; + stepInstruction(state); + + executeInstruction(state, ki); + processTimers(&state, MaxInstructionTime * numSeeds); + updateStates(&state); + + if ((stats::instructions % 1000) == 0) { + int numSeeds = 0, numStates = 0; + for (std::map<ExecutionState*, std::vector<SeedInfo> >::iterator + it = seedMap.begin(), ie = seedMap.end(); + it != ie; ++it) { + numSeeds += it->second.size(); + numStates++; + } + double time = util::getWallTime(); + if (SeedTime>0. && time > startTime + SeedTime) { + klee_warning("seed time expired, %d seeds remain over %d states", + numSeeds, numStates); + break; + } else if (numSeeds<=lastNumSeeds-10 || + time >= lastTime+10) { + lastTime = time; + lastNumSeeds = numSeeds; + klee_message("%d seeds remaining over: %d states", + numSeeds, numStates); + } + } + } + + klee_message("seeding done (%d states remain)", (int) states.size()); + + // XXX total hack, just because I like non uniform better but want + // seed results to be equally weighted. + for (std::set<ExecutionState*>::iterator + it = states.begin(), ie = states.end(); + it != ie; ++it) { + (*it)->weight = 1.; + } + + if (OnlySeed) + goto dump; + } + + searcher = constructUserSearcher(*this); + + searcher->update(0, states, std::set<ExecutionState*>()); + + while (!states.empty() && !haltExecution) { + ExecutionState &state = searcher->selectState(); + KInstruction *ki = state.pc; + stepInstruction(state); + + executeInstruction(state, ki); + processTimers(&state, MaxInstructionTime); + + if (MaxMemory) { + if ((stats::instructions & 0xFFFF) == 0) { + // We need to avoid calling GetMallocUsage() often because it + // is O(elts on freelist). This is really bad since we start + // to pummel the freelist once we hit the memory cap. + unsigned mbs = sys::Process::GetTotalMemoryUsage() >> 20; + + if (mbs > MaxMemory) { + if (mbs > MaxMemory + 100) { + // just guess at how many to kill + unsigned numStates = states.size(); + unsigned toKill = std::max(1U, numStates - numStates*MaxMemory/mbs); + + if (MaxMemoryInhibit) + klee_warning("killing %d states (over memory cap)", + toKill); + + std::vector<ExecutionState*> arr(states.begin(), states.end()); + for (unsigned i=0,N=arr.size(); N && i<toKill; ++i,--N) { + unsigned idx = rand() % N; + + // Make two pulls to try and not hit a state that + // covered new code. + if (arr[idx]->coveredNew) + idx = rand() % N; + + std::swap(arr[idx], arr[N-1]); + terminateStateEarly(*arr[N-1], "memory limit"); + } + } + atMemoryLimit = true; + } else { + atMemoryLimit = false; + } + } + } + + updateStates(&state); + } + + delete searcher; + searcher = 0; + + dump: + if (DumpStatesOnHalt && !states.empty()) { + llvm::cerr << "KLEE: halting execution, dumping remaining states\n"; + for (std::set<ExecutionState*>::iterator + it = states.begin(), ie = states.end(); + it != ie; ++it) { + ExecutionState &state = **it; + stepInstruction(state); // keep stats rolling + terminateStateEarly(state, "execution halting"); + } + updateStates(0); + } +} + +std::string Executor::getAddressInfo(ExecutionState &state, + ref<Expr> address) const{ + std::ostringstream info; + info << "\taddress: " << address << "\n"; + uint64_t example; + if (address.isConstant()) { + example = address.getConstantValue(); + } else { + ref<Expr> value; + bool success = solver->getValue(state, address, value); + assert(success && "FIXME: Unhandled solver failure"); + example = value.getConstantValue(); + info << "\texample: " << example << "\n"; + std::pair< ref<Expr>, ref<Expr> > res = solver->getRange(state, address); + info << "\trange: [" << res.first << ", " << res.second <<"]\n"; + } + + MemoryObject hack((unsigned) example); + MemoryMap::iterator lower = state.addressSpace.objects.upper_bound(&hack); + info << "\tnext: "; + if (lower==state.addressSpace.objects.end()) { + info << "none\n"; + } else { + const MemoryObject *mo = lower->first; + info << "object at " << mo->address + << " of size " << mo->size << "\n"; + } + if (lower!=state.addressSpace.objects.begin()) { + --lower; + info << "\tprev: "; + if (lower==state.addressSpace.objects.end()) { + info << "none\n"; + } else { + const MemoryObject *mo = lower->first; + info << "object at " << mo->address + << " of size " << mo->size << "\n"; + } + } + + return info.str(); +} + +void Executor::terminateState(ExecutionState &state) { + if (replayOut && replayPosition!=replayOut->numObjects) { + klee_warning_once(replayOut, "replay did not consume all objects in .bout input."); + } + + interpreterHandler->incPathsExplored(); + + std::set<ExecutionState*>::iterator it = addedStates.find(&state); + if (it==addedStates.end()) { + state.pc = state.prevPC; + + removedStates.insert(&state); + } else { + // never reached searcher, just delete immediately + std::map< ExecutionState*, std::vector<SeedInfo> >::iterator it3 = + seedMap.find(&state); + if (it3 != seedMap.end()) + seedMap.erase(it3); + addedStates.erase(it); + processTree->remove(state.ptreeNode); + delete &state; + } +} + +void Executor::terminateStateEarly(ExecutionState &state, std::string message) { + if (!OnlyOutputStatesCoveringNew || state.coveredNew || + (AlwaysOutputSeeds && seedMap.count(&state))) + interpreterHandler->processTestCase(state, (message + "\n").c_str(), "early"); + terminateState(state); +} + +void Executor::terminateStateOnExit(ExecutionState &state) { + if (!OnlyOutputStatesCoveringNew || state.coveredNew || + (AlwaysOutputSeeds && seedMap.count(&state))) + interpreterHandler->processTestCase(state, 0, 0); + terminateState(state); +} + +void Executor::terminateStateOnError(ExecutionState &state, + const std::string &message, + const std::string &suffix, + const std::string &info) { + static std::set< std::pair<Instruction*, std::string> > emittedErrors; + const InstructionInfo &ii = *state.prevPC->info; + + if (EmitAllErrors || + emittedErrors.insert(std::make_pair(state.prevPC->inst,message)).second) { + if (ii.file != "") { + klee_message("ERROR: %s:%d: %s", ii.file.c_str(), ii.line, message.c_str()); + } else { + klee_message("ERROR: %s", message.c_str()); + } + if (!EmitAllErrors) + klee_message("NOTE: now ignoring this error at this location"); + + std::ostringstream msg; + msg << "Error: " << message << "\n"; + if (ii.file != "") { + msg << "File: " << ii.file << "\n"; + msg << "Line: " << ii.line << "\n"; + } + msg << "Stack: \n"; + unsigned idx = 0; + const KInstruction *target = state.prevPC; + for (ExecutionState::stack_ty::reverse_iterator + it = state.stack.rbegin(), ie = state.stack.rend(); + it != ie; ++it) { + StackFrame &sf = *it; + Function *f = sf.kf->function; + const InstructionInfo &ii = *target->info; + msg << "\t#" << idx++ + << " " << std::setw(8) << std::setfill('0') << ii.assemblyLine + << " in " << f->getName() << " ("; + // Yawn, we could go up and print varargs if we wanted to. + unsigned index = 0; + for (Function::arg_iterator ai = f->arg_begin(), ae = f->arg_end(); + ai != ae; ++ai) { + if (ai!=f->arg_begin()) msg << ", "; + + msg << ai->getName(); + // XXX should go through function + ref<Expr> value = sf.locals[sf.kf->getArgRegister(index++)].value; + if (value.isConstant()) + msg << "=" << value; + } + msg << ")"; + if (ii.file != "") + msg << " at " << ii.file << ":" << ii.line; + msg << "\n"; + target = sf.caller; + } + + if (info != "") + msg << "Info: \n" << info; + interpreterHandler->processTestCase(state, msg.str().c_str(), suffix.c_str()); + } + + terminateState(state); +} + +// XXX shoot me +static const char *okExternalsList[] = { "printf", + "fprintf", + "puts", + "getpid" }; +static std::set<std::string> okExternals(okExternalsList, + okExternalsList + + (sizeof(okExternalsList)/sizeof(okExternalsList[0]))); + +void Executor::callExternalFunction(ExecutionState &state, + KInstruction *target, + Function *function, + std::vector< ref<Expr> > &arguments) { + // check if specialFunctionHandler wants it + if (specialFunctionHandler->handle(state, function, target, arguments)) + return; + + if (NoExternals && !okExternals.count(function->getName())) { + llvm::cerr << "KLEE:ERROR: Calling not-OK external function : " << function->getName() << "\n"; + terminateStateOnError(state, "externals disallowed", "user.err"); + return; + } + + // normal external function handling path + uint64_t *args = (uint64_t*) alloca(sizeof(*args) * (arguments.size() + 1)); + memset(args, 0, sizeof(*args) * (arguments.size() + 1)); + + unsigned i = 1; + for (std::vector<ref<Expr> >::iterator ai = arguments.begin(), ae = arguments.end(); + ai!=ae; ++ai, ++i) { + if (AllowExternalSymCalls) { // don't bother checking uniqueness + ref<Expr> ce; + bool success = solver->getValue(state, *ai, ce); + assert(success && "FIXME: Unhandled solver failure"); + static_cast<ConstantExpr*>(ce.get())->toMemory((void*) &args[i]); + } else { + ref<Expr> arg = toUnique(state, *ai); + if (arg.isConstant()) { + // XXX kick toMemory functions from here + static_cast<ConstantExpr*>(arg.get())->toMemory((void*) &args[i]); + } else { + std::string msg = "external call with symbolic argument: " + function->getName(); + terminateStateOnExecError(state, msg); + return; + } + } + } + + state.addressSpace.copyOutConcretes(); + + if (!SuppressExternalWarnings) { + std::ostringstream os; + os << "calling external: " << function->getName().c_str() << "("; + for (unsigned i=0; i<arguments.size(); i++) { + os << arguments[i]; + if (i != arguments.size()-1) + os << ", "; + } + os << ")"; + + if (AllExternalWarnings) + klee_warning("%s", os.str().c_str()); + else + klee_warning_once(function, "%s", os.str().c_str()); + } + + bool success = externalDispatcher->executeCall(function, target->inst, args); + if (!success) { + terminateStateOnError(state, "failed external call: " + function->getName(), "external.err"); + return; + } + + if (!state.addressSpace.copyInConcretes()) { + terminateStateOnError(state, "external modified read-only object", "external.err"); + return; + } + + const Type *resultType = target->inst->getType(); + if (resultType != Type::VoidTy) { + ref<Expr> e = ConstantExpr::fromMemory((void*) args, + Expr::getWidthForLLVMType(resultType)); + bindLocal(target, state, e); + } +} + +/***/ + +ref<Expr> Executor::replaceReadWithSymbolic(ExecutionState &state, + ref<Expr> e) { + unsigned n = interpreterOpts.MakeConcreteSymbolic; + if (!n || replayOut || replayPath) + return e; + + // right now, we don't replace symbolics (is there any reason too?) + if (!e.isConstant()) + return e; + + if (n != 1 && random() % n) + return e; + + // create a new fresh location, assert it is equal to concrete value in e + // and return it. + + const MemoryObject *mo = memory->allocate(Expr::getMinBytesForWidth(e.getWidth()), + false, false, + state.prevPC->inst); + assert(mo && "out of memory"); + ref<Expr> res = Expr::createTempRead(mo->array, e.getWidth()); + ref<Expr> eq = NotOptimizedExpr::create(EqExpr::create(e, res)); + llvm::cerr << "Making symbolic: " << eq << "\n"; + state.addConstraint(eq); + return res; +} + +ObjectState *Executor::bindObjectInState(ExecutionState &state, const MemoryObject *mo, + bool isLocal) { + ObjectState *os = new ObjectState(mo, mo->size); + state.addressSpace.bindObject(mo, os); + + // Its possible that multiple bindings of the same mo in the state + // will put multiple copies on this list, but it doesn't really + // matter because all we use this list for is to unbind the object + // on function return. + if (isLocal) + state.stack.back().allocas.push_back(mo); + + return os; +} + +void Executor::executeAllocN(ExecutionState &state, + uint64_t nelems, + uint64_t size, + uint64_t alignment, + bool isLocal, + KInstruction *target) { +#if 0 + // over-allocate so that we can properly align the whole buffer + uint64_t address = (uint64_t) (unsigned) malloc(nelems * size + alignment - 1); + address += (alignment - address % alignment); +#else + theMMap = + mmap((void*) 0x90000000, + nelems*size, PROT_READ|PROT_WRITE, + MAP_PRIVATE +#ifdef MAP_ANONYMOUS + |MAP_ANONYMOUS +#endif + , 0, 0); + uint64_t address = (uintptr_t) theMMap; + theMMapSize = nelems*size; +#endif + + for (unsigned i = 0; i < nelems; i++) { + MemoryObject *mo = memory->allocateFixed(address + i*size, size, state.prevPC->inst); + ObjectState *os = bindObjectInState(state, mo, isLocal); + os->initializeToRandom(); + + // bind the local to the first memory object in the whole array + if (i == 0) + bindLocal(target, state, mo->getBaseExpr()); + } + + llvm::cerr << "KLEE: allocN at: " << address << "\n"; +} + +void Executor::executeAlloc(ExecutionState &state, + ref<Expr> size, + bool isLocal, + KInstruction *target, + bool zeroMemory, + const ObjectState *reallocFrom) { + size = toUnique(state, size); + if (size.isConstant()) { + MemoryObject *mo = memory->allocate(size.getConstantValue(), isLocal, false, + state.prevPC->inst); + if (!mo) { + bindLocal(target, state, ref<Expr>(0, kMachinePointerType)); + } else { + ObjectState *os = bindObjectInState(state, mo, isLocal); + if (zeroMemory) { + os->initializeToZero(); + } else { + os->initializeToRandom(); + } + bindLocal(target, state, mo->getBaseExpr()); + + if (reallocFrom) { + 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()); + } + } + } else { + // XXX For now we just pick a size. Ideally we would support + // symbolic sizes fully but even if we don't it would be better to + // "smartly" pick a value, for example we could fork and pick the + // min and max values and perhaps some intermediate (reasonable + // value). + // + // It would also be nice to recognize the case when size has + // exactly two values and just fork (but we need to get rid of + // return argument first). This shows up in pcre when llvm + // collapses the size expression with a select. + + ref<Expr> example; + bool success = solver->getValue(state, size, example); + assert(success && "FIXME: Unhandled solver failure"); + + // Try and start with a small example + while (example.getConstantValue()>128) { + ref<Expr> tmp = ref<Expr>(example.getConstantValue() >> 1, + example.getWidth()); + bool res; + bool success = solver->mayBeTrue(state, EqExpr::create(tmp, size), res); + assert(success && "FIXME: Unhandled solver failure"); + if (!res) + break; + example = tmp; + } + + StatePair fixedSize = fork(state, EqExpr::create(example, size), true); + + if (fixedSize.second) { + // Check for exactly two values + ref<Expr> tmp; + bool success = solver->getValue(*fixedSize.second, size, tmp); + assert(success && "FIXME: Unhandled solver failure"); + bool res; + success = solver->mustBeTrue(*fixedSize.second, + EqExpr::create(tmp, size), + res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + executeAlloc(*fixedSize.second, tmp, isLocal, + target, zeroMemory, reallocFrom); + } else { + // See if a *really* big value is possible. If so assume + // malloc will fail for it, so lets fork and return 0. + StatePair hugeSize = fork(*fixedSize.second, + UltExpr::create(ref<Expr>(1<<31, Expr::Int32), size), + true); + if (hugeSize.first) { + klee_message("NOTE: found huge malloc, returing 0"); + bindLocal(target, *hugeSize.first, ref<Expr>(0,kMachinePointerType)); + } + + if (hugeSize.second) { + std::ostringstream info; + ExprPPrinter::printOne(info, " size expr", size); + info << " concretization : " << example << "\n"; + info << " unbound example: " << tmp << "\n"; + terminateStateOnError(*hugeSize.second, + "concretized symbolic size", + "model.err", + info.str()); + } + } + } + + if (fixedSize.first) // can be zero when fork fails + executeAlloc(*fixedSize.first, example, isLocal, + target, zeroMemory, reallocFrom); + } +} + +void Executor::executeFree(ExecutionState &state, + ref<Expr> address, + KInstruction *target) { + StatePair zeroPointer = fork(state, Expr::createIsZero(address), true); + if (zeroPointer.first) { + if (target) + bindLocal(target, *zeroPointer.first, Expr::createPointer(0)); + } + if (zeroPointer.second) { // address != 0 + ExactResolutionList rl; + resolveExact(*zeroPointer.second, address, rl, "free"); + + for (Executor::ExactResolutionList::iterator it = rl.begin(), + ie = rl.end(); it != ie; ++it) { + const MemoryObject *mo = it->first.first; + if (mo->isLocal) { + terminateStateOnError(*it->second, + "free of alloca", + "free.err", + getAddressInfo(*it->second, address)); + } else if (mo->isGlobal) { + terminateStateOnError(*it->second, + "free of global", + "free.err", + getAddressInfo(*it->second, address)); + } else { + it->second->addressSpace.unbindObject(mo); + if (target) + bindLocal(target, *it->second, Expr::createPointer(0)); + } + } + } +} + +void Executor::resolveExact(ExecutionState &state, + ref<Expr> p, + ExactResolutionList &results, + const std::string &name) { + // XXX we may want to be capping this? + ResolutionList rl; + state.addressSpace.resolve(state, solver, p, rl); + + ExecutionState *unbound = &state; + for (ResolutionList::iterator it = rl.begin(), ie = rl.end(); + it != ie; ++it) { + ref<Expr> inBounds = EqExpr::create(p, it->first->getBaseExpr()); + + StatePair branches = fork(*unbound, inBounds, true); + + if (branches.first) + results.push_back(std::make_pair(*it, branches.first)); + + unbound = branches.second; + if (!unbound) // Fork failure + break; + } + + if (unbound) { + terminateStateOnError(*unbound, + "memory error: invalid pointer: " + name, + "ptr.err", + getAddressInfo(*unbound, p)); + } +} + +void Executor::executeMemoryOperation(ExecutionState &state, + bool isWrite, + ref<Expr> address, + ref<Expr> value /* undef if read */, + KInstruction *target /* undef if write */) { + Expr::Width type = (isWrite ? value.getWidth() : + Expr::getWidthForLLVMType(target->inst->getType())); + unsigned bytes = Expr::getMinBytesForWidth(type); + + if (SimplifySymIndices) { + if (!address.isConstant()) + address = state.constraints.simplifyExpr(address); + if (isWrite && !value.isConstant()) + value = state.constraints.simplifyExpr(value); + } + + // fast path: single in-bounds resolution + ObjectPair op; + bool success; + solver->setTimeout(stpTimeout); + if (!state.addressSpace.resolveOne(state, solver, address, op, success)) { + address = toConstant(state, address, "resolveOne failure"); + success = state.addressSpace.resolveOne(address.getConstantValue(), op); + } + solver->setTimeout(0); + + if (success) { + const MemoryObject *mo = op.first; + + if (MaxSymArraySize && mo->size>=MaxSymArraySize) { + address = toConstant(state, address, "max-sym-array-size"); + } + + ref<Expr> offset = mo->getOffsetExpr(address); + + bool inBounds; + solver->setTimeout(stpTimeout); + bool success = solver->mustBeTrue(state, + mo->getBoundsCheckOffset(offset, bytes), + inBounds); + solver->setTimeout(0); + if (!success) { + state.pc = state.prevPC; + terminateStateEarly(state, "query timed out"); + return; + } + + if (inBounds) { + const ObjectState *os = op.second; + if (isWrite) { + if (os->readOnly) { + terminateStateOnError(state, + "memory error: object read only", + "readonly.err"); + } else { + ObjectState *wos = state.addressSpace.getWriteable(mo, os); + wos->write(offset, value); + } + } else { + ref<Expr> result = os->read(offset, type); + + if (interpreterOpts.MakeConcreteSymbolic) + result = replaceReadWithSymbolic(state, result); + + bindLocal(target, state, result); + } + + return; + } + } + + // we are on an error path (no resolution, multiple resolution, one + // resolution with out of bounds) + + ResolutionList rl; + solver->setTimeout(stpTimeout); + bool incomplete = state.addressSpace.resolve(state, solver, address, rl, + 0, stpTimeout); + solver->setTimeout(0); + + // XXX there is some query wasteage here. who cares? + ExecutionState *unbound = &state; + + for (ResolutionList::iterator i = rl.begin(), ie = rl.end(); i != ie; ++i) { + const MemoryObject *mo = i->first; + const ObjectState *os = i->second; + ref<Expr> inBounds = mo->getBoundsCheckPointer(address, bytes); + + StatePair branches = fork(*unbound, inBounds, true); + ExecutionState *bound = branches.first; + + // bound can be 0 on failure or overlapped + if (bound) { + if (isWrite) { + if (os->readOnly) { + terminateStateOnError(*bound, + "memory error: object read only", + "readonly.err"); + } else { + ObjectState *wos = bound->addressSpace.getWriteable(mo, os); + wos->write(mo->getOffsetExpr(address), value); + } + } else { + ref<Expr> result = os->read(mo->getOffsetExpr(address), type); + bindLocal(target, *bound, result); + } + } + + unbound = branches.second; + if (!unbound) + break; + } + + // XXX should we distinguish out of bounds and overlapped cases? + if (unbound) { + if (incomplete) { + terminateStateEarly(*unbound, "query timed out (resolve)"); + } else { + terminateStateOnError(*unbound, + "memory error: out of bound pointer", + "ptr.err", + getAddressInfo(*unbound, address)); + } + } +} + +void Executor::executeMakeSymbolic(ExecutionState &state, + const MemoryObject *mo) { + // make a new one and rebind, we use bind here because we want to + // create a flat out new state, not a copy. although I'm not really + // sure it matters. + ObjectState *os = bindObjectInState(state, mo, false); + if (!replayOut) { + os->makeSymbolic(); + state.addSymbolic(mo); + + std::map< ExecutionState*, std::vector<SeedInfo> >::iterator it = + seedMap.find(&state); + if (it!=seedMap.end()) { // In seed mode we need to add this as a + // binding. + for (std::vector<SeedInfo>::iterator siit = it->second.begin(), + siie = it->second.end(); siit != siie; ++siit) { + SeedInfo &si = *siit; + BOutObject *obj = si.getNextInput(mo, + NamedSeedMatching); + + if (!obj) { + if (ZeroSeedExtension) { + std::vector<unsigned char> &values = + si.assignment.bindings[mo->array]; + values = std::vector<unsigned char>(mo->size, '\0'); + } else if (!AllowSeedExtension) { + terminateStateOnError(state, + "ran out of inputs during seeding", + "user.err"); + break; + } + } else { + if (obj->numBytes != mo->size && + ((!(AllowSeedExtension || ZeroSeedExtension) + && obj->numBytes < mo->size) || + (!AllowSeedTruncation && obj->numBytes > mo->size))) { + std::stringstream msg; + msg << "replace size mismatch: " + << mo->name << "[" << mo->size << "]" + << " vs " << obj->name << "[" << obj->numBytes << "]" + << " in bout\n"; + + terminateStateOnError(state, + msg.str(), + "user.err"); + break; + } else { + std::vector<unsigned char> &values = + si.assignment.bindings[mo->array]; + values.insert(values.begin(), obj->bytes, + obj->bytes + std::min(obj->numBytes, mo->size)); + if (ZeroSeedExtension) { + for (unsigned i=obj->numBytes; i<mo->size; ++i) + values.push_back('\0'); + } + } + } + } + } + } else { + if (replayPosition >= replayOut->numObjects) { + terminateStateOnError(state, "replay count mismatch", "user.err"); + } else { + BOutObject *obj = &replayOut->objects[replayPosition++]; + if (obj->numBytes != mo->size) { + terminateStateOnError(state, "replay size mismatch", "user.err"); + } else { + for (unsigned i=0; i<mo->size; i++) + os->write8(i, obj->bytes[i]); + } + } + } +} + +/***/ + +void Executor::runFunctionAsMain(Function *f, + int argc, + char **argv, + char **envp) { + std::vector<ref<Expr> > arguments; + + // force deterministic initialization of memory objects + srand(1); + srandom(1); + + MemoryObject *argvMO = 0; + + // In order to make uclibc happy and be closer to what the system is + // doing we lay out the environments at the end of the argv array + // (both are terminated by a null). There is also a final terminating + // null that uclibc seems to expect, possibly the ELF header? + + int envc; + for (envc=0; envp[envc]; ++envc) ; + + KFunction *kf = kmodule->functionMap[f]; + assert(kf); + Function::arg_iterator ai = f->arg_begin(), ae = f->arg_end(); + if (ai!=ae) { + arguments.push_back(ref<Expr>(argc, Expr::Int32)); + + if (++ai!=ae) { + argvMO = memory->allocate((argc+1+envc+1+1) * kMachinePointerSize, false, true, + f->begin()->begin()); + + arguments.push_back(argvMO->getBaseExpr()); + + if (++ai!=ae) { + uint64_t envp_start = argvMO->address + (argc+1)*kMachinePointerSize; + arguments.push_back(Expr::createPointer(envp_start)); + + if (++ai!=ae) + klee_error("invalid main function (expect 0-3 arguments)"); + } + } + } + + ExecutionState *state = new ExecutionState(kmodule->functionMap[f]); + + if (pathWriter) + state->pathOS = pathWriter->open(); + if (symPathWriter) + state->symPathOS = symPathWriter->open(); + + + if (statsTracker) + statsTracker->framePushed(*state, 0); + + assert(arguments.size() == f->arg_size() && "wrong number of arguments"); + for (unsigned i = 0, e = f->arg_size(); i != e; ++i) + bindArgument(kf, i, *state, arguments[i]); + + if (argvMO) { + ObjectState *argvOS = bindObjectInState(*state, argvMO, false); + + for (int i=0; i<argc+1+envc+1+1; i++) { + MemoryObject *arg; + + if (i==argc || i>=argc+1+envc) { + arg = 0; + } else { + char *s = i<argc ? argv[i] : envp[i-(argc+1)]; + int j, len = strlen(s); + + arg = memory->allocate(len+1, false, true, state->pc->inst); + ObjectState *os = bindObjectInState(*state, arg, false); + for (j=0; j<len+1; j++) + os->write8(j, s[j]); + } + + if (arg) { + argvOS->write(i * kMachinePointerSize, arg->getBaseExpr()); + } else { + argvOS->write(i * kMachinePointerSize, Expr::createPointer(0)); + } + } + } + + initializeGlobals(*state); + + processTree = new PTree(state); + state->ptreeNode = processTree->root; + run(*state); + delete processTree; + processTree = 0; + + // hack to clear memory objects + delete memory; + memory = new MemoryManager(); + + globalObjects.clear(); + globalAddresses.clear(); + + if (statsTracker) + statsTracker->done(); + + if (theMMap) { + munmap(theMMap, theMMapSize); + theMMap = 0; + } +} + +unsigned Executor::getPathStreamID(const ExecutionState &state) { + assert(pathWriter); + return state.pathOS.getID(); +} + +unsigned Executor::getSymbolicPathStreamID(const ExecutionState &state) { + assert(symPathWriter); + return state.symPathOS.getID(); +} + +void Executor::getConstraintLog(const ExecutionState &state, + std::string &res, + bool asCVC) { + if (asCVC) { + Query query(state.constraints, ref<Expr>(0, Expr::Bool)); + char *log = solver->stpSolver->getConstraintLog(query); + res = std::string(log); + free(log); + } else { + std::ostringstream info; + ExprPPrinter::printConstraints(info, state.constraints); + res = info.str(); + } +} + +bool Executor::getSymbolicSolution(const ExecutionState &state, + std::vector< + std::pair<std::string, + std::vector<unsigned char> > > + &res) { + solver->setTimeout(stpTimeout); + + ExecutionState tmp(state); + if (!NoPreferCex) { + for (std::vector<const MemoryObject*>::const_iterator + it = state.symbolics.begin(), ie = state.symbolics.end(); + it != ie; ++it) { + const MemoryObject *mo = *it; + std::vector< ref<Expr> >::const_iterator pi = + mo->cexPreferences.begin(), pie = mo->cexPreferences.end(); + for (; pi != pie; ++pi) { + bool mustBeTrue; + bool success = solver->mustBeTrue(tmp, Expr::createNot(*pi), + mustBeTrue); + if (!success) break; + if (!mustBeTrue) tmp.addConstraint(*pi); + } + if (pi!=pie) break; + } + } + + std::vector< std::vector<unsigned char> > values; + std::vector<const Array*> objects; + for (unsigned i = 0; i != state.symbolics.size(); ++i) + objects.push_back(state.symbolics[i]->array); + bool success = solver->getInitialValues(tmp, objects, values); + solver->setTimeout(0); + if (!success) { + klee_warning("unable to compute initial values (invalid constraints?)!"); + ExprPPrinter::printQuery(std::cerr, + state.constraints, + ref<Expr>(0,Expr::Bool)); + return false; + } + + unsigned i = 0; + for (std::vector<const MemoryObject*>::const_iterator + it = state.symbolics.begin(), ie = state.symbolics.end(); + it != ie; ++it) { + res.push_back(std::make_pair((*it)->name, values[i])); + ++i; + } + return true; +} + +void Executor::getCoveredLines(const ExecutionState &state, + std::map<const std::string*, std::set<unsigned> > &res) { + res = state.coveredLines; +} + +void Executor::doImpliedValueConcretization(ExecutionState &state, + ref<Expr> e, + ref<Expr> value) { + assert(value.isConstant() && "non-constant passed in place of constant"); + + if (DebugCheckForImpliedValues) + ImpliedValue::checkForImpliedValues(solver->solver, e, value); + + ImpliedValueList results; + ImpliedValue::getImpliedValues(e, value, results); + for (ImpliedValueList::iterator it = results.begin(), ie = results.end(); + it != ie; ++it) { + ReadExpr *re = it->first.get(); + + if (re->index.isConstant()) { + // FIXME: This is the sole remaining usage of the Array object + // variable. Kill me. + const MemoryObject *mo = re->updates.root->object; + const ObjectState *os = state.addressSpace.findObject(mo); + + if (!os) { + // object has been free'd, no need to concretize (although as + // in other cases we would like to concretize the outstanding + // reads, but we have no facility for that yet) + } else { + assert(!os->readOnly && "not possible? read only object with static read?"); + ObjectState *wos = state.addressSpace.getWriteable(mo, os); + wos->write(re->index.getConstantValue(), it->second); + } + } + } +} + +/// + +Interpreter *Interpreter::create(const InterpreterOptions &opts, + InterpreterHandler *ih) { + return new Executor(opts, ih); +} diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h new file mode 100644 index 00000000..76868291 --- /dev/null +++ b/lib/Core/Executor.h @@ -0,0 +1,445 @@ +//===-- Executor.h ----------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Class to perform actual execution, hides implementation details from external +// interpreter. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXECUTOR_H +#define KLEE_EXECUTOR_H + +#include "klee/Interpreter.h" +#include "llvm/Support/CallSite.h" +#include <vector> +#include <string> +#include <map> +#include <set> + +struct BOut; + +namespace llvm { + class BasicBlock; + class BranchInst; + class CallInst; + class Constant; + class ConstantExpr; + class Function; + class GlobalValue; + class Instruction; + class TargetData; + class Value; +} + +namespace klee { + class ExecutionState; + class ExternalDispatcher; + class Expr; + class InstructionInfoTable; + class KFunction; + class KInstruction; + class KInstIterator; + class KModule; + class MemoryManager; + class MemoryObject; + class ObjectState; + class PTree; + class Searcher; + class SeedInfo; + class SpecialFunctionHandler; + class StackFrame; + class StatsTracker; + class TimingSolver; + class TreeStreamWriter; + template<class T> class ref; + + /// \todo Add a context object to keep track of data only live + /// during an instruction step. Should contain addedStates, + /// removedStates, and haltExecution, among others. + +class Executor : public Interpreter { + friend class BumpMergingSearcher; + friend class MergingSearcher; + friend class RandomPathSearcher; + friend class OwningSearcher; + friend class WeightedRandomSearcher; + friend class SpecialFunctionHandler; + friend class StatsTracker; + +public: + class Timer { + public: + Timer(); + virtual ~Timer(); + + /// The event callback. + virtual void run() = 0; + }; + + typedef std::pair<ExecutionState*,ExecutionState*> StatePair; + +private: + class TimerInfo; + + KModule *kmodule; + InterpreterHandler *interpreterHandler; + Searcher *searcher; + + ExternalDispatcher *externalDispatcher; + TimingSolver *solver; + MemoryManager *memory; + std::set<ExecutionState*> states; + StatsTracker *statsTracker; + TreeStreamWriter *pathWriter, *symPathWriter; + SpecialFunctionHandler *specialFunctionHandler; + std::vector<TimerInfo*> timers; + PTree *processTree; + + /// Used to track states that have been added during the current + /// instructions step. + /// \invariant \ref addedStates is a subset of \ref states. + /// \invariant \ref addedStates and \ref removedStates are disjoint. + std::set<ExecutionState*> addedStates; + /// Used to track states that have been removed during the current + /// instructions step. + /// \invariant \ref removedStates is a subset of \ref states. + /// \invariant \ref addedStates and \ref removedStates are disjoint. + std::set<ExecutionState*> removedStates; + + /// When non-empty the Executor is running in "seed" mode. The + /// states in this map will be executed in an arbitrary order + /// (outside the normal search interface) until they terminate. When + /// the states reach a symbolic branch then either direction that + /// satisfies one or more seeds will be added to this map. What + /// happens with other states (that don't satisfy the seeds) depends + /// on as-yet-to-be-determined flags. + std::map<ExecutionState*, std::vector<SeedInfo> > seedMap; + + /// Map of globals to their representative memory object. + std::map<const llvm::GlobalValue*, MemoryObject*> globalObjects; + + /// Map of globals to their bound address. This also includes + /// globals that have no representative object (i.e. functions). + std::map<const llvm::GlobalValue*, ref<Expr> > globalAddresses; + + /// The set of legal function addresses, used to validate function + /// pointers. + std::set<void*> legalFunctions; + + /// When non-null the bindings that will be used for calls to + /// klee_make_symbolic in order replay. + const struct BOut *replayOut; + /// When non-null a list of branch decisions to be used for replay. + const std::vector<bool> *replayPath; + /// The index into the current \ref replayOut or \ref replayPath + /// object. + unsigned replayPosition; + + /// When non-null a list of "seed" inputs which will be used to + /// drive execution. + const std::vector<struct BOut *> *usingSeeds; + + /// Disables forking, instead a random path is chosen. Enabled as + /// needed to control memory usage. \see fork() + bool atMemoryLimit; + + /// Disables forking, set by client. \see setInhibitForking() + bool inhibitForking; + + /// Signals the executor to halt execution at the next instruction + /// step. + bool haltExecution; + + /// Whether implied-value concretization is enabled. Currently + /// false, it is buggy (it needs to validate its writes). + bool ivcEnabled; + + /// The maximum time to allow for a single stp query. + double stpTimeout; + + llvm::Function* getCalledFunction(llvm::CallSite &cs, ExecutionState &state); + + void executeInstruction(ExecutionState &state, KInstruction *ki); + + void printFileLine(ExecutionState &state, KInstruction *ki); + + void run(ExecutionState &initialState); + + // Given a concrete object in our [klee's] address space, add it to + // objects checked code can reference. + MemoryObject *addExternalObject(ExecutionState &state, void *addr, + unsigned size, bool isReadOnly); + + void initializeGlobalObject(ExecutionState &state, ObjectState *os, + llvm::Constant *c, + unsigned offset); + void initializeGlobals(ExecutionState &state); + + void stepInstruction(ExecutionState &state); + void updateStates(ExecutionState *current); + void transferToBasicBlock(llvm::BasicBlock *dst, + llvm::BasicBlock *src, + ExecutionState &state); + + void callExternalFunction(ExecutionState &state, + KInstruction *target, + llvm::Function *function, + std::vector< ref<Expr> > &arguments); + + ObjectState *bindObjectInState(ExecutionState &state, const MemoryObject *mo, + bool isLocal); + + /// Resolve a pointer to the memory objects it could point to the + /// start of, forking execution when necessary and generating errors + /// for pointers to invalid locations (either out of bounds or + /// address inside the middle of objects). + /// + /// \param results[out] A list of ((MemoryObject,ObjectState), + /// state) pairs for each object the given address can point to the + /// beginning of. + typedef std::vector< std::pair<std::pair<const MemoryObject*, const ObjectState*>, + ExecutionState*> > ExactResolutionList; + void resolveExact(ExecutionState &state, + ref<Expr> p, + ExactResolutionList &results, + const std::string &name); + + /// Allocate and bind a new object in a particular state. NOTE: This + /// function may fork. + /// + /// \param isLocal Flag to indicate if the object should be + /// automatically deallocated on function return (this also makes it + /// illegal to free directly). + /// + /// \param target Value at which to bind the base address of the new + /// object. + /// + /// \param reallocFrom If non-zero and the allocation succeeds, + /// initialize the new object from the given one and unbind it when + /// done (realloc semantics). The initialized bytes will be the + /// minimum of the size of the old and new objects, with remaining + /// bytes initialized as specified by zeroMemory. + void executeAlloc(ExecutionState &state, + ref<Expr> size, + bool isLocal, + KInstruction *target, + bool zeroMemory=false, + const ObjectState *reallocFrom=0); + + /// XXX not for public use (this is for histar, it allocations a + /// contiguous set of objects, while guaranteeing page alignment) + void executeAllocN(ExecutionState &state, + uint64_t nelems, + uint64_t size, + uint64_t alignment, + bool isLocal, + KInstruction *target); + + /// Free the given address with checking for errors. If target is + /// given it will be bound to 0 in the resulting states (this is a + /// convenience for realloc). Note that this function can cause the + /// state to fork and that \ref state cannot be safely accessed + /// afterwards. + void executeFree(ExecutionState &state, + ref<Expr> address, + KInstruction *target = 0); + + void executeCall(ExecutionState &state, + KInstruction *ki, + llvm::Function *f, + std::vector< ref<Expr> > &arguments); + + // do address resolution / object binding / out of bounds checking + // and perform the operation + void executeMemoryOperation(ExecutionState &state, + bool isWrite, + ref<Expr> address, + ref<Expr> value /* undef if read */, + KInstruction *target /* undef if write */); + + void executeMakeSymbolic(ExecutionState &state, const MemoryObject *mo); + + /// Create a new state where each input condition has been added as + /// a constraint and return the results. The input state is included + /// as one of the results. Note that the output vector may included + /// NULL pointers for states which were unable to be created. + void branch(ExecutionState &state, + const std::vector< ref<Expr> > &conditions, + std::vector<ExecutionState*> &result); + + // Fork current and return states in which condition holds / does + // not hold, respectively. One of the states is necessarily the + // current state, and one of the states may be null. + StatePair fork(ExecutionState ¤t, ref<Expr> condition, bool isInternal); + + /// Add the given (boolean) condition as a constraint on state. This + /// function is a wrapper around the state's addConstraint function + /// which also manages manages propogation of implied values, + /// validity checks, and seed patching. + void addConstraint(ExecutionState &state, ref<Expr> condition); + + // Called on [for now] concrete reads, replaces constant with a symbolic + // Used for testing. + ref<Expr> replaceReadWithSymbolic(ExecutionState &state, ref<Expr> e); + + ref<Expr> eval(KInstruction *ki, + unsigned index, + ExecutionState &state); + + void bindLocal(KInstruction *target, + ExecutionState &state, + ref<Expr> value); + void bindArgument(KFunction *kf, + unsigned index, + ExecutionState &state, + ref<Expr> value); + + ref<Expr> evalConstantExpr(llvm::ConstantExpr *ce); + + /// Return a unique constant value for the given expression in the + /// given state, if it has one (i.e. it provably only has a single + /// value). Otherwise return the original expression. + ref<Expr> toUnique(const ExecutionState &state, ref<Expr> &e); + + /// Return a constant value for the given expression, forcing it to + /// be constant in the given state by adding a constraint if + /// necessary. Note that this function breaks completeness and + /// should generally be avoided. + /// + /// \param purpose An identify string to printed in case of concretization. + ref<Expr> toConstant(ExecutionState &state, ref<Expr> e, const char *purpose); + + /// Bind a constant value for e to the given target. NOTE: This + /// function may fork state if the state has multiple seeds. + void executeGetValue(ExecutionState &state, ref<Expr> e, KInstruction *target); + + /// Get textual information regarding a memory address. + std::string getAddressInfo(ExecutionState &state, ref<Expr> address) const; + + // remove state from queue and delete + void terminateState(ExecutionState &state); + // call exit handler and terminate state + void terminateStateEarly(ExecutionState &state, std::string message); + // call exit handler and terminate state + void terminateStateOnExit(ExecutionState &state); + // call error handler and terminate state + void terminateStateOnError(ExecutionState &state, + const std::string &message, + const std::string &suffix, + const std::string &longMessage=""); + + // call error handler and terminate state, for execution errors + // (things that should not be possible, like illegal instruction or + // unlowered instrinsic, or are unsupported, like inline assembly) + void terminateStateOnExecError(ExecutionState &state, + const std::string &message, + const std::string &info="") { + terminateStateOnError(state, message, "exec.err", info); + } + + /// bindModuleConstants - Initialize the module constant table. + void bindModuleConstants(); + + /// bindInstructionConstants - Initialize any necessary per instruction + /// constant values. + void bindInstructionConstants(KInstruction *KI); + + void handlePointsToObj(ExecutionState &state, + KInstruction *target, + const std::vector<ref<Expr> > &arguments); + + void doImpliedValueConcretization(ExecutionState &state, + ref<Expr> e, + ref<Expr> value); + + /// Add a timer to be executed periodically. + /// + /// \param timer The timer object to run on firings. + /// \param rate The approximate delay (in seconds) between firings. + void addTimer(Timer *timer, double rate); + + void initTimers(); + void processTimers(ExecutionState *current, + double maxInstTime); + +public: + Executor(const InterpreterOptions &opts, InterpreterHandler *ie); + virtual ~Executor(); + + const InterpreterHandler& getHandler() { + return *interpreterHandler; + } + + // XXX should just be moved out to utility module + ref<Expr> evalConstant(llvm::Constant *c); + + virtual void setPathWriter(TreeStreamWriter *tsw) { + pathWriter = tsw; + } + virtual void setSymbolicPathWriter(TreeStreamWriter *tsw) { + symPathWriter = tsw; + } + + virtual void setReplayOut(const struct BOut *out) { + assert(!replayPath && "cannot replay both buffer and path"); + replayOut = out; + replayPosition = 0; + } + + virtual void setReplayPath(const std::vector<bool> *path) { + assert(!replayOut && "cannot replay both buffer and path"); + replayPath = path; + replayPosition = 0; + } + + virtual const llvm::Module * + setModule(llvm::Module *module, const ModuleOptions &opts); + + virtual void useSeeds(const std::vector<struct BOut *> *seeds) { + usingSeeds = seeds; + } + + virtual void runFunctionAsMain(llvm::Function *f, + int argc, + char **argv, + char **envp); + + /*** Runtime options ***/ + + virtual void setHaltExecution(bool value) { + haltExecution = value; + } + + virtual void setInhibitForking(bool value) { + inhibitForking = value; + } + + /*** State accessor methods ***/ + + virtual unsigned getPathStreamID(const ExecutionState &state); + + virtual unsigned getSymbolicPathStreamID(const ExecutionState &state); + + virtual void getConstraintLog(const ExecutionState &state, + std::string &res, + bool asCVC = false); + + virtual bool getSymbolicSolution(const ExecutionState &state, + std::vector< + std::pair<std::string, + std::vector<unsigned char> > > + &res); + + virtual void getCoveredLines(const ExecutionState &state, + std::map<const std::string*, std::set<unsigned> > &res); +}; + +} // End klee namespace + +#endif diff --git a/lib/Core/ExecutorTimers.cpp b/lib/Core/ExecutorTimers.cpp new file mode 100644 index 00000000..51792e0d --- /dev/null +++ b/lib/Core/ExecutorTimers.cpp @@ -0,0 +1,220 @@ +//===-- ExecutorTimers.cpp ------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "CoreStats.h" +#include "Executor.h" +#include "PTree.h" +#include "StatsTracker.h" + +#include "klee/ExecutionState.h" +#include "klee/Internal/Module/InstructionInfoTable.h" +#include "klee/Internal/Module/KInstruction.h" +#include "klee/Internal/Module/KModule.h" +#include "klee/Internal/System/Time.h" + +#include "llvm/Function.h" +#include "llvm/Support/CommandLine.h" + +#include <unistd.h> +#include <signal.h> +#include <sys/time.h> +#include <math.h> + + +using namespace llvm; +using namespace klee; + +cl::opt<double> +MaxTime("max-time", + cl::desc("Halt execution after the specified number of seconds (0=off)"), + cl::init(0)); + +/// + +class HaltTimer : public Executor::Timer { + Executor *executor; + +public: + HaltTimer(Executor *_executor) : executor(_executor) {} + ~HaltTimer() {} + + void run() { + llvm::cerr << "KLEE: HaltTimer invoked\n"; + executor->setHaltExecution(true); + } +}; + +/// + +static const double kSecondsPerTick = .1; +static volatile unsigned timerTicks = 0; + +// XXX hack +extern "C" unsigned dumpStates, dumpPTree; +unsigned dumpStates = 0, dumpPTree = 0; + +static void onAlarm(int) { + ++timerTicks; +} + +// oooogalay +static void setupHandler() { + struct itimerval t; + struct timeval tv; + + tv.tv_sec = (long) kSecondsPerTick; + tv.tv_usec = (long) (fmod(kSecondsPerTick, 1.)*1000000); + + t.it_interval = t.it_value = tv; + + ::setitimer(ITIMER_REAL, &t, 0); + ::signal(SIGALRM, onAlarm); +} + +void Executor::initTimers() { + static bool first = true; + + if (first) { + first = false; + setupHandler(); + } + + if (MaxTime) { + addTimer(new HaltTimer(this), MaxTime); + } +} + +/// + +Executor::Timer::Timer() {} + +Executor::Timer::~Timer() {} + +class Executor::TimerInfo { +public: + Timer *timer; + + /// Approximate delay per timer firing. + double rate; + /// Wall time for next firing. + double nextFireTime; + +public: + TimerInfo(Timer *_timer, double _rate) + : timer(_timer), + rate(_rate), + nextFireTime(util::getWallTime() + rate) {} + ~TimerInfo() { delete timer; } +}; + +void Executor::addTimer(Timer *timer, double rate) { + timers.push_back(new TimerInfo(timer, rate)); +} + +void Executor::processTimers(ExecutionState *current, + double maxInstTime) { + static unsigned callsWithoutCheck = 0; + unsigned ticks = timerTicks; + + if (!ticks && ++callsWithoutCheck > 1000) { + setupHandler(); + ticks = 1; + } + + if (ticks || dumpPTree || dumpStates) { + if (dumpPTree) { + char name[32]; + sprintf(name, "ptree%08d.dot", (int) stats::instructions); + std::ostream *os = interpreterHandler->openOutputFile(name); + if (os) { + processTree->dump(*os); + delete os; + } + + dumpPTree = 0; + } + + if (dumpStates) { + std::ostream *os = interpreterHandler->openOutputFile("states.txt"); + + if (os) { + for (std::set<ExecutionState*>::const_iterator it = states.begin(), + ie = states.end(); it != ie; ++it) { + ExecutionState *es = *it; + *os << "(" << es << ","; + *os << "["; + ExecutionState::stack_ty::iterator next = es->stack.begin(); + ++next; + for (ExecutionState::stack_ty::iterator sfIt = es->stack.begin(), + sf_ie = es->stack.end(); sfIt != sf_ie; ++sfIt) { + *os << "('" << sfIt->kf->function->getName() << "',"; + if (next == es->stack.end()) { + *os << es->prevPC->info->line << "), "; + } else { + *os << next->caller->info->line << "), "; + ++next; + } + } + *os << "], "; + + StackFrame &sf = es->stack.back(); + uint64_t md2u = computeMinDistToUncovered(es->pc, + sf.minDistToUncoveredOnReturn); + uint64_t icnt = theStatisticManager->getIndexedValue(stats::instructions, + es->pc->info->id); + uint64_t cpicnt = sf.callPathNode->statistics.getValue(stats::instructions); + + *os << "{"; + *os << "'depth' : " << es->depth << ", "; + *os << "'weight' : " << es->weight << ", "; + *os << "'queryCost' : " << es->queryCost << ", "; + *os << "'coveredNew' : " << es->coveredNew << ", "; + *os << "'instsSinceCovNew' : " << es->instsSinceCovNew << ", "; + *os << "'md2u' : " << md2u << ", "; + *os << "'icnt' : " << icnt << ", "; + *os << "'CPicnt' : " << cpicnt << ", "; + *os << "}"; + *os << ")\n"; + } + + delete os; + } + + dumpStates = 0; + } + + if (maxInstTime>0 && current && !removedStates.count(current)) { + if (timerTicks*kSecondsPerTick > maxInstTime) { + klee_warning("max-instruction-time exceeded: %.2fs", + timerTicks*kSecondsPerTick); + terminateStateEarly(*current, "max-instruction-time exceeded"); + } + } + + if (!timers.empty()) { + double time = util::getWallTime(); + + for (std::vector<TimerInfo*>::iterator it = timers.begin(), + ie = timers.end(); it != ie; ++it) { + TimerInfo *ti = *it; + + if (time >= ti->nextFireTime) { + ti->timer->run(); + ti->nextFireTime = time + ti->rate; + } + } + } + + timerTicks = 0; + callsWithoutCheck = 0; + } +} + diff --git a/lib/Core/ExecutorUtil.cpp b/lib/Core/ExecutorUtil.cpp new file mode 100644 index 00000000..3b11dd42 --- /dev/null +++ b/lib/Core/ExecutorUtil.cpp @@ -0,0 +1,144 @@ +//===-- ExecutorUtil.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Executor.h" + +#include "klee/Expr.h" +#include "klee/Interpreter.h" +#include "klee/Machine.h" +#include "klee/Solver.h" + +#include "klee/Internal/Module/KModule.h" + +#include "llvm/Constants.h" +#include "llvm/Function.h" +#include "llvm/Instructions.h" +#include "llvm/Module.h" +#include "llvm/ModuleProvider.h" +#include "llvm/Support/CallSite.h" +#include "llvm/Support/GetElementPtrTypeIterator.h" +#include "llvm/Support/Streams.h" +#include "llvm/Target/TargetData.h" +#include <iostream> +#include <cassert> + +using namespace klee; +using namespace llvm; + +namespace klee { + +ref<Expr> +Executor::evalConstantExpr(llvm::ConstantExpr *ce) { + const llvm::Type *type = ce->getType(); + + ref<Expr> op1(0,Expr::Bool), op2(0,Expr::Bool), op3(0,Expr::Bool); + int numOperands = ce->getNumOperands(); + + if (numOperands > 0) op1 = evalConstant(ce->getOperand(0)); + if (numOperands > 1) op2 = evalConstant(ce->getOperand(1)); + if (numOperands > 2) op3 = evalConstant(ce->getOperand(2)); + + switch (ce->getOpcode()) { + case Instruction::Trunc: return ExtractExpr::createByteOff(op1, + 0, + Expr::getWidthForLLVMType(type)); + case Instruction::ZExt: return ZExtExpr::create(op1, + Expr::getWidthForLLVMType(type)); + case Instruction::SExt: return SExtExpr::create(op1, + Expr::getWidthForLLVMType(type)); + case Instruction::Add: return AddExpr::create(op1, op2); + case Instruction::Sub: return SubExpr::create(op1, op2); + case Instruction::Mul: return MulExpr::create(op1, op2); + case Instruction::SDiv: return SDivExpr::create(op1, op2); + case Instruction::UDiv: return UDivExpr::create(op1, op2); + case Instruction::SRem: return SRemExpr::create(op1, op2); + case Instruction::URem: return URemExpr::create(op1, op2); + case Instruction::And: return AndExpr::create(op1, op2); + case Instruction::Or: return OrExpr::create(op1, op2); + case Instruction::Xor: return XorExpr::create(op1, op2); + case Instruction::Shl: return ShlExpr::create(op1, op2); + case Instruction::LShr: return LShrExpr::create(op1, op2); + case Instruction::AShr: return AShrExpr::create(op1, op2); + case Instruction::BitCast: return op1; + + case Instruction::IntToPtr: { + return ZExtExpr::create(op1, Expr::getWidthForLLVMType(type)); + } + + case Instruction::PtrToInt: { + return ZExtExpr::create(op1, Expr::getWidthForLLVMType(type)); + } + + case Instruction::GetElementPtr: { + ref<Expr> base = op1; + + for (gep_type_iterator ii = gep_type_begin(ce), ie = gep_type_end(ce); + ii != ie; ++ii) { + ref<Expr> addend(0, kMachinePointerType); + + if (const StructType *st = dyn_cast<StructType>(*ii)) { + const StructLayout *sl = kmodule->targetData->getStructLayout(st); + const ConstantInt *ci = cast<ConstantInt>(ii.getOperand()); + + addend = Expr::createPointer(sl->getElementOffset((unsigned) + ci->getZExtValue())); + } else { + const SequentialType *st = cast<SequentialType>(*ii); + ref<Expr> index = evalConstant(cast<Constant>(ii.getOperand())); + unsigned elementSize = kmodule->targetData->getTypeStoreSize(st->getElementType()); + + index = Expr::createCoerceToPointerType(index); + addend = MulExpr::create(index, + Expr::createPointer(elementSize)); + } + + base = AddExpr::create(base, addend); + } + + return base; + } + + case Instruction::ICmp: { + switch(ce->getPredicate()) { + case ICmpInst::ICMP_EQ: return EqExpr::create(op1, op2); + case ICmpInst::ICMP_NE: return NeExpr::create(op1, op2); + case ICmpInst::ICMP_UGT: return UgtExpr::create(op1, op2); + case ICmpInst::ICMP_UGE: return UgeExpr::create(op1, op2); + case ICmpInst::ICMP_ULT: return UltExpr::create(op1, op2); + case ICmpInst::ICMP_ULE: return UleExpr::create(op1, op2); + case ICmpInst::ICMP_SGT: return SgtExpr::create(op1, op2); + case ICmpInst::ICMP_SGE: return SgeExpr::create(op1, op2); + case ICmpInst::ICMP_SLT: return SltExpr::create(op1, op2); + case ICmpInst::ICMP_SLE: return SleExpr::create(op1, op2); + default: + assert(0 && "unhandled ICmp predicate"); + } + } + + case Instruction::Select: { + return SelectExpr::create(op1, op2, op3); + } + + case Instruction::FDiv: + case Instruction::FRem: + case Instruction::FPTrunc: + case Instruction::FPExt: + case Instruction::UIToFP: + case Instruction::SIToFP: + case Instruction::FPToUI: + case Instruction::FPToSI: + case Instruction::FCmp: + assert(0 && "floating point ConstantExprs unsupported"); + + default : + assert(0 && "unknown ConstantExpr type"); + } +} + +} diff --git a/lib/Core/ExternalDispatcher.cpp b/lib/Core/ExternalDispatcher.cpp new file mode 100644 index 00000000..9e3b0a49 --- /dev/null +++ b/lib/Core/ExternalDispatcher.cpp @@ -0,0 +1,230 @@ +//===-- ExternalDispatcher.cpp --------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ExternalDispatcher.h" + +#include "llvm/Module.h" +#include "llvm/Constants.h" +#include "llvm/DerivedTypes.h" +#include "llvm/Instructions.h" +#include "llvm/ModuleProvider.h" +#include "llvm/ExecutionEngine/JIT.h" +#include "llvm/ExecutionEngine/GenericValue.h" +#include "llvm/Support/CallSite.h" +#include "llvm/System/DynamicLibrary.h" +#include "llvm/Support/Streams.h" +#include "llvm/Support/raw_ostream.h" +#include <setjmp.h> +#include <signal.h> + +using namespace llvm; +using namespace klee; + +/***/ + +static jmp_buf escapeCallJmpBuf; + +extern "C" { + +static void sigsegv_handler(int signal, siginfo_t *info, void *context) { + longjmp(escapeCallJmpBuf, 1); +} + +} + +void *ExternalDispatcher::resolveSymbol(const std::string &name) { + assert(executionEngine); + + const char *str = name.c_str(); + + // We use this to validate that function names can be resolved so we + // need to match how the JIT does it. Unfortunately we can't + // directly access the JIT resolution function + // JIT::getPointerToNamedFunction so we emulate the important points. + + if (str[0] == 1) // asm specifier, skipped + ++str; + + void *addr = dl_symbols.SearchForAddressOfSymbol(str); + if (addr) + return addr; + + // If it has an asm specifier and starts with an underscore we retry + // without the underscore. I (DWD) don't know why. + if (name[0] == 1 && str[0]=='_') { + ++str; + addr = dl_symbols.SearchForAddressOfSymbol(str); + } + + return addr; +} + +ExternalDispatcher::ExternalDispatcher() { + dispatchModule = new Module("ExternalDispatcher"); + ExistingModuleProvider* MP = new ExistingModuleProvider(dispatchModule); + + std::string error; + executionEngine = ExecutionEngine::createJIT(MP, &error); + if (!executionEngine) { + llvm::cerr << "unable to make jit: " << error << "\n"; + abort(); + } + + // from ExecutionEngine::create + if (executionEngine) { + // Make sure we can resolve symbols in the program as well. The zero arg + // to the function tells DynamicLibrary to load the program, not a library. + try { + dl_symbols.LoadLibraryPermanently(0); + } catch (...) { + assert(0 && "Exception in LoadLibraryPermantently.\n"); + } + } + +#ifdef WINDOWS + preboundFunctions["getpid"] = (void*) (long) getpid; + preboundFunctions["putchar"] = (void*) (long) putchar; + preboundFunctions["printf"] = (void*) (long) printf; + preboundFunctions["fprintf"] = (void*) (long) fprintf; + preboundFunctions["sprintf"] = (void*) (long) sprintf; +#endif +} + +ExternalDispatcher::~ExternalDispatcher() { + delete executionEngine; +} + +bool ExternalDispatcher::executeCall(Function *f, Instruction *i, uint64_t *args) { + dispatchers_ty::iterator it = dispatchers.find(i); + Function *dispatcher; + + if (it == dispatchers.end()) { +#ifdef WINDOWS + std::map<std::string, void*>::iterator it2 = + preboundFunctions.find(f->getName())); + + if (it2 != preboundFunctions.end()) { + // only bind once + if (it2->second) { + executionEngine->addGlobalMapping(f, it2->second); + it2->second = 0; + } + } +#endif + + dispatcher = createDispatcher(f,i); + + dispatchers.insert(std::make_pair(i, dispatcher)); + + if (dispatcher) { + // force the JIT execution engine to go ahead and build the + // function. this ensures that any errors or assertions in the + // compilation process will trigger crashes instead of being + // caught as aborts in the external function. + executionEngine->recompileAndRelinkFunction(dispatcher); + } + } else { + dispatcher = it->second; + } + + return runProtectedCall(dispatcher, args); +} + +// XXX not reentrant +static uint64_t *gTheArgsP; + +bool ExternalDispatcher::runProtectedCall(Function *f, uint64_t *args) { + struct sigaction segvAction, segvActionOld; + bool res; + + if (!f) + return false; + + std::vector<GenericValue> gvArgs; + gTheArgsP = args; + + segvAction.sa_handler = 0; + memset(&segvAction.sa_mask, 0, sizeof(segvAction.sa_mask)); + segvAction.sa_flags = SA_SIGINFO; + segvAction.sa_sigaction = ::sigsegv_handler; + sigaction(SIGSEGV, &segvAction, &segvActionOld); + + if (setjmp(escapeCallJmpBuf)) { + res = false; + } else { + executionEngine->runFunction(f, gvArgs); + res = true; + } + + sigaction(SIGSEGV, &segvActionOld, 0); + return res; +} + +// for performance purposes we construct the stub in such a way that +// the arguments pointer is passed through the static global variable +// gTheArgsP in this file. This is done so that the stub function +// prototype trivially matches the special cases that the JIT knows +// how to directly call. If this is not done, then the jit will end up +// generating a nullary stub just to call our stub, for every single +// function call. +Function *ExternalDispatcher::createDispatcher(Function *target, Instruction *inst) { + if (!resolveSymbol(target->getName())) + return 0; + + CallSite cs; + if (inst->getOpcode()==Instruction::Call) { + cs = CallSite(cast<CallInst>(inst)); + } else { + cs = CallSite(cast<InvokeInst>(inst)); + } + + Value **args = new Value*[cs.arg_size()]; + + std::vector<const Type*> nullary; + + Function *dispatcher = Function::Create(FunctionType::get(Type::VoidTy, + nullary, false), + GlobalVariable::ExternalLinkage, + "", + dispatchModule); + + + BasicBlock *dBB = BasicBlock::Create("entry", dispatcher); + + Instruction *argI64sp = new IntToPtrInst(ConstantInt::get(Type::Int64Ty, (long) (void*) &gTheArgsP), + PointerType::getUnqual(PointerType::getUnqual(Type::Int64Ty)), + "argsp", + dBB); + Instruction *argI64s = new LoadInst(argI64sp, "args", dBB); + + unsigned i = 0; + for (CallSite::arg_iterator ai = cs.arg_begin(), ae = cs.arg_end(); + ai!=ae; ++ai, ++i) { + Value *index = ConstantInt::get(Type::Int32Ty, i+1); + + Instruction *argI64p = GetElementPtrInst::Create(argI64s, index, "", dBB); + Instruction *argp = new BitCastInst(argI64p, + PointerType::getUnqual((*ai)->getType()), "", dBB); + args[i] = new LoadInst(argp, "", dBB); + } + + Instruction *result = CallInst::Create(target, args, args+i, "", dBB); + + if (result->getType() != Type::VoidTy) { + Instruction *resp = new BitCastInst(argI64s, + PointerType::getUnqual(result->getType()), "", dBB); + new StoreInst(result, resp, dBB); + } + + ReturnInst::Create(dBB); + + delete[] args; + + return dispatcher; +} diff --git a/lib/Core/ExternalDispatcher.h b/lib/Core/ExternalDispatcher.h new file mode 100644 index 00000000..fc8f80f4 --- /dev/null +++ b/lib/Core/ExternalDispatcher.h @@ -0,0 +1,50 @@ +//===-- ExternalDispatcher.h ------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_EXTERNALDISPATCHER_H +#define KLEE_EXTERNALDISPATCHER_H + +#include <map> +#include "llvm/System/DynamicLibrary.h" + +namespace llvm { + class ExecutionEngine; + class Instruction; + class Function; + class FunctionType; + class Module; +} + +namespace klee { + class ExternalDispatcher { + private: + typedef std::map<const llvm::Instruction*,llvm::Function*> dispatchers_ty; + dispatchers_ty dispatchers; + llvm::Module *dispatchModule; + llvm::ExecutionEngine *executionEngine; + llvm::sys::DynamicLibrary dl_symbols; + std::map<std::string, void*> preboundFunctions; + + llvm::Function *createDispatcher(llvm::Function *f, llvm::Instruction *i); + bool runProtectedCall(llvm::Function *f, uint64_t *args); + + public: + ExternalDispatcher(); + ~ExternalDispatcher(); + + /* Call the given function using the parameter passing convention of + * ci with arguments in args[1], args[2], ... and writing the result + * into args[0]. + */ + bool executeCall(llvm::Function *function, llvm::Instruction *i, uint64_t *args); + void *resolveSymbol(const std::string &name); + }; +} + +#endif diff --git a/lib/Core/ImpliedValue.cpp b/lib/Core/ImpliedValue.cpp new file mode 100644 index 00000000..386c8d80 --- /dev/null +++ b/lib/Core/ImpliedValue.cpp @@ -0,0 +1,274 @@ +//===-- ImpliedValue.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ImpliedValue.h" + +#include "klee/Constraints.h" +#include "klee/Expr.h" +#include "klee/Solver.h" +// FIXME: Use APInt. +#include "klee/Internal/Support/IntEvaluation.h" + +#include "klee/util/ExprUtil.h" + +#include <iostream> +#include <map> +#include <set> + +using namespace klee; + +// XXX we really want to do some sort of canonicalization of exprs +// globally so that cases below become simpler +static void _getImpliedValue(ref<Expr> e, + uint64_t value, + ImpliedValueList &results) { + switch (e.getKind()) { + + case Expr::Constant: { + assert(value == e.getConstantValue() && "error in implied value calculation"); + break; + } + + // Special + + case Expr::NotOptimized: break; + + case Expr::Read: { + // XXX in theory it is possible to descend into a symbolic index + // under certain circumstances (all values known, known value + // unique, or range known, max / min hit). Seems unlikely this + // would work often enough to be worth the effort. + ReadExpr *re = static_ref_cast<ReadExpr>(e); + results.push_back(std::make_pair(re, + ConstantExpr::create(value, e.getWidth()))); + break; + } + + case Expr::Select: { + // not much to do, could improve with range analysis + SelectExpr *se = static_ref_cast<SelectExpr>(e); + + if (se->trueExpr.isConstant()) { + if (se->falseExpr.isConstant()) { + if (se->trueExpr.getConstantValue() != se->falseExpr.getConstantValue()) { + if (value == se->trueExpr.getConstantValue()) { + _getImpliedValue(se->cond, 1, results); + } else { + assert(value == se->falseExpr.getConstantValue() && + "err in implied value calculation"); + _getImpliedValue(se->cond, 0, results); + } + } + } + } + break; + } + + case Expr::Concat: { + ConcatExpr *ce = static_ref_cast<ConcatExpr>(e); + _getImpliedValue(ce->getKid(0), (value >> ce->getKid(1).getWidth()) & ((1 << ce->getKid(0).getWidth()) - 1), results); + _getImpliedValue(ce->getKid(1), value & ((1 << ce->getKid(1).getWidth()) - 1), results); + break; + } + + case Expr::Extract: { + // XXX, could do more here with "some bits" mask + break; + } + + // Casting + + case Expr::ZExt: + case Expr::SExt: { + CastExpr *ce = static_ref_cast<CastExpr>(e); + _getImpliedValue(ce->src, + bits64::truncateToNBits(value, + ce->src.getWidth()), + results); + break; + } + + // Arithmetic + + case Expr::Add: { // constants on left + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (be->left.isConstant()) { + uint64_t nvalue = ints::sub(value, + be->left.getConstantValue(), + be->left.getWidth()); + _getImpliedValue(be->right, nvalue, results); + } + break; + } + case Expr::Sub: { // constants on left + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (be->left.isConstant()) { + uint64_t nvalue = ints::sub(be->left.getConstantValue(), + value, + be->left.getWidth()); + _getImpliedValue(be->right, nvalue, results); + } + break; + } + case Expr::Mul: { + // XXX can do stuff here, but need valid mask and other things + // because of bits that might be lost + break; + } + + case Expr::UDiv: + case Expr::SDiv: + case Expr::URem: + case Expr::SRem: + // no, no, no + break; + + // Binary + + case Expr::And: { + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (be->getWidth() == Expr::Bool) { + if (value) { + _getImpliedValue(be->left, value, results); + _getImpliedValue(be->right, value, results); + } + } else { + // XXX, we can basically propogate a mask here + // where we know "some bits". may or may not be + // useful. + } + break; + } + case Expr::Or: { + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (!value) { + _getImpliedValue(be->left, 0, results); + _getImpliedValue(be->right, 0, results); + } else { + // XXX, can do more? + } + break; + } + case Expr::Xor: { // constants on left + BinaryExpr *be = static_ref_cast<BinaryExpr>(e); + if (be->left.isConstant()) { + _getImpliedValue(be->right, value ^ be->left.getConstantValue(), results); + } + break; + } + + // Comparison + case Expr::Ne: + value = !value; + /* fallthru */ + case Expr::Eq: { + EqExpr *ee = static_ref_cast<EqExpr>(e); + if (value) { + if (ee->left.isConstant()) + _getImpliedValue(ee->right, ee->left.getConstantValue(), results); + } else { + // look for limited value range, woohoo + // + // in general anytime one side was restricted to two values we + // can apply this trick. the only obvious case where this + // occurs, aside from booleans, is as the result of a select + // expression where the true and false branches are single + // valued and distinct. + + if (ee->left.isConstant()) { + if (ee->left.getWidth() == Expr::Bool) { + _getImpliedValue(ee->right, !ee->left.getConstantValue(), results); + } + } + } + break; + } + + default: + break; + } +} + +void ImpliedValue::getImpliedValues(ref<Expr> e, + ref<Expr> value, + ImpliedValueList &results) { + assert(value.isConstant() && "non-constant in place of constant"); + _getImpliedValue(e, value.getConstantValue(), results); +} + +void ImpliedValue::checkForImpliedValues(Solver *S, ref<Expr> e, + ref<Expr> value) { + assert(value.isConstant() && "non-constant in place of constant"); + + std::vector<ref<ReadExpr> > reads; + std::map<ref<ReadExpr>, ref<Expr> > found; + ImpliedValueList results; + + getImpliedValues(e, value, results); + + for (ImpliedValueList::iterator i = results.begin(), ie = results.end(); + i != ie; ++i) { + std::map<ref<ReadExpr>, ref<Expr> >::iterator it = found.find(i->first); + if (it != found.end()) { + assert(it->second.getConstantValue() == i->second.getConstantValue() && + "I don't think so Scott"); + } else { + found.insert(std::make_pair(i->first, i->second)); + } + } + + findReads(e, false, reads); + std::set< ref<ReadExpr> > readsSet(reads.begin(), reads.end()); + reads = std::vector< ref<ReadExpr> >(readsSet.begin(), readsSet.end()); + + std::vector<ref<Expr> > assumption; + assumption.push_back(EqExpr::create(e, value)); + + // obscure... we need to make sure that all the read indices are + // bounds checked. if we don't do this we can end up constructing + // invalid counterexamples because STP will happily make out of + // bounds indices which will not get picked up. this is of utmost + // importance if we are being backed by the CexCachingSolver. + + for (std::vector< ref<ReadExpr> >::iterator i = reads.begin(), + ie = reads.end(); i != ie; ++i) { + ReadExpr *re = i->get(); + ref<Expr> size = ref<Expr>(re->updates.root->size, kMachinePointerType); + assumption.push_back(UltExpr::create(re->index, size)); + } + + ConstraintManager assume(assumption); + for (std::vector< ref<ReadExpr> >::iterator i = reads.begin(), + ie = reads.end(); i != ie; ++i) { + ref<ReadExpr> var = *i; + ref<Expr> possible; + bool success = S->getValue(Query(assume, var), possible); + assert(success && "FIXME: Unhandled solver failure"); + std::map<ref<ReadExpr>, ref<Expr> >::iterator it = found.find(var); + bool res; + success = S->mustBeTrue(Query(assume, EqExpr::create(var, possible)), res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + if (it != found.end()) { + assert(possible.getConstantValue() == it->second.getConstantValue()); + found.erase(it); + } + } else { + if (it!=found.end()) { + ref<Expr> binding = it->second; + llvm::cerr << "checkForImpliedValues: " << e << " = " << value << "\n" + << "\t\t implies " << var << " == " << binding + << " (error)\n"; + assert(0); + } + } + } + + assert(found.empty()); +} diff --git a/lib/Core/ImpliedValue.h b/lib/Core/ImpliedValue.h new file mode 100644 index 00000000..51ec6e9b --- /dev/null +++ b/lib/Core/ImpliedValue.h @@ -0,0 +1,38 @@ +//===-- ImpliedValue.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_IMPLIEDVALUE_H +#define KLEE_IMPLIEDVALUE_H + +#include "klee/Expr.h" + +#include <vector> + +// The idea of implied values is that often we know the result of some +// expression e is a concrete value C. In many cases this directly +// implies that some variable x embedded in e is also a concrete value +// (derived from C). This module is used for finding such variables +// and their computed values. + +namespace klee { + class ConstantExpr; + class Expr; + class ReadExpr; + class Solver; + + typedef std::vector< std::pair<ref<ReadExpr>, ref<Expr> > > ImpliedValueList; + + namespace ImpliedValue { + void getImpliedValues(ref<Expr> e, ref<Expr> cvalue, ImpliedValueList &result); + void checkForImpliedValues(Solver *S, ref<Expr> e, ref<Expr> cvalue); + } + +} + +#endif diff --git a/lib/Core/Makefile b/lib/Core/Makefile new file mode 100755 index 00000000..4da3c7ea --- /dev/null +++ b/lib/Core/Makefile @@ -0,0 +1,16 @@ +#===-- lib/Core/Makefile -----------------------------------*- Makefile -*--===# +# +# The KLEE Symbolic Virtual Machine +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# + +LEVEL=../.. + +LIBRARYNAME=kleeCore +DONT_BUILD_RELINKED=1 +BUILD_ARCHIVE=1 + +include $(LEVEL)/Makefile.common diff --git a/lib/Core/Memory.cpp b/lib/Core/Memory.cpp new file mode 100644 index 00000000..cd563551 --- /dev/null +++ b/lib/Core/Memory.cpp @@ -0,0 +1,812 @@ +//===-- Memory.cpp --------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "Memory.h" + +#include "klee/Expr.h" +#include "klee/Machine.h" +#include "klee/Solver.h" +#include "klee/util/BitArray.h" + +#include "ObjectHolder.h" + +#include <llvm/Function.h> +#include <llvm/Instruction.h> +#include <llvm/Value.h> + +#include <iostream> +#include <cassert> +#include <sstream> + +using namespace llvm; +using namespace klee; + +/***/ + +ObjectHolder::ObjectHolder(const ObjectHolder &b) : os(b.os) { + if (os) ++os->refCount; +} + +ObjectHolder::ObjectHolder(ObjectState *_os) : os(_os) { + if (os) ++os->refCount; +} + +ObjectHolder::~ObjectHolder() { + if (os && --os->refCount==0) delete os; +} + +ObjectHolder &ObjectHolder::operator=(const ObjectHolder &b) { + if (b.os) ++b.os->refCount; + if (os && --os->refCount==0) delete os; + os = b.os; + return *this; +} + +/***/ + +int MemoryObject::counter = 0; + +extern "C" void vc_DeleteExpr(void*); + +MemoryObject::~MemoryObject() { + // FIXME: This shouldn't be necessary. Array's should be ref-counted + // just like everything else, and the interaction with the STP array + // should hide at least inside the Expr/Solver layers. + if (array) { + if (array->stpInitialArray) { + ::vc_DeleteExpr(array->stpInitialArray); + array->stpInitialArray = 0; + } + delete array; + } +} + +void MemoryObject::getAllocInfo(std::string &result) const { + std::ostringstream info; + + info << "MO" << id << "[" << size << "]"; + + if (allocSite) { + info << " allocated at "; + if (const Instruction *i = dyn_cast<Instruction>(allocSite)) { + info << i->getParent()->getParent()->getName() << "():"; + info << *i; + } else if (const GlobalValue *gv = dyn_cast<GlobalValue>(allocSite)) { + info << "global:" << gv->getName(); + } else { + info << "value:" << *allocSite; + } + } else { + info << " (no allocation info)"; + } + + result = info.str(); +} + +/***/ + +ObjectState::ObjectState(const MemoryObject *mo, unsigned _size) + : copyOnWriteOwner(0), + refCount(0), + object(mo), + concreteStore(new uint8_t[_size]), + concreteMask(0), + flushMask(0), + knownSymbolics(0), + size(_size), + updates(mo->array, false, 0), + readOnly(false) { +} + +ObjectState::ObjectState(const ObjectState &os) + : copyOnWriteOwner(0), + refCount(0), + object(os.object), + concreteStore(new uint8_t[os.size]), + concreteMask(os.concreteMask ? new BitArray(*os.concreteMask, os.size) : 0), + flushMask(os.flushMask ? new BitArray(*os.flushMask, os.size) : 0), + knownSymbolics(0), + size(os.size), + updates(os.updates), + readOnly(false) { + assert(!os.readOnly && "no need to copy read only object?"); + + if (os.knownSymbolics) { + knownSymbolics = new ref<Expr>[size]; + for (unsigned i=0; i<size; i++) + knownSymbolics[i] = os.knownSymbolics[i]; + } + + memcpy(concreteStore, os.concreteStore, size*sizeof(*concreteStore)); +} + +ObjectState::~ObjectState() { + if (concreteMask) delete concreteMask; + if (flushMask) delete flushMask; + if (knownSymbolics) delete[] knownSymbolics; + delete[] concreteStore; +} + +/***/ + +void ObjectState::makeConcrete() { + if (concreteMask) delete concreteMask; + if (flushMask) delete flushMask; + if (knownSymbolics) delete[] knownSymbolics; + concreteMask = 0; + flushMask = 0; + knownSymbolics = 0; +} + +void ObjectState::makeSymbolic() { + assert(!updates.head && + "XXX makeSymbolic of objects with symbolic values is unsupported"); + updates.isRooted = true; + + // XXX simplify this, can just delete various arrays I guess + for (unsigned i=0; i<size; i++) { + markByteSymbolic(i); + setKnownSymbolic(i, 0); + markByteFlushed(i); + } +} + +void ObjectState::initializeToZero() { + makeConcrete(); + memset(concreteStore, 0, size); +} + +void ObjectState::initializeToRandom() { + makeConcrete(); + for (unsigned i=0; i<size; i++) { + // randomly selected by 256 sided die + concreteStore[i] = 0xAB; + } +} + +/* +Cache Invariants +-- +isByteKnownSymbolic(i) => !isByteConcrete(i) +isByteConcrete(i) => !isByteKnownSymbolic(i) +!isByteFlushed(i) => (isByteConcrete(i) || isByteKnownSymbolic(i)) + */ + +void ObjectState::fastRangeCheckOffset(ref<Expr> offset, + unsigned *base_r, + unsigned *size_r) const { + *base_r = 0; + *size_r = size; +} + +void ObjectState::flushRangeForRead(unsigned rangeBase, + unsigned rangeSize) const { + if (!flushMask) flushMask = new BitArray(size, true); + + for (unsigned offset=rangeBase; offset<rangeBase+rangeSize; offset++) { + if (!isByteFlushed(offset)) { + if (isByteConcrete(offset)) { + updates.extend(ConstantExpr::create(offset, kMachinePointerType), + ConstantExpr::create(concreteStore[offset], Expr::Int8)); + } else { + assert(isByteKnownSymbolic(offset) && "invalid bit set in flushMask"); + updates.extend(ConstantExpr::create(offset, kMachinePointerType), + knownSymbolics[offset]); + } + + flushMask->unset(offset); + } + } +} + +void ObjectState::flushRangeForWrite(unsigned rangeBase, + unsigned rangeSize) { + if (!flushMask) flushMask = new BitArray(size, true); + + for (unsigned offset=rangeBase; offset<rangeBase+rangeSize; offset++) { + if (!isByteFlushed(offset)) { + if (isByteConcrete(offset)) { + updates.extend(ConstantExpr::create(offset, kMachinePointerType), + ConstantExpr::create(concreteStore[offset], Expr::Int8)); + markByteSymbolic(offset); + } else { + assert(isByteKnownSymbolic(offset) && "invalid bit set in flushMask"); + updates.extend(ConstantExpr::create(offset, kMachinePointerType), + knownSymbolics[offset]); + setKnownSymbolic(offset, 0); + } + + flushMask->unset(offset); + } else { + // flushed bytes that are written over still need + // to be marked out + if (isByteConcrete(offset)) { + markByteSymbolic(offset); + } else if (isByteKnownSymbolic(offset)) { + setKnownSymbolic(offset, 0); + } + } + } +} + +bool ObjectState::isByteConcrete(unsigned offset) const { + return !concreteMask || concreteMask->get(offset); +} + +bool ObjectState::isByteFlushed(unsigned offset) const { + return flushMask && !flushMask->get(offset); +} + +bool ObjectState::isByteKnownSymbolic(unsigned offset) const { + return knownSymbolics && knownSymbolics[offset].get(); +} + +void ObjectState::markByteConcrete(unsigned offset) { + if (concreteMask) + concreteMask->set(offset); +} + +void ObjectState::markByteSymbolic(unsigned offset) { + if (!concreteMask) + concreteMask = new BitArray(size, true); + concreteMask->unset(offset); +} + +void ObjectState::markByteUnflushed(unsigned offset) { + if (flushMask) + flushMask->set(offset); +} + +void ObjectState::markByteFlushed(unsigned offset) { + if (!flushMask) { + flushMask = new BitArray(size, false); + } else { + flushMask->unset(offset); + } +} + +void ObjectState::setKnownSymbolic(unsigned offset, + Expr *value /* can be null */) { + if (knownSymbolics) { + knownSymbolics[offset] = value; + } else { + if (value) { + knownSymbolics = new ref<Expr>[size]; + knownSymbolics[offset] = value; + } + } +} + +/***/ + +ref<Expr> ObjectState::read8(unsigned offset) const { + if (isByteConcrete(offset)) { + return ConstantExpr::create(concreteStore[offset], Expr::Int8); + } else if (isByteKnownSymbolic(offset)) { + return knownSymbolics[offset]; + } else { + assert(isByteFlushed(offset) && "unflushed byte without cache value"); + + return ReadExpr::create(updates, + ConstantExpr::create(offset, kMachinePointerType)); + } +} + +ref<Expr> ObjectState::read8(ref<Expr> offset) const { + assert(!offset.isConstant() && "constant offset passed to symbolic read8"); + unsigned base, size; + fastRangeCheckOffset(offset, &base, &size); + flushRangeForRead(base, size); + + if (size>4096) { + std::string allocInfo; + object->getAllocInfo(allocInfo); + klee_warning_once(0, "flushing %d bytes on read, may be slow and/or crash: %s", + size, + allocInfo.c_str()); + } + + return ReadExpr::create(updates, offset); +} + +void ObjectState::write8(unsigned offset, uint8_t value) { + //assert(read_only == false && "writing to read-only object!"); + concreteStore[offset] = value; + setKnownSymbolic(offset, 0); + + markByteConcrete(offset); + markByteUnflushed(offset); +} + +void ObjectState::write8(unsigned offset, ref<Expr> value) { + // can happen when ExtractExpr special cases + if (value.isConstant()) { + write8(offset, (uint8_t) value.getConstantValue()); + } else { + setKnownSymbolic(offset, value.get()); + + markByteSymbolic(offset); + markByteUnflushed(offset); + } +} + +void ObjectState::write8(ref<Expr> offset, ref<Expr> value) { + assert(!offset.isConstant() && "constant offset passed to symbolic write8"); + unsigned base, size; + fastRangeCheckOffset(offset, &base, &size); + flushRangeForWrite(base, size); + + if (size>4096) { + std::string allocInfo; + object->getAllocInfo(allocInfo); + klee_warning_once(0, "flushing %d bytes on read, may be slow and/or crash: %s", + size, + allocInfo.c_str()); + } + + updates.extend(offset, value); +} + +/***/ + +ref<Expr> ObjectState::read(ref<Expr> offset, Expr::Width width) const { + if (offset.isConstant()) { + return read((unsigned) offset.getConstantValue(), width); + } else { + switch (width) { + case Expr::Bool: return read1(offset); + case Expr::Int8: return read8(offset); + case Expr::Int16: return read16(offset); + case Expr::Int32: return read32(offset); + case Expr::Int64: return read64(offset); + default: assert(0 && "invalid type"); + } + } +} + +ref<Expr> ObjectState::read(unsigned offset, Expr::Width width) const { + switch (width) { + case Expr::Bool: return read1(offset); + case Expr::Int8: return read8(offset); + case Expr::Int16: return read16(offset); + case Expr::Int32: return read32(offset); + case Expr::Int64: return read64(offset); + default: assert(0 && "invalid type"); + } +} + +ref<Expr> ObjectState::read1(unsigned offset) const { + return ExtractExpr::createByteOff(read8(offset), 0, Expr::Bool); +} + +ref<Expr> ObjectState::read1(ref<Expr> offset) const { + return ExtractExpr::createByteOff(read8(offset), 0, Expr::Bool); +} + +ref<Expr> ObjectState::read16(unsigned offset) const { + if (kMachineByteOrder == machine::MSB) { + return ConcatExpr::create(read8(offset+0), + read8(offset+1)); + } else { + return ConcatExpr::create(read8(offset+1), + read8(offset+0)); + } +} + +ref<Expr> ObjectState::read16(ref<Expr> offset) const { + if (kMachineByteOrder == machine::MSB) { + return ConcatExpr::create + (read8(AddExpr::create(offset, + ConstantExpr::create(0, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(1, + kMachinePointerType)))); + } else { + return ConcatExpr::create + (read8(AddExpr::create(offset, + ConstantExpr::create(1, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(0, + kMachinePointerType)))); + } +} + +ref<Expr> ObjectState::read32(unsigned offset) const { + if (kMachineByteOrder == machine::MSB) { + return ConcatExpr::create4(read8(offset+0), + read8(offset+1), + read8(offset+2), + read8(offset+3)); + } else { + return ConcatExpr::create4(read8(offset+3), + read8(offset+2), + read8(offset+1), + read8(offset+0)); + } +} + +ref<Expr> ObjectState::read32(ref<Expr> offset) const { + if (kMachineByteOrder == machine::MSB) { + return ConcatExpr::create4 + (read8(AddExpr::create(offset, + ConstantExpr::create(0, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(1, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(2, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(3, + kMachinePointerType)))); + } else { + return ConcatExpr::create4 + (read8(AddExpr::create(offset, + ConstantExpr::create(3, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(2, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(1, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(0, + kMachinePointerType)))); + } +} + +ref<Expr> ObjectState::read64(unsigned offset) const { + if (kMachineByteOrder == machine::MSB) { + return ConcatExpr::create8(read8(offset+0), + read8(offset+1), + read8(offset+2), + read8(offset+3), + read8(offset+4), + read8(offset+5), + read8(offset+6), + read8(offset+7)); + } else { + return ConcatExpr::create8(read8(offset+7), + read8(offset+6), + read8(offset+5), + read8(offset+4), + read8(offset+3), + read8(offset+2), + read8(offset+1), + read8(offset+0)); + } +} + +ref<Expr> ObjectState::read64(ref<Expr> offset) const { + if (kMachineByteOrder == machine::MSB) { + return ConcatExpr::create8 + (read8(AddExpr::create(offset, + ConstantExpr::create(0, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(1, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(2, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(3, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(4, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(5, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(6, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(7, + kMachinePointerType)))); + } else { + return ConcatExpr::create8 + (read8(AddExpr::create(offset, + ConstantExpr::create(7, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(6, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(5, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(4, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(3, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(2, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(1, + kMachinePointerType))), + read8(AddExpr::create(offset, + ConstantExpr::create(0, + kMachinePointerType)))); + } +} + +void ObjectState::write(ref<Expr> offset, ref<Expr> value) { + Expr::Width w = value.getWidth(); + if (offset.isConstant()) { + write(offset.getConstantValue(), value); + } else { + switch(w) { + case Expr::Bool: write1(offset, value); break; + case Expr::Int8: write8(offset, value); break; + case Expr::Int16: write16(offset, value); break; + case Expr::Int32: write32(offset, value); break; + case Expr::Int64: write64(offset, value); break; + default: assert(0 && "invalid number of bytes in write"); + } + } +} + +void ObjectState::write(unsigned offset, ref<Expr> value) { + Expr::Width w = value.getWidth(); + if (value.isConstant()) { + uint64_t val = value.getConstantValue(); + switch(w) { + case Expr::Bool: + case Expr::Int8: write8(offset, val); break; + case Expr::Int16: write16(offset, val); break; + case Expr::Int32: write32(offset, val); break; + case Expr::Int64: write64(offset, val); break; + default: assert(0 && "invalid number of bytes in write"); + } + } else { + switch(w) { + case Expr::Bool: write1(offset, value); break; + case Expr::Int8: write8(offset, value); break; + case Expr::Int16: write16(offset, value); break; + case Expr::Int32: write32(offset, value); break; + case Expr::Int64: write64(offset, value); break; + default: assert(0 && "invalid number of bytes in write"); + } + } +} + +void ObjectState::write1(unsigned offset, ref<Expr> value) { + write8(offset, ZExtExpr::create(value, Expr::Int8)); +} + +void ObjectState::write1(ref<Expr> offset, ref<Expr> value) { + write8(offset, ZExtExpr::create(value, Expr::Int8)); +} + +void ObjectState::write16(unsigned offset, uint16_t value) { + if (kMachineByteOrder == machine::MSB) { + write8(offset+0, (uint8_t) (value >> 8)); + write8(offset+1, (uint8_t) (value >> 0)); + } else { + write8(offset+1, (uint8_t) (value >> 8)); + write8(offset+0, (uint8_t) (value >> 0)); + } +} + +void ObjectState::write16(unsigned offset, ref<Expr> value) { + if (kMachineByteOrder == machine::MSB) { + write8(offset+0, ExtractExpr::createByteOff(value, 1)); + write8(offset+1, ExtractExpr::createByteOff(value, 0)); + } else { + write8(offset+1, ExtractExpr::createByteOff(value, 1)); + write8(offset+0, ExtractExpr::createByteOff(value, 0)); + } +} + + +void ObjectState::write16(ref<Expr> offset, ref<Expr> value) { + if (kMachineByteOrder == machine::MSB) { + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,1)); + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,0)); + } else { + write8(AddExpr::create(offset, + ConstantExpr::create(1, kMachinePointerType)), + ExtractExpr::createByteOff(value,1)); + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,0)); + } +} + +void ObjectState::write32(unsigned offset, uint32_t value) { + if (kMachineByteOrder == machine::MSB) { + write8(offset+0, (uint8_t) (value >> 24)); + write8(offset+1, (uint8_t) (value >> 16)); + write8(offset+2, (uint8_t) (value >> 8)); + write8(offset+3, (uint8_t) (value >> 0)); + } else { + write8(offset+3, (uint8_t) (value >> 24)); + write8(offset+2, (uint8_t) (value >> 16)); + write8(offset+1, (uint8_t) (value >> 8)); + write8(offset+0, (uint8_t) (value >> 0)); + } +} + +void ObjectState::write32(unsigned offset, ref<Expr> value) { + if (kMachineByteOrder == machine::MSB) { + write8(offset+0, ExtractExpr::createByteOff(value, 3)); + write8(offset+1, ExtractExpr::createByteOff(value, 2)); + write8(offset+2, ExtractExpr::createByteOff(value, 1)); + write8(offset+3, ExtractExpr::createByteOff(value, 0)); + } else { + write8(offset+3, ExtractExpr::createByteOff(value, 3)); + write8(offset+2, ExtractExpr::createByteOff(value, 2)); + write8(offset+1, ExtractExpr::createByteOff(value, 1)); + write8(offset+0, ExtractExpr::createByteOff(value, 0)); + } +} + +void ObjectState::write32(ref<Expr> offset, ref<Expr> value) { + if (kMachineByteOrder == machine::MSB) { + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,3)); + write8(AddExpr::create(offset, + ConstantExpr::create(1, kMachinePointerType)), + ExtractExpr::createByteOff(value,2)); + write8(AddExpr::create(offset, + ConstantExpr::create(2, kMachinePointerType)), + ExtractExpr::createByteOff(value,1)); + write8(AddExpr::create(offset, + ConstantExpr::create(3, kMachinePointerType)), + ExtractExpr::createByteOff(value,0)); + } else { + write8(AddExpr::create(offset, + ConstantExpr::create(3, kMachinePointerType)), + ExtractExpr::createByteOff(value,3)); + write8(AddExpr::create(offset, + ConstantExpr::create(2, kMachinePointerType)), + ExtractExpr::createByteOff(value,2)); + write8(AddExpr::create(offset, + ConstantExpr::create(1, kMachinePointerType)), + ExtractExpr::createByteOff(value,1)); + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,0)); + } +} + +void ObjectState::write64(unsigned offset, uint64_t value) { + if (kMachineByteOrder == machine::MSB) { + write8(offset+0, (uint8_t) (value >> 56)); + write8(offset+1, (uint8_t) (value >> 48)); + write8(offset+2, (uint8_t) (value >> 40)); + write8(offset+3, (uint8_t) (value >> 32)); + write8(offset+4, (uint8_t) (value >> 24)); + write8(offset+5, (uint8_t) (value >> 16)); + write8(offset+6, (uint8_t) (value >> 8)); + write8(offset+7, (uint8_t) (value >> 0)); + } else { + write8(offset+7, (uint8_t) (value >> 56)); + write8(offset+6, (uint8_t) (value >> 48)); + write8(offset+5, (uint8_t) (value >> 40)); + write8(offset+4, (uint8_t) (value >> 32)); + write8(offset+3, (uint8_t) (value >> 24)); + write8(offset+2, (uint8_t) (value >> 16)); + write8(offset+1, (uint8_t) (value >> 8)); + write8(offset+0, (uint8_t) (value >> 0)); + } +} + +void ObjectState::write64(unsigned offset, ref<Expr> value) { + if (kMachineByteOrder == machine::MSB) { + write8(offset+0, ExtractExpr::createByteOff(value, 7)); + write8(offset+1, ExtractExpr::createByteOff(value, 6)); + write8(offset+2, ExtractExpr::createByteOff(value, 5)); + write8(offset+3, ExtractExpr::createByteOff(value, 4)); + write8(offset+4, ExtractExpr::createByteOff(value, 3)); + write8(offset+5, ExtractExpr::createByteOff(value, 2)); + write8(offset+6, ExtractExpr::createByteOff(value, 1)); + write8(offset+7, ExtractExpr::createByteOff(value, 0)); + } else { + write8(offset+7, ExtractExpr::createByteOff(value, 7)); + write8(offset+6, ExtractExpr::createByteOff(value, 6)); + write8(offset+5, ExtractExpr::createByteOff(value, 5)); + write8(offset+4, ExtractExpr::createByteOff(value, 4)); + write8(offset+3, ExtractExpr::createByteOff(value, 3)); + write8(offset+2, ExtractExpr::createByteOff(value, 2)); + write8(offset+1, ExtractExpr::createByteOff(value, 1)); + write8(offset+0, ExtractExpr::createByteOff(value, 0)); + } +} + +void ObjectState::write64(ref<Expr> offset, ref<Expr> value) { + if (kMachineByteOrder == machine::MSB) { + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,7)); + write8(AddExpr::create(offset, + ConstantExpr::create(1, kMachinePointerType)), + ExtractExpr::createByteOff(value,6)); + write8(AddExpr::create(offset, + ConstantExpr::create(2, kMachinePointerType)), + ExtractExpr::createByteOff(value,5)); + write8(AddExpr::create(offset, + ConstantExpr::create(3, kMachinePointerType)), + ExtractExpr::createByteOff(value,4)); + write8(AddExpr::create(offset, + ConstantExpr::create(4, kMachinePointerType)), + ExtractExpr::createByteOff(value,3)); + write8(AddExpr::create(offset, + ConstantExpr::create(5, kMachinePointerType)), + ExtractExpr::createByteOff(value,2)); + write8(AddExpr::create(offset, + ConstantExpr::create(6, kMachinePointerType)), + ExtractExpr::createByteOff(value,1)); + write8(AddExpr::create(offset, + ConstantExpr::create(7, kMachinePointerType)), + ExtractExpr::createByteOff(value,0)); + } else { + write8(AddExpr::create(offset, + ConstantExpr::create(7, kMachinePointerType)), + ExtractExpr::createByteOff(value,7)); + write8(AddExpr::create(offset, + ConstantExpr::create(6, kMachinePointerType)), + ExtractExpr::createByteOff(value,6)); + write8(AddExpr::create(offset, + ConstantExpr::create(5, kMachinePointerType)), + ExtractExpr::createByteOff(value,5)); + write8(AddExpr::create(offset, + ConstantExpr::create(4, kMachinePointerType)), + ExtractExpr::createByteOff(value,4)); + write8(AddExpr::create(offset, + ConstantExpr::create(3, kMachinePointerType)), + ExtractExpr::createByteOff(value,3)); + write8(AddExpr::create(offset, + ConstantExpr::create(2, kMachinePointerType)), + ExtractExpr::createByteOff(value,2)); + write8(AddExpr::create(offset, + ConstantExpr::create(1, kMachinePointerType)), + ExtractExpr::createByteOff(value,1)); + write8(AddExpr::create(offset, + ConstantExpr::create(0, kMachinePointerType)), + ExtractExpr::createByteOff(value,0)); + } +} + +void ObjectState::print() { + llvm::cerr << "-- ObjectState --\n"; + llvm::cerr << "\tMemoryObject ID: " << object->id << "\n"; + llvm::cerr << "\tRoot Object: " << updates.root << "\n"; + llvm::cerr << "\tIs Rooted? " << updates.isRooted << "\n"; + llvm::cerr << "\tSize: " << size << "\n"; + + llvm::cerr << "\tBytes:\n"; + for (unsigned i=0; i<size; i++) { + llvm::cerr << "\t\t["<<i<<"]" + << " concrete? " << isByteConcrete(i) + << " known-sym? " << isByteKnownSymbolic(i) + << " flushed? " << isByteFlushed(i) << " = "; + ref<Expr> e = read8(i); + llvm::cerr << e << "\n"; + } + + llvm::cerr << "\tUpdates:\n"; + for (const UpdateNode *un=updates.head; un; un=un->next) { + llvm::cerr << "\t\t[" << un->index << "] = " << un->value << "\n"; + } +} diff --git a/lib/Core/Memory.h b/lib/Core/Memory.h new file mode 100644 index 00000000..0f09b162 --- /dev/null +++ b/lib/Core/Memory.h @@ -0,0 +1,239 @@ +//===-- Memory.h ------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_MEMORY_H +#define KLEE_MEMORY_H + +#include "klee/Expr.h" + +#include <vector> +#include <string> + +namespace llvm { + class Value; +} + +namespace klee { + +class BitArray; +class MemoryManager; +class Solver; + +class MemoryObject { + friend class STPBuilder; + +private: + static int counter; + +public: + unsigned id; + uint64_t address; + Array *array; + + /// size in bytes + unsigned size; + std::string name; + + bool isLocal; + bool isGlobal; + bool isFixed; + + /// true if created by us. + bool fake_object; + bool isUserSpecified; + + /// "Location" for which this memory object was allocated. This + /// should be either the allocating instruction or the global object + /// it was allocated for (or whatever else makes sense). + const llvm::Value *allocSite; + + /// A list of boolean expressions the user has requested be true of + /// a counterexample. Mutable since we play a little fast and loose + /// with allowing it to be added to during execution (although + /// should sensibly be only at creation time). + mutable std::vector< ref<Expr> > cexPreferences; + + // DO NOT IMPLEMENT + MemoryObject(const MemoryObject &b); + MemoryObject &operator=(const MemoryObject &b); + +public: + // XXX this is just a temp hack, should be removed + explicit + MemoryObject(uint64_t _address) + : id(counter++), + address(_address), + array(new Array(this, 0, id)), + size(0), + isFixed(true), + allocSite(0) { + } + + MemoryObject(uint64_t _address, unsigned _size, + bool _isLocal, bool _isGlobal, bool _isFixed, + const llvm::Value *_allocSite) + : id(counter++), + address(_address), + array(new Array(this, id, _size)), + size(_size), + name("unnamed"), + isLocal(_isLocal), + isGlobal(_isGlobal), + isFixed(_isFixed), + fake_object(false), + isUserSpecified(false), + allocSite(_allocSite) { + } + + ~MemoryObject(); + + /// Get an identifying string for this allocation. + void getAllocInfo(std::string &result) const; + + void setName(std::string name) { + this->name = name; + } + + ref<Expr> getBaseExpr() const { + return ConstantExpr::create(address, kMachinePointerType); + } + ref<Expr> getSizeExpr() const { + return ConstantExpr::create(size, kMachinePointerType); + } + ref<Expr> getOffsetExpr(ref<Expr> pointer) const { + return SubExpr::create(pointer, getBaseExpr()); + } + ref<Expr> getBoundsCheckPointer(ref<Expr> pointer) const { + return getBoundsCheckOffset(getOffsetExpr(pointer)); + } + ref<Expr> getBoundsCheckPointer(ref<Expr> pointer, unsigned bytes) const { + return getBoundsCheckOffset(getOffsetExpr(pointer), bytes); + } + + ref<Expr> getBoundsCheckOffset(ref<Expr> offset) const { + if (size==0) { + return EqExpr::create(offset, ref<Expr>(0, kMachinePointerType)); + } else { + return UltExpr::create(offset, getSizeExpr()); + } + } + ref<Expr> getBoundsCheckOffset(ref<Expr> offset, unsigned bytes) const { + if (bytes<=size) { + return UltExpr::create(offset, + ref<Expr>(size - bytes + 1, kMachinePointerType)); + } else { + return ref<Expr>(0, Expr::Bool); + } + } +}; + +class ObjectState { +private: + friend class AddressSpace; + unsigned copyOnWriteOwner; // exclusively for AddressSpace + + friend class ObjectHolder; + unsigned refCount; + + const MemoryObject *object; + + uint8_t *concreteStore; + // XXX cleanup name of flushMask (its backwards or something) + BitArray *concreteMask; + + // mutable because may need flushed during read of const + mutable BitArray *flushMask; + + ref<Expr> *knownSymbolics; + +public: + unsigned size; + + // mutable because we may need flush during read of const + mutable UpdateList updates; + + bool readOnly; + +public: + // initial contents are undefined but concrete, it is the creators + // responsibility to initialize the object contents appropriate + ObjectState(const MemoryObject *mo, unsigned size); + ObjectState(const ObjectState &os); + ~ObjectState(); + + const MemoryObject *getObject() const { return object; } + + void setReadOnly(bool ro) { readOnly = ro; } + + // make all bytes are concrete with undefined values + void makeConcrete(); + + void makeSymbolic(); + + // make contents all concrete and zero + void initializeToZero(); + // make contents all concrete and random + void initializeToRandom(); + + ref<Expr> read(ref<Expr> offset, Expr::Width width) const; + ref<Expr> read(unsigned offset, Expr::Width width) const; + ref<Expr> read1(unsigned offset) const; + ref<Expr> read8(unsigned offset) const; + ref<Expr> read16(unsigned offset) const; + ref<Expr> read32(unsigned offset) const; + ref<Expr> read64(unsigned offset) const; + + // return bytes written. + void write(unsigned offset, ref<Expr> value); + void write(ref<Expr> offset, ref<Expr> value); + + void write8(unsigned offset, uint8_t value); + void write16(unsigned offset, uint16_t value); + void write32(unsigned offset, uint32_t value); + void write64(unsigned offset, uint64_t value); + +private: + ref<Expr> read1(ref<Expr> offset) const; + ref<Expr> read8(ref<Expr> offset) const; + ref<Expr> read16(ref<Expr> offset) const; + ref<Expr> read32(ref<Expr> offset) const; + ref<Expr> read64(ref<Expr> offset) const; + + void write1(unsigned offset, ref<Expr> value); + void write1(ref<Expr> offset, ref<Expr> value); + void write8(unsigned offset, ref<Expr> value); + void write8(ref<Expr> offset, ref<Expr> value); + void write16(unsigned offset, ref<Expr> value); + void write16(ref<Expr> offset, ref<Expr> value); + void write32(unsigned offset, ref<Expr> value); + void write32(ref<Expr> offset, ref<Expr> value); + void write64(unsigned offset, ref<Expr> value); + void write64(ref<Expr> offset, ref<Expr> value); + + + void fastRangeCheckOffset(ref<Expr> offset, unsigned *base_r, unsigned *size_r) const; + void flushRangeForRead(unsigned rangeBase, unsigned rangeSize) const; + void flushRangeForWrite(unsigned rangeBase, unsigned rangeSize); + + bool isByteConcrete(unsigned offset) const; + bool isByteFlushed(unsigned offset) const; + bool isByteKnownSymbolic(unsigned offset) const; + + void markByteConcrete(unsigned offset); + void markByteSymbolic(unsigned offset); + void markByteFlushed(unsigned offset); + void markByteUnflushed(unsigned offset); + void setKnownSymbolic(unsigned offset, Expr *value); + + void print(); +}; + +} // End klee namespace + +#endif diff --git a/lib/Core/MemoryManager.cpp b/lib/Core/MemoryManager.cpp new file mode 100644 index 00000000..cec7b7d1 --- /dev/null +++ b/lib/Core/MemoryManager.cpp @@ -0,0 +1,69 @@ +//===-- MemoryManager.cpp -------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "CoreStats.h" +#include "Memory.h" +#include "MemoryManager.h" + +#include "klee/ExecutionState.h" +#include "klee/Expr.h" +#include "klee/Solver.h" + +#include "llvm/Support/CommandLine.h" + +using namespace klee; + +/***/ + +MemoryManager::~MemoryManager() { + while (!objects.empty()) { + MemoryObject *mo = objects.back(); + objects.pop_back(); + delete mo; + } +} + +MemoryObject *MemoryManager::allocate(uint64_t size, bool isLocal, bool isGlobal, + const llvm::Value *allocSite) { + if (size>10*1024*1024) { + klee_warning_once(0, "failing large alloc: %u bytes", (unsigned) size); + return 0; + } + uint64_t address = (uint64_t) (unsigned long) malloc((unsigned) size); + if (!address) + return 0; + + ++stats::allocations; + MemoryObject *res = new MemoryObject(address, size, isLocal, isGlobal, false, + allocSite); + objects.push_back(res); + return res; +} + +MemoryObject *MemoryManager::allocateFixed(uint64_t address, uint64_t size, + const llvm::Value *allocSite) { + for (objects_ty::iterator it = objects.begin(), ie = objects.end(); + it != ie; ++it) { + MemoryObject *mo = *it; + assert(!(address+size > mo->address && address < mo->address+mo->size) && + "allocated an overlapping object"); + } + + ++stats::allocations; + MemoryObject *res = new MemoryObject(address, size, false, true, true, + allocSite); + objects.push_back(res); + return res; +} + +void MemoryManager::deallocate(const MemoryObject *mo) { + assert(0); +} diff --git a/lib/Core/MemoryManager.h b/lib/Core/MemoryManager.h new file mode 100644 index 00000000..adb2ba22 --- /dev/null +++ b/lib/Core/MemoryManager.h @@ -0,0 +1,41 @@ +//===-- MemoryManager.h -----------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_MEMORYMANAGER_H +#define KLEE_MEMORYMANAGER_H + +#include <vector> +#include <stdint.h> + +namespace llvm { + class Value; +} + +namespace klee { + class MemoryObject; + + class MemoryManager { + private: + typedef std::vector<MemoryObject*> objects_ty; + objects_ty objects; + + public: + MemoryManager() {} + ~MemoryManager(); + + MemoryObject *allocate(uint64_t size, bool isLocal, bool isGlobal, + const llvm::Value *allocSite); + MemoryObject *allocateFixed(uint64_t address, uint64_t size, + const llvm::Value *allocSite); + void deallocate(const MemoryObject *mo); + }; + +} // End klee namespace + +#endif diff --git a/lib/Core/ObjectHolder.h b/lib/Core/ObjectHolder.h new file mode 100644 index 00000000..abf2c6f0 --- /dev/null +++ b/lib/Core/ObjectHolder.h @@ -0,0 +1,33 @@ +//===-- ObjectHolder.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_OBJECTHOLDER_H +#define KLEE_OBJECTHOLDER_H + +namespace klee { + class ObjectState; + + class ObjectHolder { + ObjectState *os; + + public: + ObjectHolder() : os(0) {} + ObjectHolder(ObjectState *_os); + ObjectHolder(const ObjectHolder &b); + ~ObjectHolder(); + + ObjectHolder &operator=(const ObjectHolder &b); + + operator class ObjectState *() { return os; } + operator class ObjectState *() const { return (ObjectState*) os; } + }; +} + +#endif + diff --git a/lib/Core/PTree.cpp b/lib/Core/PTree.cpp new file mode 100644 index 00000000..349761cd --- /dev/null +++ b/lib/Core/PTree.cpp @@ -0,0 +1,103 @@ +//===-- PTree.cpp ---------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PTree.h" + +#include <klee/Expr.h> +#include <klee/util/ExprPPrinter.h> + +#include <vector> +#include <iostream> + +using namespace klee; + + /* *** */ + +PTree::PTree(const data_type &_root) : root(new Node(0,_root)) { +} + +PTree::~PTree() {} + +std::pair<PTreeNode*, PTreeNode*> +PTree::split(Node *n, + const data_type &leftData, + const data_type &rightData) { + assert(n && !n->left && !n->right); + n->left = new Node(n, leftData); + n->right = new Node(n, rightData); + return std::make_pair(n->left, n->right); +} + +void PTree::remove(Node *n) { + assert(!n->left && !n->right); + do { + Node *p = n->parent; + delete n; + if (p) { + if (n == p->left) { + p->left = 0; + } else { + assert(n == p->right); + p->right = 0; + } + } + n = p; + } while (n && !n->left && !n->right); +} + +void PTree::dump(std::ostream &os) { + ExprPPrinter *pp = ExprPPrinter::create(os); + pp->setNewline("\\l"); + os << "digraph G {\n"; + os << "\tsize=\"10,7.5\";\n"; + os << "\tratio=fill;\n"; + os << "\trotate=90;\n"; + os << "\tcenter = \"true\";\n"; + os << "\tnode [style=\"filled\",width=.1,height=.1,fontname=\"Terminus\"]\n"; + os << "\tedge [arrowsize=.3]\n"; + std::vector<PTree::Node*> stack; + stack.push_back(root); + while (!stack.empty()) { + PTree::Node *n = stack.back(); + stack.pop_back(); + if (n->condition.isNull()) { + os << "\tn" << n << " [label=\"\""; + } else { + os << "\tn" << n << " [label=\""; + pp->print(n->condition); + os << "\",shape=diamond"; + } + if (n->data) + os << ",fillcolor=green"; + os << "];\n"; + if (n->left) { + os << "\tn" << n << " -> n" << n->left << ";\n"; + stack.push_back(n->left); + } + if (n->right) { + os << "\tn" << n << " -> n" << n->right << ";\n"; + stack.push_back(n->right); + } + } + os << "}\n"; + delete pp; +} + +PTreeNode::PTreeNode(PTreeNode *_parent, + ExecutionState *_data) + : parent(_parent), + left(0), + right(0), + data(_data), + condition(0) { +} + +PTreeNode::~PTreeNode() { +} + diff --git a/lib/Core/PTree.h b/lib/Core/PTree.h new file mode 100644 index 00000000..6accc8e2 --- /dev/null +++ b/lib/Core/PTree.h @@ -0,0 +1,53 @@ +//===-- PTree.h -------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef __UTIL_PTREE_H__ +#define __UTIL_PTREE_H__ + +#include <klee/Expr.h> + +#include <utility> +#include <cassert> +#include <iostream> + +namespace klee { + class ExecutionState; + + class PTree { + typedef ExecutionState* data_type; + + public: + typedef class PTreeNode Node; + Node *root; + + PTree(const data_type &_root); + ~PTree(); + + std::pair<Node*,Node*> split(Node *n, + const data_type &leftData, + const data_type &rightData); + void remove(Node *n); + + void dump(std::ostream &os); + }; + + class PTreeNode { + friend class PTree; + public: + PTreeNode *parent, *left, *right; + ExecutionState *data; + ref<Expr> condition; + + private: + PTreeNode(PTreeNode *_parent, ExecutionState *_data); + ~PTreeNode(); + }; +} + +#endif diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp new file mode 100644 index 00000000..4c94c59b --- /dev/null +++ b/lib/Core/Searcher.cpp @@ -0,0 +1,575 @@ +//===-- Searcher.cpp ------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "Searcher.h" + +#include "CoreStats.h" +#include "Executor.h" +#include "PTree.h" +#include "StatsTracker.h" + +#include "klee/ExecutionState.h" +#include "klee/Statistics.h" +#include "klee/Internal/Module/InstructionInfoTable.h" +#include "klee/Internal/Module/KInstruction.h" +#include "klee/Internal/Module/KModule.h" +#include "klee/Internal/ADT/DiscretePDF.h" +#include "klee/Internal/ADT/RNG.h" +#include "klee/Internal/Support/ModuleUtil.h" +#include "klee/Internal/System/Time.h" + +#include "llvm/Constants.h" +#include "llvm/Instructions.h" +#include "llvm/Module.h" +#include "llvm/Support/CallSite.h" +#include "llvm/Support/CFG.h" +#include "llvm/Support/CommandLine.h" + +#include <cassert> +#include <fstream> +#include <climits> + +using namespace klee; +using namespace llvm; + +namespace { + cl::opt<bool> + DebugLogMerge("debug-log-merge"); +} + +namespace klee { + extern RNG theRNG; +} + +Searcher::~Searcher() { +} + +/// + +ExecutionState &DFSSearcher::selectState() { + return *states.back(); +} + +void DFSSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + states.insert(states.end(), + addedStates.begin(), + addedStates.end()); + for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(), + ie = removedStates.end(); it != ie; ++it) { + ExecutionState *es = *it; + if (es == states.back()) { + states.pop_back(); + } else { + bool ok = false; + + for (std::vector<ExecutionState*>::iterator it = states.begin(), + ie = states.end(); it != ie; ++it) { + if (es==*it) { + states.erase(it); + ok = true; + break; + } + } + + assert(ok && "invalid state removed"); + } + } +} + +/// + +ExecutionState &RandomSearcher::selectState() { + return *states[theRNG.getInt32()%states.size()]; +} + +void RandomSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + states.insert(states.end(), + addedStates.begin(), + addedStates.end()); + for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(), + ie = removedStates.end(); it != ie; ++it) { + ExecutionState *es = *it; + bool ok = false; + + for (std::vector<ExecutionState*>::iterator it = states.begin(), + ie = states.end(); it != ie; ++it) { + if (es==*it) { + states.erase(it); + ok = true; + break; + } + } + + assert(ok && "invalid state removed"); + } +} + +/// + +WeightedRandomSearcher::WeightedRandomSearcher(Executor &_executor, + WeightType _type) + : executor(_executor), + states(new DiscretePDF<ExecutionState*>()), + type(_type) { + switch(type) { + case Depth: + updateWeights = false; + break; + case InstCount: + case CPInstCount: + case QueryCost: + case MinDistToUncovered: + case CoveringNew: + updateWeights = true; + break; + default: + assert(0 && "invalid weight type"); + } +} + +WeightedRandomSearcher::~WeightedRandomSearcher() { + delete states; +} + +ExecutionState &WeightedRandomSearcher::selectState() { + return *states->choose(theRNG.getDoubleL()); +} + +double WeightedRandomSearcher::getWeight(ExecutionState *es) { + switch(type) { + default: + case Depth: + return es->weight; + case InstCount: { + uint64_t count = theStatisticManager->getIndexedValue(stats::instructions, + es->pc->info->id); + double inv = 1. / std::max((uint64_t) 1, count); + return inv * inv; + } + case CPInstCount: { + StackFrame &sf = es->stack.back(); + uint64_t count = sf.callPathNode->statistics.getValue(stats::instructions); + double inv = 1. / std::max((uint64_t) 1, count); + return inv; + } + case QueryCost: + return (es->queryCost < .1) ? 1. : 1./es->queryCost; + case CoveringNew: + case MinDistToUncovered: { + uint64_t md2u = computeMinDistToUncovered(es->pc, + es->stack.back().minDistToUncoveredOnReturn); + + double invMD2U = 1. / (md2u ? md2u : 10000); + if (type==CoveringNew) { + double invCovNew = 0.; + if (es->instsSinceCovNew) + invCovNew = 1. / std::max(1, (int) es->instsSinceCovNew - 1000); + return (invCovNew * invCovNew + invMD2U * invMD2U); + } else { + return invMD2U * invMD2U; + } + } + } +} + +void WeightedRandomSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + if (current && updateWeights && !removedStates.count(current)) + states->update(current, getWeight(current)); + + for (std::set<ExecutionState*>::const_iterator it = addedStates.begin(), + ie = addedStates.end(); it != ie; ++it) { + ExecutionState *es = *it; + states->insert(es, getWeight(es)); + } + + for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(), + ie = removedStates.end(); it != ie; ++it) { + states->remove(*it); + } +} + +bool WeightedRandomSearcher::empty() { + return states->empty(); +} + +/// + +RandomPathSearcher::RandomPathSearcher(Executor &_executor) + : executor(_executor) { +} + +RandomPathSearcher::~RandomPathSearcher() { +} + +ExecutionState &RandomPathSearcher::selectState() { + unsigned flips=0, bits=0; + PTree::Node *n = executor.processTree->root; + + while (!n->data) { + if (!n->left) { + n = n->right; + } else if (!n->right) { + n = n->left; + } else { + if (bits==0) { + flips = theRNG.getInt32(); + bits = 32; + } + --bits; + n = (flips&(1<<bits)) ? n->left : n->right; + } + } + + return *n->data; +} + +void RandomPathSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { +} + +bool RandomPathSearcher::empty() { + return executor.states.empty(); +} + +/// + +BumpMergingSearcher::BumpMergingSearcher(Executor &_executor, Searcher *_baseSearcher) + : executor(_executor), + baseSearcher(_baseSearcher), + mergeFunction(executor.kmodule->kleeMergeFn) { +} + +BumpMergingSearcher::~BumpMergingSearcher() { + delete baseSearcher; +} + +/// + +Instruction *BumpMergingSearcher::getMergePoint(ExecutionState &es) { + if (mergeFunction) { + Instruction *i = es.pc->inst; + + if (i->getOpcode()==Instruction::Call) { + CallSite cs(cast<CallInst>(i)); + if (mergeFunction==cs.getCalledFunction()) + return i; + } + } + + return 0; +} + +ExecutionState &BumpMergingSearcher::selectState() { +entry: + // out of base states, pick one to pop + if (baseSearcher->empty()) { + std::map<llvm::Instruction*, ExecutionState*>::iterator it = + statesAtMerge.begin(); + ExecutionState *es = it->second; + statesAtMerge.erase(it); + ++es->pc; + + baseSearcher->addState(es); + } + + ExecutionState &es = baseSearcher->selectState(); + + if (Instruction *mp = getMergePoint(es)) { + std::map<llvm::Instruction*, ExecutionState*>::iterator it = + statesAtMerge.find(mp); + + baseSearcher->removeState(&es); + + if (it==statesAtMerge.end()) { + statesAtMerge.insert(std::make_pair(mp, &es)); + } else { + ExecutionState *mergeWith = it->second; + if (mergeWith->merge(es)) { + // hack, because we are terminating the state we need to let + // the baseSearcher know about it again + baseSearcher->addState(&es); + executor.terminateState(es); + } else { + it->second = &es; // the bump + ++mergeWith->pc; + + baseSearcher->addState(mergeWith); + } + } + + goto entry; + } else { + return es; + } +} + +void BumpMergingSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + baseSearcher->update(current, addedStates, removedStates); +} + +/// + +MergingSearcher::MergingSearcher(Executor &_executor, Searcher *_baseSearcher) + : executor(_executor), + baseSearcher(_baseSearcher), + mergeFunction(executor.kmodule->kleeMergeFn) { +} + +MergingSearcher::~MergingSearcher() { + delete baseSearcher; +} + +/// + +Instruction *MergingSearcher::getMergePoint(ExecutionState &es) { + if (mergeFunction) { + Instruction *i = es.pc->inst; + + if (i->getOpcode()==Instruction::Call) { + CallSite cs(cast<CallInst>(i)); + if (mergeFunction==cs.getCalledFunction()) + return i; + } + } + + return 0; +} + +ExecutionState &MergingSearcher::selectState() { + while (!baseSearcher->empty()) { + ExecutionState &es = baseSearcher->selectState(); + if (getMergePoint(es)) { + baseSearcher->removeState(&es, &es); + statesAtMerge.insert(&es); + } else { + return es; + } + } + + // build map of merge point -> state list + std::map<Instruction*, std::vector<ExecutionState*> > merges; + for (std::set<ExecutionState*>::const_iterator it = statesAtMerge.begin(), + ie = statesAtMerge.end(); it != ie; ++it) { + ExecutionState &state = **it; + Instruction *mp = getMergePoint(state); + + merges[mp].push_back(&state); + } + + if (DebugLogMerge) + llvm::cerr << "-- all at merge --\n"; + for (std::map<Instruction*, std::vector<ExecutionState*> >::iterator + it = merges.begin(), ie = merges.end(); it != ie; ++it) { + if (DebugLogMerge) { + llvm::cerr << "\tmerge: " << it->first << " ["; + for (std::vector<ExecutionState*>::iterator it2 = it->second.begin(), + ie2 = it->second.end(); it2 != ie2; ++it2) { + ExecutionState *state = *it2; + llvm::cerr << state << ", "; + } + llvm::cerr << "]\n"; + } + + // merge states + std::set<ExecutionState*> toMerge(it->second.begin(), it->second.end()); + while (!toMerge.empty()) { + ExecutionState *base = *toMerge.begin(); + toMerge.erase(toMerge.begin()); + + std::set<ExecutionState*> toErase; + for (std::set<ExecutionState*>::iterator it = toMerge.begin(), + ie = toMerge.end(); it != ie; ++it) { + ExecutionState *mergeWith = *it; + + if (base->merge(*mergeWith)) { + toErase.insert(mergeWith); + } + } + if (DebugLogMerge && !toErase.empty()) { + llvm::cerr << "\t\tmerged: " << base << " with ["; + for (std::set<ExecutionState*>::iterator it = toErase.begin(), + ie = toErase.end(); it != ie; ++it) { + if (it!=toErase.begin()) llvm::cerr << ", "; + llvm::cerr << *it; + } + llvm::cerr << "]\n"; + } + for (std::set<ExecutionState*>::iterator it = toErase.begin(), + ie = toErase.end(); it != ie; ++it) { + std::set<ExecutionState*>::iterator it2 = toMerge.find(*it); + assert(it2!=toMerge.end()); + executor.terminateState(**it); + toMerge.erase(it2); + } + + // step past merge and toss base back in pool + statesAtMerge.erase(statesAtMerge.find(base)); + ++base->pc; + baseSearcher->addState(base); + } + } + + if (DebugLogMerge) + llvm::cerr << "-- merge complete, continuing --\n"; + + return selectState(); +} + +void MergingSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + if (!removedStates.empty()) { + std::set<ExecutionState *> alt = removedStates; + for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(), + ie = removedStates.end(); it != ie; ++it) { + ExecutionState *es = *it; + std::set<ExecutionState*>::const_iterator it = statesAtMerge.find(es); + if (it!=statesAtMerge.end()) { + statesAtMerge.erase(it); + alt.erase(alt.find(es)); + } + } + baseSearcher->update(current, addedStates, alt); + } else { + baseSearcher->update(current, addedStates, removedStates); + } +} + +/// + +BatchingSearcher::BatchingSearcher(Searcher *_baseSearcher, + double _timeBudget, + unsigned _instructionBudget) + : baseSearcher(_baseSearcher), + timeBudget(_timeBudget), + instructionBudget(_instructionBudget), + lastState(0) { + +} + +BatchingSearcher::~BatchingSearcher() { + delete baseSearcher; +} + +ExecutionState &BatchingSearcher::selectState() { + if (!lastState || + (util::getWallTime()-lastStartTime)>timeBudget || + (stats::instructions-lastStartInstructions)>instructionBudget) { + if (lastState) { + double delta = util::getWallTime()-lastStartTime; + if (delta>timeBudget*1.1) { + llvm::cerr << "KLEE: increased time budget from " << timeBudget << " to " << delta << "\n"; + timeBudget = delta; + } + } + lastState = &baseSearcher->selectState(); + lastStartTime = util::getWallTime(); + lastStartInstructions = stats::instructions; + return *lastState; + } else { + return *lastState; + } +} + +void BatchingSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + if (removedStates.count(lastState)) + lastState = 0; + baseSearcher->update(current, addedStates, removedStates); +} + +/***/ + +IterativeDeepeningTimeSearcher::IterativeDeepeningTimeSearcher(Searcher *_baseSearcher) + : baseSearcher(_baseSearcher), + time(1.) { +} + +IterativeDeepeningTimeSearcher::~IterativeDeepeningTimeSearcher() { + delete baseSearcher; +} + +ExecutionState &IterativeDeepeningTimeSearcher::selectState() { + ExecutionState &res = baseSearcher->selectState(); + startTime = util::getWallTime(); + return res; +} + +void IterativeDeepeningTimeSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + double elapsed = util::getWallTime() - startTime; + + if (!removedStates.empty()) { + std::set<ExecutionState *> alt = removedStates; + for (std::set<ExecutionState*>::const_iterator it = removedStates.begin(), + ie = removedStates.end(); it != ie; ++it) { + ExecutionState *es = *it; + std::set<ExecutionState*>::const_iterator it = pausedStates.find(es); + if (it!=pausedStates.end()) { + pausedStates.erase(it); + alt.erase(alt.find(es)); + } + } + baseSearcher->update(current, addedStates, alt); + } else { + baseSearcher->update(current, addedStates, removedStates); + } + + if (current && !removedStates.count(current) && elapsed>time) { + pausedStates.insert(current); + baseSearcher->removeState(current); + } + + if (baseSearcher->empty()) { + time *= 2; + llvm::cerr << "KLEE: increasing time budget to: " << time << "\n"; + baseSearcher->update(0, pausedStates, std::set<ExecutionState*>()); + pausedStates.clear(); + } +} + +/***/ + +InterleavedSearcher::InterleavedSearcher(const std::vector<Searcher*> &_searchers) + : searchers(_searchers), + index(1) { +} + +InterleavedSearcher::~InterleavedSearcher() { + for (std::vector<Searcher*>::const_iterator it = searchers.begin(), + ie = searchers.end(); it != ie; ++it) + delete *it; +} + +ExecutionState &InterleavedSearcher::selectState() { + Searcher *s = searchers[--index]; + if (index==0) index = searchers.size(); + return s->selectState(); +} + +void InterleavedSearcher::update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) { + for (std::vector<Searcher*>::const_iterator it = searchers.begin(), + ie = searchers.end(); it != ie; ++it) + (*it)->update(current, addedStates, removedStates); +} diff --git a/lib/Core/Searcher.h b/lib/Core/Searcher.h new file mode 100644 index 00000000..455a7679 --- /dev/null +++ b/lib/Core/Searcher.h @@ -0,0 +1,279 @@ +//===-- Searcher.h ----------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_SEARCHER_H +#define KLEE_SEARCHER_H + +#include <vector> +#include <set> +#include <map> +#include <queue> + +// FIXME: Move out of header, use llvm streams. +#include <ostream> + +namespace llvm { + class BasicBlock; + class Function; + class Instruction; +} + +namespace klee { + template<class T> class DiscretePDF; + class ExecutionState; + class Executor; + + class Searcher { + public: + virtual ~Searcher(); + + virtual ExecutionState &selectState() = 0; + + virtual void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates) = 0; + + virtual bool empty() = 0; + + // prints name of searcher as a klee_message() + // TODO: could probably make prettier or more flexible + virtual void printName(std::ostream &os) { + os << "<unnamed searcher>\n"; + } + + // pgbovine - to be called when a searcher gets activated and + // deactivated, say, by a higher-level searcher; most searchers + // don't need this functionality, so don't have to override. + virtual void activate() {}; + virtual void deactivate() {}; + + // utility functions + + void addState(ExecutionState *es, ExecutionState *current = 0) { + std::set<ExecutionState*> tmp; + tmp.insert(es); + update(current, tmp, std::set<ExecutionState*>()); + } + + void removeState(ExecutionState *es, ExecutionState *current = 0) { + std::set<ExecutionState*> tmp; + tmp.insert(es); + update(current, std::set<ExecutionState*>(), tmp); + } + }; + + class DFSSearcher : public Searcher { + std::vector<ExecutionState*> states; + + public: + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return states.empty(); } + void printName(std::ostream &os) { + os << "DFSSearcher\n"; + } + }; + + class RandomSearcher : public Searcher { + std::vector<ExecutionState*> states; + + public: + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return states.empty(); } + void printName(std::ostream &os) { + os << "RandomSearcher\n"; + } + }; + + class WeightedRandomSearcher : public Searcher { + public: + enum WeightType { + Depth, + QueryCost, + InstCount, + CPInstCount, + MinDistToUncovered, + CoveringNew + }; + + private: + Executor &executor; + DiscretePDF<ExecutionState*> *states; + WeightType type; + bool updateWeights; + + double getWeight(ExecutionState*); + + public: + WeightedRandomSearcher(Executor &executor, WeightType type); + ~WeightedRandomSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty(); + void printName(std::ostream &os) { + os << "WeightedRandomSearcher::"; + switch(type) { + case Depth : os << "Depth\n"; return; + case QueryCost : os << "QueryCost\n"; return; + case InstCount : os << "InstCount\n"; return; + case CPInstCount : os << "CPInstCount\n"; return; + case MinDistToUncovered : os << "MinDistToUncovered\n"; return; + case CoveringNew : os << "CoveringNew\n"; return; + default : os << "<unknown type>\n"; return; + } + } + }; + + class RandomPathSearcher : public Searcher { + Executor &executor; + + public: + RandomPathSearcher(Executor &_executor); + ~RandomPathSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty(); + void printName(std::ostream &os) { + os << "RandomPathSearcher\n"; + } + }; + + class MergingSearcher : public Searcher { + Executor &executor; + std::set<ExecutionState*> statesAtMerge; + Searcher *baseSearcher; + llvm::Function *mergeFunction; + + private: + llvm::Instruction *getMergePoint(ExecutionState &es); + + public: + MergingSearcher(Executor &executor, Searcher *baseSearcher); + ~MergingSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return baseSearcher->empty() && statesAtMerge.empty(); } + void printName(std::ostream &os) { + os << "MergingSearcher\n"; + } + }; + + class BumpMergingSearcher : public Searcher { + Executor &executor; + std::map<llvm::Instruction*, ExecutionState*> statesAtMerge; + Searcher *baseSearcher; + llvm::Function *mergeFunction; + + private: + llvm::Instruction *getMergePoint(ExecutionState &es); + + public: + BumpMergingSearcher(Executor &executor, Searcher *baseSearcher); + ~BumpMergingSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return baseSearcher->empty() && statesAtMerge.empty(); } + void printName(std::ostream &os) { + os << "BumpMergingSearcher\n"; + } + }; + + class BatchingSearcher : public Searcher { + Searcher *baseSearcher; + double timeBudget; + unsigned instructionBudget; + + ExecutionState *lastState; + double lastStartTime; + unsigned lastStartInstructions; + + public: + BatchingSearcher(Searcher *baseSearcher, + double _timeBudget, + unsigned _instructionBudget); + ~BatchingSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return baseSearcher->empty(); } + void printName(std::ostream &os) { + os << "<BatchingSearcher> timeBudget: " << timeBudget + << ", instructionBudget: " << instructionBudget + << ", baseSearcher:\n"; + baseSearcher->printName(os); + os << "</BatchingSearcher>\n"; + } + }; + + class IterativeDeepeningTimeSearcher : public Searcher { + Searcher *baseSearcher; + double time, startTime; + std::set<ExecutionState*> pausedStates; + + public: + IterativeDeepeningTimeSearcher(Searcher *baseSearcher); + ~IterativeDeepeningTimeSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return baseSearcher->empty() && pausedStates.empty(); } + void printName(std::ostream &os) { + os << "IterativeDeepeningTimeSearcher\n"; + } + }; + + class InterleavedSearcher : public Searcher { + typedef std::vector<Searcher*> searchers_ty; + + searchers_ty searchers; + unsigned index; + + public: + explicit InterleavedSearcher(const searchers_ty &_searchers); + ~InterleavedSearcher(); + + ExecutionState &selectState(); + void update(ExecutionState *current, + const std::set<ExecutionState*> &addedStates, + const std::set<ExecutionState*> &removedStates); + bool empty() { return searchers[0]->empty(); } + void printName(std::ostream &os) { + os << "<InterleavedSearcher> containing " + << searchers.size() << " searchers:\n"; + for (searchers_ty::iterator it = searchers.begin(), ie = searchers.end(); + it != ie; ++it) + (*it)->printName(os); + os << "</InterleavedSearcher>\n"; + } + }; + +} + +#endif diff --git a/lib/Core/SeedInfo.cpp b/lib/Core/SeedInfo.cpp new file mode 100644 index 00000000..d76d75dc --- /dev/null +++ b/lib/Core/SeedInfo.cpp @@ -0,0 +1,151 @@ +//===-- SeedInfo.cpp ------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "Memory.h" +#include "SeedInfo.h" +#include "TimingSolver.h" + +#include "klee/ExecutionState.h" +#include "klee/Expr.h" +#include "klee/util/ExprUtil.h" +#include "klee/Internal/ADT/BOut.h" + +using namespace klee; + +BOutObject *SeedInfo::getNextInput(const MemoryObject *mo, + bool byName) { + if (byName) { + unsigned i; + + for (i=0; i<input->numObjects; ++i) { + BOutObject *obj = &input->objects[i]; + if (std::string(obj->name) == mo->name) + if (used.insert(obj).second) + return obj; + } + + // If first unused input matches in size then accept that as + // well. + for (i=0; i<input->numObjects; ++i) + if (!used.count(&input->objects[i])) + break; + if (i<input->numObjects) { + BOutObject *obj = &input->objects[i]; + if (obj->numBytes == mo->size) { + used.insert(obj); + klee_warning_once(mo, "using seed input %s[%d] for: %s (no name match)", + obj->name, obj->numBytes, mo->name.c_str()); + return obj; + } + } + + klee_warning_once(mo, "no seed input for: %s", mo->name.c_str()); + return 0; + } else { + if (inputPosition >= input->numObjects) { + return 0; + } else { + return &input->objects[inputPosition++]; + } + } +} + +void SeedInfo::patchSeed(const ExecutionState &state, + ref<Expr> condition, + TimingSolver *solver) { + std::vector< ref<Expr> > required(state.constraints.begin(), + state.constraints.end()); + ExecutionState tmp(required); + tmp.addConstraint(condition); + + // Try and patch direct reads first, this is likely to resolve the + // problem quickly and avoids long traversal of all seed + // values. There are other smart ways to do this, the nicest is if + // we got a minimal counterexample from STP, in which case we would + // just inject those values back into the seed. + std::set< std::pair<const Array*, unsigned> > directReads; + std::vector< ref<ReadExpr> > reads; + findReads(condition, false, reads); + for (std::vector< ref<ReadExpr> >::iterator it = reads.begin(), + ie = reads.end(); it != ie; ++it) { + ReadExpr *re = it->get(); + if (re->index.isConstant()) { + unsigned index = (unsigned) re->index.getConstantValue(); + directReads.insert(std::make_pair(re->updates.root, index)); + } + } + + for (std::set< std::pair<const Array*, unsigned> >::iterator + it = directReads.begin(), ie = directReads.end(); it != ie; ++it) { + const Array *array = it->first; + unsigned i = it->second; + ref<Expr> read = ReadExpr::create(UpdateList(array, true, 0), + ref<Expr>(i, Expr::Int32)); + + // If not in bindings then this can't be a violation? + Assignment::bindings_ty::iterator it2 = assignment.bindings.find(array); + if (it2 != assignment.bindings.end()) { + ref<Expr> isSeed = EqExpr::create(read, ref<Expr>(it2->second[i], Expr::Int8)); + bool res; + bool success = solver->mustBeFalse(tmp, isSeed, res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + ref<Expr> value; + bool success = solver->getValue(tmp, read, value); + assert(success && "FIXME: Unhandled solver failure"); + it2->second[i] = value.getConstantValue(); + tmp.addConstraint(EqExpr::create(read, ref<Expr>(it2->second[i], Expr::Int8))); + } else { + tmp.addConstraint(isSeed); + } + } + } + + bool res; + bool success = solver->mayBeTrue(state, assignment.evaluate(condition), res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) + return; + + // We could still do a lot better than this, for example by looking at + // independence. But really, this shouldn't be happening often. + for (Assignment::bindings_ty::iterator it = assignment.bindings.begin(), + ie = assignment.bindings.end(); it != ie; ++it) { + const Array *array = it->first; + for (unsigned i=0; i<array->size; ++i) { + ref<Expr> read = ReadExpr::create(UpdateList(array, true, 0), + ref<Expr>(i, Expr::Int32)); + ref<Expr> isSeed = EqExpr::create(read, ref<Expr>(it->second[i], Expr::Int8)); + bool res; + bool success = solver->mustBeFalse(tmp, isSeed, res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + ref<Expr> value; + bool success = solver->getValue(tmp, read, value); + assert(success && "FIXME: Unhandled solver failure"); + it->second[i] = value.getConstantValue(); + tmp.addConstraint(EqExpr::create(read, ref<Expr>(it->second[i], Expr::Int8))); + } else { + tmp.addConstraint(isSeed); + } + } + } + +#ifndef NDEBUG + { + bool res; + bool success = + solver->mayBeTrue(state, assignment.evaluate(condition), res); + assert(success && "FIXME: Unhandled solver failure"); + assert(res && "seed patching failed"); + } +#endif +} diff --git a/lib/Core/SeedInfo.h b/lib/Core/SeedInfo.h new file mode 100644 index 00000000..dd151ed0 --- /dev/null +++ b/lib/Core/SeedInfo.h @@ -0,0 +1,48 @@ +//===-- SeedInfo.h ----------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_SEEDINFO_H +#define KLEE_SEEDINFO_H + +#include "klee/util/Assignment.h" + +extern "C" { + struct BOut; + struct BOutObject; +} + +namespace klee { + class ExecutionState; + class TimingSolver; + + class SeedInfo { + public: + Assignment assignment; + BOut *input; + unsigned inputPosition; + std::set<struct BOutObject*> used; + + public: + explicit + SeedInfo(BOut *_input) : assignment(true), + input(_input), + inputPosition(0) {} + + BOutObject *getNextInput(const MemoryObject *mo, + bool byName); + + /// Patch the seed so that condition is satisfied while retaining as + /// many of the seed values as possible. + void patchSeed(const ExecutionState &state, + ref<Expr> condition, + TimingSolver *solver); + }; +} + +#endif diff --git a/lib/Core/SpecialFunctionHandler.cpp b/lib/Core/SpecialFunctionHandler.cpp new file mode 100644 index 00000000..da2a4a49 --- /dev/null +++ b/lib/Core/SpecialFunctionHandler.cpp @@ -0,0 +1,727 @@ +//===-- SpecialFunctionHandler.cpp ----------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "Memory.h" +#include "SpecialFunctionHandler.h" +#include "TimingSolver.h" + +#include "klee/ExecutionState.h" + +#include "klee/Internal/Module/KInstruction.h" +#include "klee/Internal/Module/KModule.h" + +#include "Executor.h" +#include "MemoryManager.h" + +#include "llvm/Module.h" + +#include <errno.h> + +using namespace llvm; +using namespace klee; + +/// \todo Almost all of the demands in this file should be replaced +/// with terminateState calls. + +/// + +struct HandlerInfo { + const char *name; + SpecialFunctionHandler::Handler handler; + bool doesNotReturn; /// Intrinsic terminates the process + bool hasReturnValue; /// Intrinsic has a return value + bool doNotOverride; /// Intrinsic should not be used if already defined +}; + +// FIXME: We are more or less committed to requiring an intrinsic +// library these days. We can move some of this stuff there, +// especially things like realloc which have complicated semantics +// w.r.t. forking. Among other things this makes delayed query +// dispatch easier to implement. +HandlerInfo handlerInfo[] = { +#define add(name, handler, ret) { name, \ + &SpecialFunctionHandler::handler, \ + false, ret, false } +#define addDNR(name, handler) { name, \ + &SpecialFunctionHandler::handler, \ + true, false, false } + addDNR("__assert_rtn", handleAssertFail), + addDNR("__assert_fail", handleAssertFail), + addDNR("_assert", handleAssert), + addDNR("abort", handleAbort), + addDNR("_exit", handleExit), + { "exit", &SpecialFunctionHandler::handleExit, true, false, true }, + addDNR("klee_abort", handleAbort), + addDNR("klee_silent_exit", handleSilentExit), + addDNR("klee_report_error", handleReportError), + + add("calloc", handleCalloc, true), + add("free", handleFree, false), + add("klee_assume", handleAssume, false), + add("klee_check_memory_access", handleCheckMemoryAccess, false), + add("klee_get_value", handleGetValue, true), + add("klee_define_fixed_object", handleDefineFixedObject, false), + add("klee_get_obj_size", handleGetObjSize, true), + add("klee_get_errno", handleGetErrno, true), + add("klee_is_symbolic", handleIsSymbolic, true), + add("klee_make_symbolic_name", handleMakeSymbolic, false), + add("klee_mark_global", handleMarkGlobal, false), + add("klee_malloc_n", handleMallocN, true), + add("klee_merge", handleMerge, false), + add("klee_prefer_cex", handlePreferCex, false), + add("klee_print_expr", handlePrintExpr, false), + add("klee_print_range", handlePrintRange, false), + add("klee_set_forking", handleSetForking, false), + add("klee_warning", handleWarning, false), + add("klee_warning_once", handleWarningOnce, false), + add("klee_under_constrained", handleUnderConstrained, false), + add("klee_alias_function", handleAliasFunction, false), + add("malloc", handleMalloc, true), + add("realloc", handleRealloc, true), + + // operator delete[](void*) + add("_ZdaPv", handleDeleteArray, false), + // operator delete(void*) + add("_ZdlPv", handleDelete, false), + + // operator new[](unsigned int) + add("_Znaj", handleNewArray, true), + // operator new(unsigned int) + add("_Znwj", handleNew, true), + + // FIXME-64: This is wrong for 64-bit long... + + // operator new[](unsigned long) + add("_Znam", handleNewArray, true), + // operator new(unsigned long) + add("_Znwm", handleNew, true), + +#undef addDNR +#undef add +}; + +SpecialFunctionHandler::SpecialFunctionHandler(Executor &_executor) + : executor(_executor) {} + + +void SpecialFunctionHandler::prepare() { + unsigned N = sizeof(handlerInfo)/sizeof(handlerInfo[0]); + + for (unsigned i=0; i<N; ++i) { + HandlerInfo &hi = handlerInfo[i]; + Function *f = executor.kmodule->module->getFunction(hi.name); + + // No need to create if the function doesn't exist, since it cannot + // be called in that case. + + if (f && (!hi.doNotOverride || f->isDeclaration())) { + // Make sure NoReturn attribute is set, for optimization and + // coverage counting. + if (hi.doesNotReturn) + f->addFnAttr(Attribute::NoReturn); + + // Change to a declaration since we handle internally (simplifies + // module and allows deleting dead code). + if (!f->isDeclaration()) + f->deleteBody(); + } + } +} + +void SpecialFunctionHandler::bind() { + unsigned N = sizeof(handlerInfo)/sizeof(handlerInfo[0]); + + for (unsigned i=0; i<N; ++i) { + HandlerInfo &hi = handlerInfo[i]; + Function *f = executor.kmodule->module->getFunction(hi.name); + + if (f && (!hi.doNotOverride || f->isDeclaration())) + handlers[f] = std::make_pair(hi.handler, hi.hasReturnValue); + } +} + + +bool SpecialFunctionHandler::handle(ExecutionState &state, + Function *f, + KInstruction *target, + std::vector< ref<Expr> > &arguments) { + handlers_ty::iterator it = handlers.find(f); + if (it != handlers.end()) { + Handler h = it->second.first; + bool hasReturnValue = it->second.second; + // FIXME: Check this... add test? + if (!hasReturnValue && !target->inst->use_empty()) { + executor.terminateStateOnExecError(state, + "expected return value from void special function"); + } else { + (this->*h)(state, target, arguments); + } + return true; + } else { + return false; + } +} + +/****/ + +// reads a concrete string from memory +std::string SpecialFunctionHandler::readStringAtAddress(ExecutionState &state, + ref<Expr> address) { + ObjectPair op; + address = executor.toUnique(state, address); + assert(address.isConstant() && "symbolic string arg to intrinsic"); + if (!state.addressSpace.resolveOne(address.getConstantValue(), op)) + assert(0 && "XXX out of bounds / multiple resolution unhandled"); + bool res; + assert(executor.solver->mustBeTrue(state, + EqExpr::create(address, + op.first->getBaseExpr()), + res) && + res && + "XXX interior pointer unhandled"); + const MemoryObject *mo = op.first; + const ObjectState *os = op.second; + + char *buf = new char[mo->size]; + + unsigned i; + for (i = 0; i < mo->size - 1; i++) { + ref<Expr> cur = os->read8(i); + cur = executor.toUnique(state, cur); + assert(cur.isConstant() && + "hit symbolic char while reading concrete string"); + buf[i] = cur.getConstantValue(); + } + buf[i] = 0; + + std::string result(buf); + delete[] buf; + return result; +} + +/****/ + +void SpecialFunctionHandler::handleAbort(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==0 && "invalid number of arguments to abort"); + + //XXX:DRE:TAINT + if(state.underConstrained) { + llvm::cerr << "TAINT: skipping abort fail\n"; + executor.terminateState(state); + } else { + executor.terminateStateOnError(state, "abort failure", "abort.err"); + } +} + +void SpecialFunctionHandler::handleExit(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && "invalid number of arguments to exit"); + executor.terminateStateOnExit(state); +} + +void SpecialFunctionHandler::handleSilentExit(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && "invalid number of arguments to exit"); + executor.terminateState(state); +} + +void SpecialFunctionHandler::handleAliasFunction(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==2 && + "invalid number of arguments to klee_alias_function"); + std::string old_fn = readStringAtAddress(state, arguments[0]); + std::string new_fn = readStringAtAddress(state, arguments[1]); + //llvm::cerr << "Replacing " << old_fn << "() with " << new_fn << "()\n"; + if (old_fn == new_fn) + state.removeFnAlias(old_fn); + else state.addFnAlias(old_fn, new_fn); +} + +void SpecialFunctionHandler::handleAssert(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==3 && "invalid number of arguments to _assert"); + + //XXX:DRE:TAINT + if(state.underConstrained) { + llvm::cerr << "TAINT: skipping assertion:" + << readStringAtAddress(state, arguments[0]) << "\n"; + executor.terminateState(state); + } else + executor.terminateStateOnError(state, + "ASSERTION FAIL: " + readStringAtAddress(state, arguments[0]), + "assert.err"); +} + +void SpecialFunctionHandler::handleAssertFail(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==4 && "invalid number of arguments to __assert_fail"); + + //XXX:DRE:TAINT + if(state.underConstrained) { + llvm::cerr << "TAINT: skipping assertion:" + << readStringAtAddress(state, arguments[0]) << "\n"; + executor.terminateState(state); + } else + executor.terminateStateOnError(state, + "ASSERTION FAIL: " + readStringAtAddress(state, arguments[0]), + "assert.err"); +} + +void SpecialFunctionHandler::handleReportError(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==4 && "invalid number of arguments to klee_report_error"); + + // arguments[0], arguments[1] are file, line + + //XXX:DRE:TAINT + if(state.underConstrained) { + llvm::cerr << "TAINT: skipping klee_report_error:" + << readStringAtAddress(state, arguments[2]) << ":" + << readStringAtAddress(state, arguments[3]) << "\n"; + executor.terminateState(state); + } else + executor.terminateStateOnError(state, + readStringAtAddress(state, arguments[2]), + readStringAtAddress(state, arguments[3])); +} + +void SpecialFunctionHandler::handleMerge(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // nop +} + +void SpecialFunctionHandler::handleNew(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && "invalid number of arguments to new"); + + executor.executeAlloc(state, arguments[0], false, target); +} + +void SpecialFunctionHandler::handleDelete(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && "invalid number of arguments to delete"); + executor.executeFree(state, arguments[0]); +} + +void SpecialFunctionHandler::handleNewArray(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && "invalid number of arguments to new[]"); + executor.executeAlloc(state, arguments[0], false, target); +} + +void SpecialFunctionHandler::handleDeleteArray(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && "invalid number of arguments to delete[]"); + executor.executeFree(state, arguments[0]); +} + +void SpecialFunctionHandler::handleMalloc(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && "invalid number of arguments to malloc"); + executor.executeAlloc(state, arguments[0], false, target); +} + +void SpecialFunctionHandler::handleMallocN(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + + // XXX should type check args + assert(arguments.size() == 3 && "invalid number of arguments to malloc"); + + // mallocn(number, size, alignment) + ref<Expr> numElems = executor.toUnique(state, arguments[0]); + ref<Expr> elemSize = executor.toUnique(state, arguments[1]); + ref<Expr> elemAlignment = executor.toUnique(state, arguments[2]); + + assert(numElems.isConstant() && + elemSize.isConstant() && + elemAlignment.isConstant() && + "symbolic arguments passed to klee_mallocn"); + + executor.executeAllocN(state, + numElems.getConstantValue(), + elemSize.getConstantValue(), + elemAlignment.getConstantValue(), + false, + target); +} + +void SpecialFunctionHandler::handleAssume(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && "invalid number of arguments to klee_assume"); + + ref<Expr> e = arguments[0]; + + if(e.getWidth() != Expr::Bool) + e = NeExpr::create(e, ConstantExpr::create(0, e.getWidth())); + + bool res; + bool success = executor.solver->mustBeFalse(state, e, res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + executor.terminateStateOnError(state, + "invalid klee_assume call (provably false)", + "user.err"); + } else { + executor.addConstraint(state, e); + } +} + +void SpecialFunctionHandler::handleIsSymbolic(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && "invalid number of arguments to klee_is_symbolic"); + + executor.bindLocal(target, state, + ConstantExpr::create(!arguments[0].isConstant(), Expr::Int32)); +} + +void SpecialFunctionHandler::handlePreferCex(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==2 && + "invalid number of arguments to klee_prefex_cex"); + + ref<Expr> cond = arguments[1]; + if (cond.getWidth() != Expr::Bool) + cond = NeExpr::create(cond, ref<Expr>(0, cond.getWidth())); + + Executor::ExactResolutionList rl; + executor.resolveExact(state, arguments[0], rl, "prefex_cex"); + + assert(rl.size() == 1 && + "prefer_cex target must resolve to precisely one object"); + + rl[0].first.first->cexPreferences.push_back(cond); +} + +void SpecialFunctionHandler::handlePrintExpr(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==2 && + "invalid number of arguments to klee_print_expr"); + + std::string msg_str = readStringAtAddress(state, arguments[0]); + llvm::cerr << msg_str << ":" << arguments[1] << "\n"; +} + + +void SpecialFunctionHandler::handleUnderConstrained(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && + "invalid number of arguments to klee_under_constrained()."); + assert(arguments[0].isConstant() && + "symbolic argument given to klee_under_constrained!"); + + unsigned v = arguments[0].getConstantValue(); + llvm::cerr << "argument = " << v << " under=" << state.underConstrained << "\n"; + if(v) { + assert(state.underConstrained == false && + "Bogus call to klee_under_constrained()."); + state.underConstrained = v; + llvm::cerr << "turning on under!\n"; + } else { + assert(state.underConstrained != 0 && "Bogus call to klee_taint_end()"); + state.underConstrained = 0; + llvm::cerr << "turning off under!\n"; + } +} + +void SpecialFunctionHandler::handleSetForking(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && + "invalid number of arguments to klee_set_forking"); + ref<Expr> value = executor.toUnique(state, arguments[0]); + + if (!value.isConstant()) { + executor.terminateStateOnError(state, + "klee_set_forking requires a constant arg", + "user.err"); + } else { + state.forkDisabled = !value.getConstantValue(); + } +} + +void SpecialFunctionHandler::handleWarning(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && "invalid number of arguments to klee_warning"); + + std::string msg_str = readStringAtAddress(state, arguments[0]); + klee_warning("%s: %s", state.stack.back().kf->function->getName().c_str(), + msg_str.c_str()); +} + +void SpecialFunctionHandler::handleWarningOnce(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && + "invalid number of arguments to klee_warning_once"); + + std::string msg_str = readStringAtAddress(state, arguments[0]); + klee_warning_once(0, "%s: %s", state.stack.back().kf->function->getName().c_str(), + msg_str.c_str()); +} + +void SpecialFunctionHandler::handlePrintRange(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==2 && + "invalid number of arguments to klee_print_range"); + + std::string msg_str = readStringAtAddress(state, arguments[0]); + llvm::cerr << msg_str << ":" << arguments[1]; + if (!arguments[1].isConstant()) { + // FIXME: Pull into a unique value method? + ref<Expr> value; + bool success = executor.solver->getValue(state, arguments[1], value); + assert(success && "FIXME: Unhandled solver failure"); + bool res; + success = executor.solver->mustBeTrue(state, + EqExpr::create(arguments[1], value), + res); + assert(success && "FIXME: Unhandled solver failure"); + if (res) { + llvm::cerr << " == " << value; + } else { + llvm::cerr << " ~= " << value; + std::pair< ref<Expr>, ref<Expr> > res = + executor.solver->getRange(state, arguments[1]); + llvm::cerr << " (in [" << res.first << ", " << res.second <<"])"; + } + } + llvm::cerr << "\n"; +} + +void SpecialFunctionHandler::handleGetObjSize(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && + "invalid number of arguments to klee_get_obj_size"); + Executor::ExactResolutionList rl; + executor.resolveExact(state, arguments[0], rl, "klee_get_obj_size"); + for (Executor::ExactResolutionList::iterator it = rl.begin(), + ie = rl.end(); it != ie; ++it) { + executor.bindLocal(target, *it->second, + ConstantExpr::create(it->first.first->size, Expr::Int32)); + } +} + +void SpecialFunctionHandler::handleGetErrno(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==0 && + "invalid number of arguments to klee_get_obj_size"); + executor.bindLocal(target, state, + ConstantExpr::create(errno, Expr::Int32)); +} + +void SpecialFunctionHandler::handleCalloc(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==2 && + "invalid number of arguments to calloc"); + + ref<Expr> size = MulExpr::create(arguments[0], + arguments[1]); + executor.executeAlloc(state, size, false, target, true); +} + +void SpecialFunctionHandler::handleRealloc(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==2 && + "invalid number of arguments to realloc"); + ref<Expr> address = arguments[0]; + ref<Expr> size = arguments[1]; + + Executor::StatePair zeroSize = executor.fork(state, + Expr::createIsZero(size), + true); + + if (zeroSize.first) { // size == 0 + executor.executeFree(*zeroSize.first, address, target); + } + if (zeroSize.second) { // size != 0 + Executor::StatePair zeroPointer = executor.fork(*zeroSize.second, + Expr::createIsZero(address), + true); + + if (zeroPointer.first) { // address == 0 + executor.executeAlloc(*zeroPointer.first, size, false, target); + } + if (zeroPointer.second) { // address != 0 + Executor::ExactResolutionList rl; + executor.resolveExact(*zeroPointer.second, address, rl, "realloc"); + + for (Executor::ExactResolutionList::iterator it = rl.begin(), + ie = rl.end(); it != ie; ++it) { + executor.executeAlloc(*it->second, size, false, target, false, + it->first.second); + } + } + } +} + +void SpecialFunctionHandler::handleFree(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + // XXX should type check args + assert(arguments.size()==1 && + "invalid number of arguments to free"); + executor.executeFree(state, arguments[0]); +} + +void SpecialFunctionHandler::handleCheckMemoryAccess(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==2 && + "invalid number of arguments to klee_check_memory_access"); + + ref<Expr> address = executor.toUnique(state, arguments[0]); + ref<Expr> size = executor.toUnique(state, arguments[1]); + if (!address.isConstant() || !size.isConstant()) { + executor.terminateStateOnError(state, + "check_memory_access requires constant args", + "user.err"); + } else { + ObjectPair op; + + if (!state.addressSpace.resolveOne(address.getConstantValue(), op)) { + executor.terminateStateOnError(state, + "check_memory_access: memory error", + "ptr.err", + executor.getAddressInfo(state, address)); + } else { + ref<Expr> chk = op.first->getBoundsCheckPointer(address, + size.getConstantValue()); + assert(chk.isConstant()); + if (!chk.getConstantValue()) { + executor.terminateStateOnError(state, + "check_memory_access: memory error", + "ptr.err", + executor.getAddressInfo(state, address)); + } + } + } +} + +void SpecialFunctionHandler::handleGetValue(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && + "invalid number of arguments to klee_get_value"); + + executor.executeGetValue(state, arguments[0], target); +} + +void SpecialFunctionHandler::handleDefineFixedObject(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==2 && + "invalid number of arguments to klee_define_fixed_object"); + assert(arguments[0].isConstant() && + "expect constant address argument to klee_define_fixed_object"); + assert(arguments[1].isConstant() && + "expect constant size argument to klee_define_fixed_object"); + + uint64_t address = arguments[0].getConstantValue(); + uint64_t size = arguments[1].getConstantValue(); + MemoryObject *mo = executor.memory->allocateFixed(address, size, state.prevPC->inst); + executor.bindObjectInState(state, mo, false); + mo->isUserSpecified = true; // XXX hack; +} + +void SpecialFunctionHandler::handleMakeSymbolic(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==3 && + "invalid number of arguments to klee_make_symbolic[_name]"); + + Executor::ExactResolutionList rl; + executor.resolveExact(state, arguments[0], rl, "make_symbolic"); + + for (Executor::ExactResolutionList::iterator it = rl.begin(), + ie = rl.end(); it != ie; ++it) { + MemoryObject *mo = (MemoryObject*) it->first.first; + std::string name = readStringAtAddress(state, arguments[2]); + mo->setName(name); + + const ObjectState *old = it->first.second; + ExecutionState *s = it->second; + + if (old->readOnly) { + executor.terminateStateOnError(*s, + "cannot make readonly object symbolic", + "user.err"); + return; + } + + bool res; + bool success = + executor.solver->mustBeTrue(*s, EqExpr::create(arguments[1], + mo->getSizeExpr()), + res); + assert(success && "FIXME: Unhandled solver failure"); + + if (res) { + executor.executeMakeSymbolic(*s, mo); + } else { + executor.terminateStateOnError(*s, + "wrong size given to klee_make_symbolic[_name]", + "user.err"); + } + } +} + +void SpecialFunctionHandler::handleMarkGlobal(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > &arguments) { + assert(arguments.size()==1 && + "invalid number of arguments to klee_mark_global"); + + Executor::ExactResolutionList rl; + executor.resolveExact(state, arguments[0], rl, "mark_global"); + + for (Executor::ExactResolutionList::iterator it = rl.begin(), + ie = rl.end(); it != ie; ++it) { + MemoryObject *mo = (MemoryObject*) it->first.first; + assert(!mo->isLocal); + mo->isGlobal = true; + } +} diff --git a/lib/Core/SpecialFunctionHandler.h b/lib/Core/SpecialFunctionHandler.h new file mode 100644 index 00000000..d5d1af93 --- /dev/null +++ b/lib/Core/SpecialFunctionHandler.h @@ -0,0 +1,106 @@ +//===-- SpecialFunctionHandler.h --------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_SPECIALFUNCTIONHANDLER_H +#define KLEE_SPECIALFUNCTIONHANDLER_H + +#include <map> +#include <vector> +#include <string> + +namespace llvm { + class Function; +} + +namespace klee { + class Executor; + class Expr; + class ExecutionState; + class KInstruction; + template<typename T> class ref; + + class SpecialFunctionHandler { + public: + typedef void (SpecialFunctionHandler::*Handler)(ExecutionState &state, + KInstruction *target, + std::vector<ref<Expr> > + &arguments); + typedef std::map<const llvm::Function*, + std::pair<Handler,bool> > handlers_ty; + + handlers_ty handlers; + class Executor &executor; + + public: + SpecialFunctionHandler(Executor &_executor); + + /// Perform any modifications on the LLVM module before it is + /// prepared for execution. At the moment this involves deleting + /// unused function bodies and marking intrinsics with appropriate + /// flags for use in optimizations. + void prepare(); + + /// Initialize the internal handler map after the module has been + /// prepared for execution. + void bind(); + + bool handle(ExecutionState &state, + llvm::Function *f, + KInstruction *target, + std::vector< ref<Expr> > &arguments); + + /* Convenience routines */ + + std::string readStringAtAddress(ExecutionState &state, ref<Expr> address); + + /* Handlers */ + +#define HANDLER(name) void name(ExecutionState &state, \ + KInstruction *target, \ + std::vector< ref<Expr> > &arguments) + HANDLER(handleAbort); + HANDLER(handleAssert); + HANDLER(handleAssertFail); + HANDLER(handleAssume); + HANDLER(handleCalloc); + HANDLER(handleCheckMemoryAccess); + HANDLER(handleDefineFixedObject); + HANDLER(handleDelete); + HANDLER(handleDeleteArray); + HANDLER(handleExit); + HANDLER(handleAliasFunction); + HANDLER(handleFree); + HANDLER(handleGetErrno); + HANDLER(handleGetObjSize); + HANDLER(handleGetValue); + HANDLER(handleIsSymbolic); + HANDLER(handleMakeSymbolic); + HANDLER(handleMalloc); + HANDLER(handleMallocN); + HANDLER(handleMarkGlobal); + HANDLER(handleMerge); + HANDLER(handleNew); + HANDLER(handleNewArray); + HANDLER(handlePreferCex); + HANDLER(handlePrintExpr); + HANDLER(handlePrintRange); + HANDLER(handleRange); + HANDLER(handleRealloc); + HANDLER(handleReportError); + HANDLER(handleRevirtObjects); + HANDLER(handleSetForking); + HANDLER(handleSilentExit); + HANDLER(handleUnderConstrained); + HANDLER(handleWarning); + HANDLER(handleWarningOnce); +#undef HANDLER + }; +} // End klee namespace + +#endif diff --git a/lib/Core/StatsTracker.cpp b/lib/Core/StatsTracker.cpp new file mode 100644 index 00000000..35c073a3 --- /dev/null +++ b/lib/Core/StatsTracker.cpp @@ -0,0 +1,814 @@ +//===-- StatsTracker.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "StatsTracker.h" + +#include "klee/ExecutionState.h" +#include "klee/Statistics.h" +#include "klee/Internal/Module/InstructionInfoTable.h" +#include "klee/Internal/Module/KModule.h" +#include "klee/Internal/Module/KInstruction.h" +#include "klee/Internal/Support/ModuleUtil.h" +#include "klee/Internal/System/Time.h" + +#include "CallPathManager.h" +#include "CoreStats.h" +#include "Executor.h" +#include "MemoryManager.h" +#include "UserSearcher.h" +#include "../Solver/SolverStats.h" + +#include "llvm/BasicBlock.h" +#include "llvm/Function.h" +#include "llvm/Instructions.h" +#include "llvm/IntrinsicInst.h" +#include "llvm/InlineAsm.h" +#include "llvm/Module.h" +#include "llvm/Type.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/CFG.h" +#include "llvm/System/Process.h" +#include "llvm/System/Path.h" + +#include <iostream> +#include <fstream> + +using namespace klee; +using namespace llvm; + +/// + +namespace { + cl::opt<bool> + TrackInstructionTime("track-instruction-time", + cl::desc("Enable tracking of time for individual instructions"), + cl::init(false)); + + cl::opt<bool> + OutputStats("output-stats", + cl::desc("Write running stats trace file"), + cl::init(true)); + + cl::opt<bool> + OutputIStats("output-istats", + cl::desc("Write instruction level statistics (in callgrind format)"), + cl::init(true)); + + cl::opt<double> + StatsWriteInterval("stats-write-interval", + cl::desc("Approximate number of seconds between stats writes (default: 1.0)"), + cl::init(1.)); + + cl::opt<double> + IStatsWriteInterval("istats-write-interval", + cl::desc("Approximate number of seconds between istats writes (default: 10.0)"), + cl::init(10.)); + + /* + cl::opt<double> + BranchCovCountsWriteInterval("branch-cov-counts-write-interval", + cl::desc("Approximate number of seconds between run.branches writes (default: 5.0)"), + cl::init(5.)); + */ + + // XXX I really would like to have dynamic rate control for something like this. + cl::opt<double> + UncoveredUpdateInterval("uncovered-update-interval", + cl::init(30.)); + + cl::opt<bool> + UseCallPaths("use-call-paths", + cl::desc("Enable calltree tracking for instruction level statistics"), + cl::init(true)); + +} + +/// + +bool StatsTracker::useStatistics() { + return OutputStats || OutputIStats; +} + +namespace klee { + class WriteIStatsTimer : public Executor::Timer { + StatsTracker *statsTracker; + + public: + WriteIStatsTimer(StatsTracker *_statsTracker) : statsTracker(_statsTracker) {} + ~WriteIStatsTimer() {} + + void run() { statsTracker->writeIStats(); } + }; + + class WriteStatsTimer : public Executor::Timer { + StatsTracker *statsTracker; + + public: + WriteStatsTimer(StatsTracker *_statsTracker) : statsTracker(_statsTracker) {} + ~WriteStatsTimer() {} + + void run() { statsTracker->writeStatsLine(); } + }; + + class UpdateReachableTimer : public Executor::Timer { + StatsTracker *statsTracker; + + public: + UpdateReachableTimer(StatsTracker *_statsTracker) : statsTracker(_statsTracker) {} + + void run() { statsTracker->computeReachableUncovered(); } + }; + +} + +// + +/// Check for special cases where we statically know an instruction is +/// uncoverable. Currently the case is an unreachable instruction +/// following a noreturn call; the instruction is really only there to +/// satisfy LLVM's termination requirement. +static bool instructionIsCoverable(Instruction *i) { + if (i->getOpcode() == Instruction::Unreachable) { + BasicBlock *bb = i->getParent(); + BasicBlock::iterator it(i); + if (it==bb->begin()) { + return true; + } else { + Instruction *prev = --it; + if (isa<CallInst>(prev) || isa<InvokeInst>(prev)) { + Function *target = getDirectCallTarget(prev); + if (target && target->doesNotReturn()) + return false; + } + } + } + + return true; +} + +StatsTracker::StatsTracker(Executor &_executor, std::string _objectFilename, + bool _updateMinDistToUncovered) + : executor(_executor), + objectFilename(_objectFilename), + statsFile(0), + istatsFile(0), + startWallTime(util::getWallTime()), + numBranches(0), + fullBranches(0), + partialBranches(0), + updateMinDistToUncovered(_updateMinDistToUncovered) { + KModule *km = executor.kmodule; + + sys::Path module(objectFilename); + if (!sys::Path(objectFilename).isAbsolute()) { + sys::Path current = sys::Path::GetCurrentDirectory(); + current.appendComponent(objectFilename); + if (current.exists()) + objectFilename = current.c_str(); + } + + if (OutputIStats) + theStatisticManager->useIndexedStats(km->infos->getMaxID()); + + for (std::vector<KFunction*>::iterator it = km->functions.begin(), + ie = km->functions.end(); it != ie; ++it) { + KFunction *kf = *it; + kf->trackCoverage = 1; + + for (unsigned i=0; i<kf->numInstructions; ++i) { + KInstruction *ki = kf->instructions[i]; + + if (OutputIStats) { + unsigned id = ki->info->id; + theStatisticManager->setIndex(id); + if (kf->trackCoverage && instructionIsCoverable(ki->inst)) + ++stats::uncoveredInstructions; + } + + if (kf->trackCoverage) { + if (BranchInst *bi = dyn_cast<BranchInst>(ki->inst)) + if (!bi->isUnconditional()) + numBranches++; + } + } + } + + if (OutputStats) { + statsFile = executor.interpreterHandler->openOutputFile("run.stats"); + assert(statsFile && "unable to open statistics trace file"); + writeStatsHeader(); + writeStatsLine(); + + executor.addTimer(new WriteStatsTimer(this), StatsWriteInterval); + + if (updateMinDistToUncovered) + executor.addTimer(new UpdateReachableTimer(this), UncoveredUpdateInterval); + } + + if (OutputIStats) { + istatsFile = executor.interpreterHandler->openOutputFile("run.istats"); + assert(istatsFile && "unable to open istats file"); + + executor.addTimer(new WriteIStatsTimer(this), IStatsWriteInterval); + } +} + +StatsTracker::~StatsTracker() { + if (statsFile) + delete statsFile; + if (istatsFile) + delete istatsFile; +} + +void StatsTracker::done() { + if (statsFile) + writeStatsLine(); + if (OutputIStats) + writeIStats(); +} + +void StatsTracker::stepInstruction(ExecutionState &es) { + if (OutputIStats) { + if (TrackInstructionTime) { + static sys::TimeValue lastNowTime(0,0),lastUserTime(0,0); + + if (lastUserTime.seconds()==0 && lastUserTime.nanoseconds()==0) { + sys::TimeValue sys(0,0); + sys::Process::GetTimeUsage(lastNowTime,lastUserTime,sys); + } else { + sys::TimeValue now(0,0),user(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + sys::TimeValue delta = user - lastUserTime; + sys::TimeValue deltaNow = now - lastNowTime; + stats::instructionTime += delta.usec(); + stats::instructionRealTime += deltaNow.usec(); + lastUserTime = user; + lastNowTime = now; + } + } + + Instruction *inst = es.pc->inst; + const InstructionInfo &ii = *es.pc->info; + StackFrame &sf = es.stack.back(); + theStatisticManager->setIndex(ii.id); + if (UseCallPaths) + theStatisticManager->setContext(&sf.callPathNode->statistics); + + if (es.instsSinceCovNew) + ++es.instsSinceCovNew; + + if (sf.kf->trackCoverage && instructionIsCoverable(inst)) { + if (!theStatisticManager->getIndexedValue(stats::coveredInstructions, ii.id)) { + // Checking for actual stoppoints avoids inconsistencies due + // to line number propogation. + if (isa<DbgStopPointInst>(inst)) + es.coveredLines[&ii.file].insert(ii.line); + es.coveredNew = true; + es.instsSinceCovNew = 1; + ++stats::coveredInstructions; + stats::uncoveredInstructions += (uint64_t)-1; + } + } + } +} + +/// + +/* Should be called _after_ the es->pushFrame() */ +void StatsTracker::framePushed(ExecutionState &es, StackFrame *parentFrame) { + if (OutputIStats) { + StackFrame &sf = es.stack.back(); + + if (UseCallPaths) { + CallPathNode *parent = parentFrame ? parentFrame->callPathNode : 0; + CallPathNode *cp = callPathManager.getCallPath(parent, + sf.caller ? sf.caller->inst : 0, + sf.kf->function); + sf.callPathNode = cp; + cp->count++; + } + + if (updateMinDistToUncovered) { + uint64_t minDistAtRA = 0; + if (parentFrame) + minDistAtRA = parentFrame->minDistToUncoveredOnReturn; + + sf.minDistToUncoveredOnReturn = sf.caller ? + computeMinDistToUncovered(sf.caller, minDistAtRA) : 0; + } + } +} + +/* Should be called _after_ the es->popFrame() */ +void StatsTracker::framePopped(ExecutionState &es) { + // XXX remove me? +} + + +void StatsTracker::markBranchVisited(ExecutionState *visitedTrue, + ExecutionState *visitedFalse) { + if (OutputIStats) { + unsigned id = theStatisticManager->getIndex(); + uint64_t hasTrue = theStatisticManager->getIndexedValue(stats::trueBranches, id); + uint64_t hasFalse = theStatisticManager->getIndexedValue(stats::falseBranches, id); + if (visitedTrue && !hasTrue) { + visitedTrue->coveredNew = true; + visitedTrue->instsSinceCovNew = 1; + ++stats::trueBranches; + if (hasFalse) { ++fullBranches; --partialBranches; } + else ++partialBranches; + hasTrue = 1; + } + if (visitedFalse && !hasFalse) { + visitedFalse->coveredNew = true; + visitedFalse->instsSinceCovNew = 1; + ++stats::falseBranches; + if (hasTrue) { ++fullBranches; --partialBranches; } + else ++partialBranches; + } + } +} + +void StatsTracker::writeStatsHeader() { + *statsFile << "('Instructions'," + << "'FullBranches'," + << "'PartialBranches'," + << "'NumBranches'," + << "'UserTime'," + << "'NumStates'," + << "'MallocUsage'," + << "'NumQueries'," + << "'NumQueryConstructs'," + << "'NumObjects'," + << "'WallTime'," + << "'CoveredInstructions'," + << "'UncoveredInstructions'," + << "'QueryTime'," + << "'SolverTime'," + << "'CexCacheTime'," + << "'ForkTime'," + << "'ResolveTime'," + << ")\n"; + statsFile->flush(); +} + +double StatsTracker::elapsed() { + return util::getWallTime() - startWallTime; +} + +void StatsTracker::writeStatsLine() { + *statsFile << "(" << stats::instructions + << "," << fullBranches + << "," << partialBranches + << "," << numBranches + << "," << util::getUserTime() + << "," << executor.states.size() + << "," << sys::Process::GetTotalMemoryUsage() + << "," << stats::queries + << "," << stats::queryConstructs + << "," << 0 // was numObjects + << "," << elapsed() + << "," << stats::coveredInstructions + << "," << stats::uncoveredInstructions + << "," << stats::queryTime / 1000000. + << "," << stats::solverTime / 1000000. + << "," << stats::cexCacheTime / 1000000. + << "," << stats::forkTime / 1000000. + << "," << stats::resolveTime / 1000000. + << ")\n"; + statsFile->flush(); +} + +void StatsTracker::updateStateStatistics(uint64_t addend) { + for (std::set<ExecutionState*>::iterator it = executor.states.begin(), + ie = executor.states.end(); it != ie; ++it) { + ExecutionState &state = **it; + const InstructionInfo &ii = *state.pc->info; + theStatisticManager->incrementIndexedValue(stats::states, ii.id, addend); + if (UseCallPaths) + state.stack.back().callPathNode->statistics.incrementValue(stats::states, addend); + } +} + +void StatsTracker::writeIStats() { + Module *m = executor.kmodule->module; + uint64_t istatsMask = 0; + std::ostream &of = *istatsFile; + + of.seekp(0, std::ios::end); + unsigned istatsSize = of.tellp(); + of.seekp(0); + + of << "version: 1\n"; + of << "creator: klee\n"; + of << "pid: " << sys::Process::GetCurrentUserId() << "\n"; + of << "cmd: " << m->getModuleIdentifier() << "\n\n"; + of << "\n"; + + StatisticManager &sm = *theStatisticManager; + unsigned nStats = sm.getNumStatistics(); + + // Max is 13, sadly + istatsMask |= 1<<sm.getStatisticID("Queries"); + istatsMask |= 1<<sm.getStatisticID("QueriesValid"); + istatsMask |= 1<<sm.getStatisticID("QueriesInvalid"); + istatsMask |= 1<<sm.getStatisticID("QueryTime"); + istatsMask |= 1<<sm.getStatisticID("ResolveTime"); + istatsMask |= 1<<sm.getStatisticID("Instructions"); + istatsMask |= 1<<sm.getStatisticID("InstructionTimes"); + istatsMask |= 1<<sm.getStatisticID("InstructionRealTimes"); + istatsMask |= 1<<sm.getStatisticID("Forks"); + istatsMask |= 1<<sm.getStatisticID("CoveredInstructions"); + istatsMask |= 1<<sm.getStatisticID("UncoveredInstructions"); + istatsMask |= 1<<sm.getStatisticID("States"); + istatsMask |= 1<<sm.getStatisticID("MinDistToUncovered"); + + of << "positions: instr line\n"; + + for (unsigned i=0; i<nStats; i++) { + if (istatsMask & (1<<i)) { + Statistic &s = sm.getStatistic(i); + of << "event: " << s.getShortName() << " : " + << s.getName() << "\n"; + } + } + + of << "events: "; + for (unsigned i=0; i<nStats; i++) { + if (istatsMask & (1<<i)) + of << sm.getStatistic(i).getShortName() << " "; + } + of << "\n"; + + // set state counts, decremented after we process so that we don't + // have to zero all records each time. + if (istatsMask & (1<<stats::states.getID())) + updateStateStatistics(1); + + std::string sourceFile = ""; + + CallSiteSummaryTable callSiteStats; + if (UseCallPaths) + callPathManager.getSummaryStatistics(callSiteStats); + + of << "ob=" << objectFilename << "\n"; + + for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); + fnIt != fn_ie; ++fnIt) { + if (!fnIt->isDeclaration()) { + of << "fn=" << fnIt->getName() << "\n"; + for (Function::iterator bbIt = fnIt->begin(), bb_ie = fnIt->end(); + bbIt != bb_ie; ++bbIt) { + for (BasicBlock::iterator it = bbIt->begin(), ie = bbIt->end(); + it != it; ++it) { + Instruction *instr = &*it; + const InstructionInfo &ii = executor.kmodule->infos->getInfo(instr); + unsigned index = ii.id; + if (ii.file!=sourceFile) { + of << "fl=" << ii.file << "\n"; + sourceFile = ii.file; + } + of << ii.assemblyLine << " "; + of << ii.line << " "; + for (unsigned i=0; i<nStats; i++) + if (istatsMask&(1<<i)) + of << sm.getIndexedValue(sm.getStatistic(i), index) << " "; + of << "\n"; + + if (UseCallPaths && + (isa<CallInst>(instr) || isa<InvokeInst>(instr))) { + CallSiteSummaryTable::iterator it = callSiteStats.find(instr); + if (it!=callSiteStats.end()) { + for (std::map<llvm::Function*, CallSiteInfo>::iterator + fit = it->second.begin(), fie = it->second.end(); + fit != fie; ++fit) { + Function *f = fit->first; + CallSiteInfo &csi = fit->second; + const InstructionInfo &fii = + executor.kmodule->infos->getFunctionInfo(f); + + if (fii.file!="" && fii.file!=sourceFile) + of << "cfl=" << fii.file << "\n"; + of << "cfn=" << f->getName() << "\n"; + of << "calls=" << csi.count << " "; + of << fii.assemblyLine << " "; + of << fii.line << "\n"; + + of << ii.assemblyLine << " "; + of << ii.line << " "; + for (unsigned i=0; i<nStats; i++) { + if (istatsMask&(1<<i)) { + Statistic &s = sm.getStatistic(i); + uint64_t value; + + // Hack, ignore things that don't make sense on + // call paths. + if (&s == &stats::uncoveredInstructions) { + value = 0; + } else { + value = csi.statistics.getValue(s); + } + + of << value << " "; + } + } + of << "\n"; + } + } + } + } + } + } + } + + if (istatsMask & (1<<stats::states.getID())) + updateStateStatistics((uint64_t)-1); + + // Clear then end of the file if necessary (no truncate op?). + unsigned pos = of.tellp(); + for (unsigned i=pos; i<istatsSize; ++i) + of << '\n'; + + of.flush(); +} + +/// + +typedef std::map<Instruction*, std::vector<Function*> > calltargets_ty; + +static calltargets_ty callTargets; +static std::map<Function*, std::vector<Instruction*> > functionCallers; +static std::map<Function*, unsigned> functionShortestPath; + +static std::vector<Instruction*> getSuccs(Instruction *i) { + BasicBlock *bb = i->getParent(); + std::vector<Instruction*> res; + + if (i==bb->getTerminator()) { + for (succ_iterator it = succ_begin(bb), ie = succ_end(bb); it != ie; ++it) + res.push_back(it->begin()); + } else { + res.push_back(++BasicBlock::iterator(i)); + } + + return res; +} + +uint64_t klee::computeMinDistToUncovered(const KInstruction *ki, + uint64_t minDistAtRA) { + StatisticManager &sm = *theStatisticManager; + if (minDistAtRA==0) { // unreachable on return, best is local + return sm.getIndexedValue(stats::minDistToUncovered, + ki->info->id); + } else { + uint64_t minDistLocal = sm.getIndexedValue(stats::minDistToUncovered, + ki->info->id); + uint64_t distToReturn = sm.getIndexedValue(stats::minDistToReturn, + ki->info->id); + + if (distToReturn==0) { // return unreachable, best is local + return minDistLocal; + } else if (!minDistLocal) { // no local reachable + return distToReturn + minDistAtRA; + } else { + return std::min(minDistLocal, distToReturn + minDistAtRA); + } + } +} + +void StatsTracker::computeReachableUncovered() { + KModule *km = executor.kmodule; + Module *m = km->module; + static bool init = true; + const InstructionInfoTable &infos = *km->infos; + StatisticManager &sm = *theStatisticManager; + + if (init) { + init = false; + + // Compute call targets. It would be nice to use alias information + // instead of assuming all indirect calls hit all escaping + // functions, eh? + for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); + fnIt != fn_ie; ++fnIt) { + for (Function::iterator bbIt = fnIt->begin(), bb_ie = fnIt->end(); + bbIt != bb_ie; ++bbIt) { + for (BasicBlock::iterator it = bbIt->begin(), ie = bbIt->end(); + it != it; ++it) { + if (isa<CallInst>(it) || isa<InvokeInst>(it)) { + if (isa<InlineAsm>(it->getOperand(0))) { + // We can never call through here so assume no targets + // (which should be correct anyhow). + callTargets.insert(std::make_pair(it, + std::vector<Function*>())); + } else if (Function *target = getDirectCallTarget(it)) { + callTargets[it].push_back(target); + } else { + callTargets[it] = + std::vector<Function*>(km->escapingFunctions.begin(), + km->escapingFunctions.end()); + } + } + } + } + } + + // Compute function callers as reflexion of callTargets. + for (calltargets_ty::iterator it = callTargets.begin(), + ie = callTargets.end(); it != ie; ++it) + for (std::vector<Function*>::iterator fit = it->second.begin(), + fie = it->second.end(); fit != fie; ++fit) + functionCallers[*fit].push_back(it->first); + + // Initialize minDistToReturn to shortest paths through + // functions. 0 is unreachable. + std::vector<Instruction *> instructions; + for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); + fnIt != fn_ie; ++fnIt) { + if (fnIt->isDeclaration()) { + if (fnIt->doesNotReturn()) { + functionShortestPath[fnIt] = 0; + } else { + functionShortestPath[fnIt] = 1; // whatever + } + } else { + functionShortestPath[fnIt] = 0; + } + + // Not sure if I should bother to preorder here. XXX I should. + for (Function::iterator bbIt = fnIt->begin(), bb_ie = fnIt->end(); + bbIt != bb_ie; ++bbIt) { + for (BasicBlock::iterator it = bbIt->begin(), ie = bbIt->end(); + it != it; ++it) { + instructions.push_back(it); + unsigned id = infos.getInfo(it).id; + sm.setIndexedValue(stats::minDistToReturn, + id, + isa<ReturnInst>(it) || isa<UnwindInst>(it)); + } + } + } + + std::reverse(instructions.begin(), instructions.end()); + + // I'm so lazy it's not even worklisted. + bool changed; + do { + changed = false; + for (std::vector<Instruction*>::iterator it = instructions.begin(), + ie = instructions.end(); it != ie; ++it) { + Instruction *inst = *it; + unsigned bestThrough = 0; + + if (isa<CallInst>(inst) || isa<InvokeInst>(inst)) { + std::vector<Function*> &targets = callTargets[inst]; + for (std::vector<Function*>::iterator fnIt = targets.begin(), + ie = targets.end(); fnIt != ie; ++fnIt) { + uint64_t dist = functionShortestPath[*fnIt]; + if (dist) { + dist = 1+dist; // count instruction itself + if (bestThrough==0 || dist<bestThrough) + bestThrough = dist; + } + } + } else { + bestThrough = 1; + } + + if (bestThrough) { + unsigned id = infos.getInfo(*it).id; + uint64_t best, cur = best = sm.getIndexedValue(stats::minDistToReturn, id); + std::vector<Instruction*> succs = getSuccs(*it); + for (std::vector<Instruction*>::iterator it2 = succs.begin(), + ie = succs.end(); it2 != ie; ++it2) { + uint64_t dist = sm.getIndexedValue(stats::minDistToReturn, + infos.getInfo(*it2).id); + if (dist) { + uint64_t val = bestThrough + dist; + if (best==0 || val<best) + best = val; + } + } + if (best != cur) { + sm.setIndexedValue(stats::minDistToReturn, id, best); + changed = true; + + // Update shortest path if this is the entry point. + Function *f = inst->getParent()->getParent(); + if (inst==f->begin()->begin()) + functionShortestPath[f] = best; + } + } + } + } while (changed); + } + + // compute minDistToUncovered, 0 is unreachable + std::vector<Instruction *> instructions; + for (Module::iterator fnIt = m->begin(), fn_ie = m->end(); + fnIt != fn_ie; ++fnIt) { + // Not sure if I should bother to preorder here. + for (Function::iterator bbIt = fnIt->begin(), bb_ie = fnIt->end(); + bbIt != bb_ie; ++bbIt) { + for (BasicBlock::iterator it = bbIt->begin(), ie = bbIt->end(); + it != it; ++it) { + unsigned id = infos.getInfo(it).id; + instructions.push_back(&*it); + sm.setIndexedValue(stats::minDistToUncovered, + id, + sm.getIndexedValue(stats::uncoveredInstructions, id)); + } + } + } + + std::reverse(instructions.begin(), instructions.end()); + + // I'm so lazy it's not even worklisted. + bool changed; + do { + changed = false; + for (std::vector<Instruction*>::iterator it = instructions.begin(), + ie = instructions.end(); it != ie; ++it) { + Instruction *inst = *it; + uint64_t best, cur = best = sm.getIndexedValue(stats::minDistToUncovered, + infos.getInfo(inst).id); + unsigned bestThrough = 0; + + if (isa<CallInst>(inst) || isa<InvokeInst>(inst)) { + std::vector<Function*> &targets = callTargets[inst]; + for (std::vector<Function*>::iterator fnIt = targets.begin(), + ie = targets.end(); fnIt != ie; ++fnIt) { + uint64_t dist = functionShortestPath[*fnIt]; + if (dist) { + dist = 1+dist; // count instruction itself + if (bestThrough==0 || dist<bestThrough) + bestThrough = dist; + } + + if (!(*fnIt)->isDeclaration()) { + uint64_t calleeDist = sm.getIndexedValue(stats::minDistToUncovered, + infos.getFunctionInfo(*fnIt).id); + if (calleeDist) { + calleeDist = 1+calleeDist; // count instruction itself + if (best==0 || calleeDist<best) + best = calleeDist; + } + } + } + } else { + bestThrough = 1; + } + + if (bestThrough) { + std::vector<Instruction*> succs = getSuccs(inst); + for (std::vector<Instruction*>::iterator it2 = succs.begin(), + ie = succs.end(); it2 != ie; ++it2) { + uint64_t dist = sm.getIndexedValue(stats::minDistToUncovered, + infos.getInfo(*it2).id); + if (dist) { + uint64_t val = bestThrough + dist; + if (best==0 || val<best) + best = val; + } + } + } + + if (best != cur) { + sm.setIndexedValue(stats::minDistToUncovered, + infos.getInfo(inst).id, + best); + changed = true; + } + } + } while (changed); + + for (std::set<ExecutionState*>::iterator it = executor.states.begin(), + ie = executor.states.end(); it != ie; ++it) { + ExecutionState *es = *it; + uint64_t currentFrameMinDist = 0; + for (ExecutionState::stack_ty::iterator sfIt = es->stack.begin(), + sf_ie = es->stack.end(); sfIt != sf_ie; ++sfIt) { + ExecutionState::stack_ty::iterator next = sfIt + 1; + KInstIterator kii; + + if (next==es->stack.end()) { + kii = es->pc; + } else { + kii = next->caller; + ++kii; + } + + sfIt->minDistToUncoveredOnReturn = currentFrameMinDist; + + currentFrameMinDist = computeMinDistToUncovered(kii, currentFrameMinDist); + } + } +} diff --git a/lib/Core/StatsTracker.h b/lib/Core/StatsTracker.h new file mode 100644 index 00000000..9d22b389 --- /dev/null +++ b/lib/Core/StatsTracker.h @@ -0,0 +1,93 @@ +//===-- StatsTracker.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_STATSTRACKER_H +#define KLEE_STATSTRACKER_H + +#include "CallPathManager.h" + +#include <iostream> +#include <set> + +namespace llvm { + class BranchInst; + class Function; + class Instruction; +} + +namespace klee { + class ExecutionState; + class Executor; + class InstructionInfoTable; + class InterpreterHandler; + class KInstruction; + class StackFrame; + + class StatsTracker { + friend class WriteStatsTimer; + friend class WriteIStatsTimer; + + Executor &executor; + std::string objectFilename; + + std::ostream *statsFile, *istatsFile; + double startWallTime; + + unsigned numBranches; + unsigned fullBranches, partialBranches; + + CallPathManager callPathManager; + + bool updateMinDistToUncovered; + + public: + static bool useStatistics(); + + private: + void updateStateStatistics(uint64_t addend); + void writeStatsHeader(); + void writeStatsLine(); + void writeIStats(); + + public: + StatsTracker(Executor &_executor, std::string _objectFilename, + bool _updateMinDistToUncovered); + ~StatsTracker(); + + // called after a new StackFrame has been pushed (for callpath tracing) + void framePushed(ExecutionState &es, StackFrame *parentFrame); + + // called after a StackFrame has been popped + void framePopped(ExecutionState &es); + + // called when some side of a branch has been visited. it is + // imperative that this be called when the statistics index is at + // the index for the branch itself. + void markBranchVisited(ExecutionState *visitedTrue, + ExecutionState *visitedFalse); + + // called when execution is done and stats files should be flushed + void done(); + + // process stats for a single instruction step, es is the state + // about to be stepped + void stepInstruction(ExecutionState &es); + + /// Return time in seconds since execution start. + double elapsed(); + + void computeReachableUncovered(); + }; + + uint64_t computeMinDistToUncovered(const KInstruction *ki, + uint64_t minDistAtRA); + +} + +#endif diff --git a/lib/Core/TimingSolver.cpp b/lib/Core/TimingSolver.cpp new file mode 100644 index 00000000..70e42836 --- /dev/null +++ b/lib/Core/TimingSolver.cpp @@ -0,0 +1,147 @@ +//===-- TimingSolver.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TimingSolver.h" + +#include "klee/ExecutionState.h" +#include "klee/Solver.h" +#include "klee/Statistics.h" + +#include "CoreStats.h" + +#include "llvm/System/Process.h" + +using namespace klee; +using namespace llvm; + +/***/ + +bool TimingSolver::evaluate(const ExecutionState& state, ref<Expr> expr, + Solver::Validity &result) { + // Fast path, to avoid timer and OS overhead. + if (expr.isConstant()) { + result = expr.getConstantValue() ? Solver::True : Solver::False; + return true; + } + + sys::TimeValue now(0,0),user(0,0),delta(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + + if (simplifyExprs) + expr = state.constraints.simplifyExpr(expr); + + bool success = solver->evaluate(Query(state.constraints, expr), result); + + sys::Process::GetTimeUsage(delta,user,sys); + delta -= now; + stats::solverTime += delta.usec(); + state.queryCost += delta.usec()/1000000.; + + return success; +} + +bool TimingSolver::mustBeTrue(const ExecutionState& state, ref<Expr> expr, + bool &result) { + // Fast path, to avoid timer and OS overhead. + if (expr.isConstant()) { + result = expr.getConstantValue() ? true : false; + return true; + } + + sys::TimeValue now(0,0),user(0,0),delta(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + + if (simplifyExprs) + expr = state.constraints.simplifyExpr(expr); + + bool success = solver->mustBeTrue(Query(state.constraints, expr), result); + + sys::Process::GetTimeUsage(delta,user,sys); + delta -= now; + stats::solverTime += delta.usec(); + state.queryCost += delta.usec()/1000000.; + + return success; +} + +bool TimingSolver::mustBeFalse(const ExecutionState& state, ref<Expr> expr, + bool &result) { + return mustBeTrue(state, Expr::createNot(expr), result); +} + +bool TimingSolver::mayBeTrue(const ExecutionState& state, ref<Expr> expr, + bool &result) { + bool res; + if (!mustBeFalse(state, expr, res)) + return false; + result = !res; + return true; +} + +bool TimingSolver::mayBeFalse(const ExecutionState& state, ref<Expr> expr, + bool &result) { + bool res; + if (!mustBeTrue(state, expr, res)) + return false; + result = !res; + return true; +} + +bool TimingSolver::getValue(const ExecutionState& state, ref<Expr> expr, + ref<Expr> &result) { + // Fast path, to avoid timer and OS overhead. + if (expr.isConstant()) { + result = expr; + return true; + } + + sys::TimeValue now(0,0),user(0,0),delta(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + + if (simplifyExprs) + expr = state.constraints.simplifyExpr(expr); + + bool success = solver->getValue(Query(state.constraints, expr), result); + + sys::Process::GetTimeUsage(delta,user,sys); + delta -= now; + stats::solverTime += delta.usec(); + state.queryCost += delta.usec()/1000000.; + + return success; +} + +bool +TimingSolver::getInitialValues(const ExecutionState& state, + const std::vector<const Array*> + &objects, + std::vector< std::vector<unsigned char> > + &result) { + if (objects.empty()) + return true; + + sys::TimeValue now(0,0),user(0,0),delta(0,0),sys(0,0); + sys::Process::GetTimeUsage(now,user,sys); + + bool success = solver->getInitialValues(Query(state.constraints, + ref<Expr>(0, Expr::Bool)), + objects, result); + + sys::Process::GetTimeUsage(delta,user,sys); + delta -= now; + stats::solverTime += delta.usec(); + state.queryCost += delta.usec()/1000000.; + + return success; +} + +std::pair< ref<Expr>, ref<Expr> > +TimingSolver::getRange(const ExecutionState& state, ref<Expr> expr) { + return solver->getRange(Query(state.constraints, expr)); +} diff --git a/lib/Core/TimingSolver.h b/lib/Core/TimingSolver.h new file mode 100644 index 00000000..875216d9 --- /dev/null +++ b/lib/Core/TimingSolver.h @@ -0,0 +1,70 @@ +//===-- TimingSolver.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_TIMINGSOLVER_H +#define KLEE_TIMINGSOLVER_H + +#include "klee/Expr.h" +#include "klee/Solver.h" + +#include <vector> + +namespace klee { + class ExecutionState; + class Solver; + class STPSolver; + + /// TimingSolver - A simple class which wraps a solver and handles + /// tracking the statistics that we care about. + class TimingSolver { + public: + Solver *solver; + STPSolver *stpSolver; + bool simplifyExprs; + + public: + /// TimingSolver - Construct a new timing solver. + /// + /// \param _simplifyExprs - Whether expressions should be + /// simplified (via the constraint manager interface) prior to + /// querying. + TimingSolver(Solver *_solver, STPSolver *_stpSolver, + bool _simplifyExprs = true) + : solver(_solver), stpSolver(_stpSolver), simplifyExprs(_simplifyExprs) {} + ~TimingSolver() { + delete solver; + } + + void setTimeout(double t) { + stpSolver->setTimeout(t); + } + + bool evaluate(const ExecutionState&, ref<Expr>, Solver::Validity &result); + + bool mustBeTrue(const ExecutionState&, ref<Expr>, bool &result); + + bool mustBeFalse(const ExecutionState&, ref<Expr>, bool &result); + + bool mayBeTrue(const ExecutionState&, ref<Expr>, bool &result); + + bool mayBeFalse(const ExecutionState&, ref<Expr>, bool &result); + + bool getValue(const ExecutionState &, ref<Expr> expr, ref<Expr> &result); + + bool getInitialValues(const ExecutionState&, + const std::vector<const Array*> &objects, + std::vector< std::vector<unsigned char> > &result); + + virtual std::pair< ref<Expr>, ref<Expr> > + getRange(const ExecutionState&, ref<Expr> query); + }; + +} + +#endif diff --git a/lib/Core/UserSearcher.cpp b/lib/Core/UserSearcher.cpp new file mode 100644 index 00000000..1aff9e5e --- /dev/null +++ b/lib/Core/UserSearcher.cpp @@ -0,0 +1,175 @@ +//===-- UserSearcher.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Common.h" + +#include "UserSearcher.h" + +#include "Searcher.h" +#include "Executor.h" + +#include "llvm/Support/CommandLine.h" + +using namespace llvm; +using namespace klee; + +namespace { + cl::opt<bool> + UseRandomSearch("use-random-search"); + + cl::opt<bool> + UseInterleavedRS("use-interleaved-RS"); + + cl::opt<bool> + UseInterleavedNURS("use-interleaved-NURS"); + + cl::opt<bool> + UseInterleavedMD2UNURS("use-interleaved-MD2U-NURS"); + + cl::opt<bool> + UseInterleavedInstCountNURS("use-interleaved-icnt-NURS"); + + cl::opt<bool> + UseInterleavedCPInstCountNURS("use-interleaved-cpicnt-NURS"); + + cl::opt<bool> + UseInterleavedQueryCostNURS("use-interleaved-query-cost-NURS"); + + cl::opt<bool> + UseInterleavedCovNewNURS("use-interleaved-covnew-NURS"); + + cl::opt<bool> + UseNonUniformRandomSearch("use-non-uniform-random-search"); + + cl::opt<bool> + UseRandomPathSearch("use-random-path"); + + cl::opt<WeightedRandomSearcher::WeightType> + WeightType("weight-type", cl::desc("Set the weight type for --use-non-uniform-random-search"), + cl::values(clEnumValN(WeightedRandomSearcher::Depth, "none", "use (2^depth)"), + clEnumValN(WeightedRandomSearcher::InstCount, "icnt", "use current pc exec count"), + clEnumValN(WeightedRandomSearcher::CPInstCount, "cpicnt", "use current pc exec count"), + clEnumValN(WeightedRandomSearcher::QueryCost, "query-cost", "use query cost"), + clEnumValN(WeightedRandomSearcher::MinDistToUncovered, "md2u", "use min dist to uncovered"), + clEnumValN(WeightedRandomSearcher::CoveringNew, "covnew", "use min dist to uncovered + coveringNew flag"), + clEnumValEnd)); + + cl::opt<bool> + UseMerge("use-merge", + cl::desc("Enable support for klee_merge() (experimental)")); + + cl::opt<bool> + UseBumpMerge("use-bump-merge", + cl::desc("Enable support for klee_merge() (extra experimental)")); + + cl::opt<bool> + UseIterativeDeepeningTimeSearch("use-iterative-deepening-time-search", + cl::desc("(experimental)")); + + cl::opt<bool> + UseBatchingSearch("use-batching-search", + cl::desc("Use batching searcher (keep running selected state for N instructions/time, see --batch-instructions and --batch-time")); + + cl::opt<unsigned> + BatchInstructions("batch-instructions", + cl::desc("Number of instructions to batch when using --use-batching-search"), + cl::init(10000)); + + cl::opt<double> + BatchTime("batch-time", + cl::desc("Amount of time to batch when using --use-batching-search"), + cl::init(5.0)); +} + +bool klee::userSearcherRequiresMD2U() { + return (WeightType==WeightedRandomSearcher::MinDistToUncovered || + WeightType==WeightedRandomSearcher::CoveringNew || + UseInterleavedMD2UNURS || + UseInterleavedCovNewNURS || + UseInterleavedInstCountNURS || + UseInterleavedCPInstCountNURS || + UseInterleavedQueryCostNURS); +} + +// FIXME: Remove. +bool klee::userSearcherRequiresBranchSequences() { + return false; +} + +Searcher *klee::constructUserSearcher(Executor &executor) { + Searcher *searcher = 0; + + if (UseRandomPathSearch) { + searcher = new RandomPathSearcher(executor); + } else if (UseNonUniformRandomSearch) { + searcher = new WeightedRandomSearcher(executor, WeightType); + } else if (UseRandomSearch) { + searcher = new RandomSearcher(); + } else { + searcher = new DFSSearcher(); + } + + if (UseInterleavedNURS || UseInterleavedMD2UNURS || UseInterleavedRS || + UseInterleavedCovNewNURS || UseInterleavedInstCountNURS || + UseInterleavedCPInstCountNURS || UseInterleavedQueryCostNURS) { + std::vector<Searcher *> s; + s.push_back(searcher); + + if (UseInterleavedNURS) + s.push_back(new WeightedRandomSearcher(executor, + WeightedRandomSearcher::Depth)); + if (UseInterleavedMD2UNURS) + s.push_back(new WeightedRandomSearcher(executor, + WeightedRandomSearcher::MinDistToUncovered)); + + if (UseInterleavedCovNewNURS) + s.push_back(new WeightedRandomSearcher(executor, + WeightedRandomSearcher::CoveringNew)); + + if (UseInterleavedInstCountNURS) + s.push_back(new WeightedRandomSearcher(executor, + WeightedRandomSearcher::InstCount)); + + if (UseInterleavedCPInstCountNURS) + s.push_back(new WeightedRandomSearcher(executor, + WeightedRandomSearcher::CPInstCount)); + + if (UseInterleavedQueryCostNURS) + s.push_back(new WeightedRandomSearcher(executor, + WeightedRandomSearcher::QueryCost)); + + if (UseInterleavedRS) + s.push_back(new RandomSearcher()); + + searcher = new InterleavedSearcher(s); + } + + if (UseBatchingSearch) { + searcher = new BatchingSearcher(searcher, BatchTime, BatchInstructions); + } + + if (UseMerge) { + assert(!UseBumpMerge); + searcher = new MergingSearcher(executor, searcher); + } else if (UseBumpMerge) { + searcher = new BumpMergingSearcher(executor, searcher); + } + + if (UseIterativeDeepeningTimeSearch) { + searcher = new IterativeDeepeningTimeSearcher(searcher); + } + + std::ostream &os = executor.getHandler().getInfoStream(); + + os << "BEGIN searcher description\n"; + searcher->printName(os); + os << "END searcher description\n"; + + return searcher; +} diff --git a/lib/Core/UserSearcher.h b/lib/Core/UserSearcher.h new file mode 100644 index 00000000..9571bf5b --- /dev/null +++ b/lib/Core/UserSearcher.h @@ -0,0 +1,25 @@ +//===-- UserSearcher.h ------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_USERSEARCHER_H +#define KLEE_USERSEARCHER_H + +namespace klee { + class Executor; + class Searcher; + + // XXX gross, should be on demand? + bool userSearcherRequiresMD2U(); + + bool userSearcherRequiresBranchSequences(); + + Searcher *constructUserSearcher(Executor &executor); +} + +#endif |