diff --git a/build.sh b/build.sh index 12f4182699..7108975de4 100755 --- a/build.sh +++ b/build.sh @@ -1,35 +1,51 @@ -#!/bin/bash -# This script is used to build KLEE as UTBot backend - -set -e -set -o pipefail -mkdir -p build -cd build - -$UTBOT_CMAKE_BINARY -G Ninja \ - -DCMAKE_PREFIX_PATH=$UTBOT_INSTALL_DIR/lib/cmake/z3 \ - -DCMAKE_LIBRARY_PATH=$UTBOT_INSTALL_DIR/lib \ - -DCMAKE_INCLUDE_PATH=$UTBOT_INSTALL_DIR/include \ - -DENABLE_SOLVER_Z3=TRUE \ - -DENABLE_POSIX_RUNTIME=TRUE \ - -DENABLE_KLEE_UCLIBC=ON \ - -DKLEE_UCLIBC_PATH=$UTBOT_ALL/klee-uclibc \ - -DENABLE_FLOATING_POINT=TRUE \ - -DENABLE_FP_RUNTIME=TRUE \ - -DLLVM_CONFIG_BINARY=$UTBOT_INSTALL_DIR/bin/llvm-config \ - -DLLVMCC=/utbot_distr/install/bin/clang \ - -DLLVMCXX=/utbot_distr/install/bin/clang++ \ - -DENABLE_UNIT_TESTS=TRUE \ - -DENABLE_SYSTEM_TESTS=TRUE \ - -DGTEST_SRC_DIR=$UTBOT_ALL/gtest \ - -DGTEST_INCLUDE_DIR=$UTBOT_ALL/gtest/googletest/include \ - -DCMAKE_INSTALL_PREFIX=$UTBOT_ALL/klee \ - -DENABLE_KLEE_LIBCXX=TRUE \ - -DKLEE_LIBCXX_DIR=$UTBOT_ALL/libcxx/install \ - -DKLEE_LIBCXX_INCLUDE_DIR=$UTBOT_ALL/libcxx/install/include/c++/v1 \ - -DENABLE_KLEE_EH_CXX=TRUE \ - -DKLEE_LIBCXXABI_SRC_DIR=$UTBOT_ALL/libcxx/libcxxabi \ - .. - -$UTBOT_CMAKE_BINARY --build . -$UTBOT_CMAKE_BINARY --install . +#!/bin/sh + +# For more build options, visit +# https://klee.github.io/build-script/ + +# Base folder where dependencies and KLEE itself are installed +BASE=$HOME/klee_build + +## KLEE Required options +# Build type for KLEE. The options are: +# Release +# Release+Debug +# Release+Asserts +# Release+Debug+Asserts +# Debug +# Debug+Asserts +# KLEE_RUNTIME_BUILD="Debug+Asserts" +KLEE_RUNTIME_BUILD="Debug" # "Debug+Asserts" + +COVERAGE=0 +ENABLE_DOXYGEN=0 +USE_TCMALLOC=0 +USE_LIBCXX=1 +# Also required despite not being mentioned in the guide +SQLITE_VERSION="3370200" + + +## LLVM Required options +LLVM_VERSION=11 +ENABLE_OPTIMIZED=1 +ENABLE_DEBUG=0 +DISABLE_ASSERTIONS=1 +REQUIRES_RTTI=1 + +## Solvers Required options +# SOLVERS=STP +SOLVERS=Z3 + +## Google Test Required options +GTEST_VERSION=1.11.0 + +## UClibC Required options +UCLIBC_VERSION=klee_0_9_29 +# LLVM_VERSION is also required for UClibC + +## Z3 Required options +Z3_VERSION=4.8.15 +STP_VERSION=2.3.3 +MINISAT_VERSION=master + +BASE="$BASE" KLEE_RUNTIME_BUILD=$KLEE_RUNTIME_BUILD COVERAGE=$COVERAGE ENABLE_DOXYGEN=$ENABLE_DOXYGEN USE_TCMALLOC=$USE_TCMALLOC USE_LIBCXX=$USE_LIBCXX LLVM_VERSION=$LLVM_VERSION ENABLE_OPTIMIZED=$ENABLE_OPTIMIZED ENABLE_DEBUG=$ENABLE_DEBUG DISABLE_ASSERTIONS=$DISABLE_ASSERTIONS REQUIRES_RTTI=$REQUIRES_RTTI SOLVERS=$SOLVERS GTEST_VERSION=$GTEST_VERSION UCLIBC_VERSION=$UCLIBC_VERSION STP_VERSION=$STP_VERSION MINISAT_VERSION=$MINISAT_VERSION Z3_VERSION=$Z3_VERSION SQLITE_VERSION=$SQLITE_VERSION ./scripts/build/build.sh klee --install-system-deps diff --git a/configs/options.json b/configs/options.json new file mode 100644 index 0000000000..32f20ebbf7 --- /dev/null +++ b/configs/options.json @@ -0,0 +1,52 @@ +{ + "inputs" : { + "buildPath" : "", + "sarifTracesFilePath" : ".json", + "bytecodeFilePath" : ".bc", + "maxTime" : "240", + "maxSolverTime" : "5", + "maxForks" : "200", + "maxSymAlloc" : "32", + "SymbolicAllocationThreshold" : "2048", + "minNumberElements" : "4", + "maxCycles" : 10, + "useSymbolicSizeLI" : false, + "writeKpaths": false + }, + "configurations": [ + { + "program": "${buildPath}/bin/klee", + "args": [ + "--use-guided-search=error", + "--mock-external-calls", + "--posix-runtime", + "--check-out-of-memory", + "--suppress-external-warnings", + "--libc=klee", + "--skip-not-lazy-initialized", + "--external-calls=all", + "--output-source=false", + "--output-istats=false", + "--output-stats=false", + "--max-time=${maxTime}s", + "--max-sym-alloc=${maxSymAlloc}", + "--max-forks=${maxForks}", + "--max-solver-time=${maxSolverTime}s", + "--mock-all-externals", + "--smart-resolve-entry-function", + "--extern-calls-can-return-null", + "--align-symbolic-pointers=false", + "--write-no-tests", + "--write-kpaths=${writeKpaths}", + "--use-lazy-initialization=only", + "--min-number-elements-li=${minNumberElements}", + "--use-sym-size-li=${useSymbolicSizeLI}", + "--max-cycles-before-stuck=${maxCycles}", + "--rewrite-equalities=simple", + "--symbolic-allocation-threshold=${SymbolicAllocationThreshold}", + "--analysis-reproduce=${sarifTracesFilePath}", + "${bytecodeFilePath}" + ] + } + ] +} \ No newline at end of file diff --git a/include/klee/ADT/Ticker.h b/include/klee/ADT/Ticker.h new file mode 100644 index 0000000000..9736c9c3b5 --- /dev/null +++ b/include/klee/ADT/Ticker.h @@ -0,0 +1,33 @@ +// -*- C++ -*- +#ifndef KLEE_TICKER_H +#define KLEE_TICKER_H + +#include + +class Ticker { + std::vector ticks; + unsigned index = 0; + unsigned counter = 0; + +public: + Ticker(std::vector ticks) : ticks(ticks) {} + + unsigned getCurrent() { + unsigned current = index; + counter += 1; + if (counter == ticks[index]) { + index = (index + 1) % ticks.size(); + counter = 0; + } + return current; + } + + void moveToNext() { + if (counter != 0) { + index = (index + 1) % ticks.size(); + counter = 0; + } + } +}; + +#endif diff --git a/include/klee/Core/Interpreter.h b/include/klee/Core/Interpreter.h index a7cde8600a..9b3a91c7f2 100644 --- a/include/klee/Core/Interpreter.h +++ b/include/klee/Core/Interpreter.h @@ -56,9 +56,15 @@ class InterpreterHandler { virtual void incPathsCompleted() = 0; virtual void incPathsExplored(std::uint32_t num = 1) = 0; + virtual void incSummarizedLocations() = 0; - virtual void processTestCase(const ExecutionState &state, const char *err, - const char *suffix) = 0; + virtual void processTestCase(const ExecutionState &state, const char *message, + const char *suffix, bool isError = false) = 0; +}; + +enum class ExecutionKind { + Forward, + Bidirectional }; class Interpreter { diff --git a/include/klee/Core/TargetedExecutionReporter.h b/include/klee/Core/TargetedExecutionReporter.h index 87bc69928d..9fa0e771c7 100644 --- a/include/klee/Core/TargetedExecutionReporter.h +++ b/include/klee/Core/TargetedExecutionReporter.h @@ -30,7 +30,7 @@ ty min(ty left, ty right); }; // namespace confidence void reportFalsePositive(confidence::ty confidence, - const std::set &errors, unsigned id, + const std::vector &errors, unsigned id, std::string whatToIncrease); } // namespace klee diff --git a/include/klee/Core/TerminationTypes.h b/include/klee/Core/TerminationTypes.h index 96e6507dfa..b44158382a 100644 --- a/include/klee/Core/TerminationTypes.h +++ b/include/klee/Core/TerminationTypes.h @@ -20,7 +20,9 @@ TTYPE(MaxDepth, 3U, "early") \ TTYPE(OutOfMemory, 4U, "early") \ TTYPE(OutOfStackMemory, 5U, "early") \ - MARK(EARLY, 5U) \ + TTYPE(MaxCycles, 6U, "early") \ + TTYPE(MissedAllTargets, 7U, "miss_all_targets.early") \ + MARK(EARLY, 7U) \ TTYPE(Solver, 8U, "solver.err") \ MARK(SOLVERERR, 8U) \ TTYPE(Abort, 10U, "abort.err") \ @@ -33,7 +35,7 @@ TTYPE(ReadOnly, 17U, "read_only.err") \ TTYPE(ReportError, 18U, "report_error.err") \ TTYPE(UndefinedBehavior, 19U, "undefined_behavior.err") \ - TTYPE(InternalOutOfMemory, 20U, "out_of_memory.er") \ + TTYPE(InternalOutOfMemory, 20U, "out_of_memory.err") \ MARK(PROGERR, 20U) \ TTYPE(User, 23U, "user.err") \ MARK(USERERR, 23U) \ @@ -60,6 +62,7 @@ enum Reason { MaxInstructions, MaxSteppedInstructions, MaxTime, + MaxCycles, CovCheck, NoMoreStates, ReachedTarget, diff --git a/include/klee/Expr/Assignment.h b/include/klee/Expr/Assignment.h index a31ed3c22b..ad48af0baa 100644 --- a/include/klee/Expr/Assignment.h +++ b/include/klee/Expr/Assignment.h @@ -66,6 +66,7 @@ class Assignment { bindings_ty::const_iterator begin() const { return bindings.begin(); } bindings_ty::const_iterator end() const { return bindings.end(); } + bool isEmpty() { return begin() == end(); } std::vector keys() const; std::vector> values() const; diff --git a/include/klee/Expr/Constraints.h b/include/klee/Expr/Constraints.h index a14b9e191e..319d4513b3 100644 --- a/include/klee/Expr/Constraints.h +++ b/include/klee/Expr/Constraints.h @@ -73,8 +73,9 @@ class ConstraintSet { class PathConstraints { public: - using ordered_constraints_ty = - std::map; + using ordered_constraints_ty = std::vector>; + using path_ordered_constraints_ty = + std::map; void advancePath(KInstruction *ki); void advancePath(const Path &path); @@ -90,7 +91,7 @@ class PathConstraints { const ConstraintSet &cs() const; const Path &path() const; const ExprHashMap &indexes() const; - const ordered_constraints_ty &orderedCS() const; + const path_ordered_constraints_ty &orderedCS() const; static PathConstraints concat(const PathConstraints &l, const PathConstraints &r); @@ -100,7 +101,7 @@ class PathConstraints { constraints_ty _original; ConstraintSet constraints; ExprHashMap pathIndexes; - ordered_constraints_ty orderedConstraints; + path_ordered_constraints_ty orderedConstraints; ExprHashMap _simplificationMap; }; diff --git a/include/klee/Expr/Expr.h b/include/klee/Expr/Expr.h index f57e908d79..188c2cb7c9 100644 --- a/include/klee/Expr/Expr.h +++ b/include/klee/Expr/Expr.h @@ -123,7 +123,7 @@ class Expr { }; static ExprCacheSet cachedExpressions; - static ref createCachedExpr(const ref &e); + static ref createCachedExpr(ref e); bool isCached = false; bool toBeCleared = false; @@ -321,6 +321,9 @@ class Expr { /// isZero - Is this a constant zero. bool isZero() const; + /// isZero - Is this a constant one. + bool isOne() const; + /// isTrue - Is this the true expression. bool isTrue() const; @@ -391,7 +394,7 @@ struct Expr::CreateArg { // Comparison operators inline bool operator==(const Expr &lhs, const Expr &rhs) { - return lhs.compare(rhs) == 0; + return lhs.equals(rhs); } inline bool operator<(const Expr &lhs, const Expr &rhs) { @@ -1553,6 +1556,12 @@ inline bool Expr::isZero() const { return false; } +inline bool Expr::isOne() const { + if (const ConstantExpr *CE = dyn_cast(this)) + return CE->isOne(); + return false; +} + inline bool Expr::isTrue() const { assert(getWidth() == Expr::Bool && "Invalid isTrue() call!"); if (const ConstantExpr *CE = dyn_cast(this)) diff --git a/include/klee/Expr/ExprEvaluator.h b/include/klee/Expr/ExprEvaluator.h index 93a78eb82b..cafecaa45f 100644 --- a/include/klee/Expr/ExprEvaluator.h +++ b/include/klee/Expr/ExprEvaluator.h @@ -17,15 +17,16 @@ namespace klee { class ExprEvaluator : public ExprVisitor { protected: Action evalRead(const UpdateList &ul, unsigned index); - Action visitRead(const ReadExpr &re); - Action visitExpr(const Expr &e); + Action visitRead(const ReadExpr &re) override; + Action visitSelect(const SelectExpr &se) override; + Action visitExpr(const Expr &e) override; Action protectedDivOperation(const BinaryExpr &e); - Action visitUDiv(const UDivExpr &e); - Action visitSDiv(const SDivExpr &e); - Action visitURem(const URemExpr &e); - Action visitSRem(const SRemExpr &e); - Action visitExprPost(const Expr &e); + Action visitUDiv(const UDivExpr &e) override; + Action visitSDiv(const SDivExpr &e) override; + Action visitURem(const URemExpr &e) override; + Action visitSRem(const SRemExpr &e) override; + Action visitExprPost(const Expr &e) override; public: ExprEvaluator() {} diff --git a/include/klee/Expr/ExprPPrinter.h b/include/klee/Expr/ExprPPrinter.h index 333159f6e5..9e86dec869 100644 --- a/include/klee/Expr/ExprPPrinter.h +++ b/include/klee/Expr/ExprPPrinter.h @@ -11,6 +11,7 @@ #define KLEE_EXPRPPRINTER_H #include "klee/Expr/Expr.h" +#include "klee/Expr/Lemma.h" namespace llvm { class raw_ostream; @@ -69,6 +70,8 @@ class ExprPPrinter { const Array *const *evalArraysBegin = 0, const Array *const *evalArraysEnd = 0, bool printArrayDecls = true); + + static void printLemma(llvm::raw_ostream &os, const Lemma &l); }; } // namespace klee diff --git a/include/klee/Expr/ExprVisitor.h b/include/klee/Expr/ExprVisitor.h index 8808f63add..15c9741272 100644 --- a/include/klee/Expr/ExprVisitor.h +++ b/include/klee/Expr/ExprVisitor.h @@ -137,10 +137,9 @@ class ExprVisitor { virtual Action visitFNeg(const FNegExpr &); virtual Action visitFRint(const FRintExpr &); -protected: - VisitorHash visited; - private: + typedef ExprHashMap> visited_ty; + visited_ty visited; bool recursive; ref visitActual(const ref &e); diff --git a/include/klee/Expr/Lemma.h b/include/klee/Expr/Lemma.h new file mode 100644 index 0000000000..e5119b39e6 --- /dev/null +++ b/include/klee/Expr/Lemma.h @@ -0,0 +1,63 @@ +// -*- C++ -*- +#ifndef KLEE_LEMMA_H +#define KLEE_LEMMA_H + +#include "klee/Core/Interpreter.h" +#include "klee/Expr/ExprHashMap.h" +#include "klee/Expr/Path.h" +#include "klee/Module/KModule.h" + +#include +#include +#include + +namespace klee { + +struct Lemma { + /// @brief Required by klee::ref-managed objects + class ReferenceCounter _refCount; + + Path path; + ExprOrderedSet constraints; + + Lemma(Path path, ExprOrderedSet constraints) + : path(path), constraints(constraints) {} + + bool operator==(const Lemma &other) { + return this->path == other.path && this->constraints == other.constraints; + } + + bool operator!=(const Lemma &other) { return !(*this == other); } + + ref asExpr(); + + int compare(const Lemma &b) const { + if (path == b.path && constraints == b.constraints) { + return 0; + } + return (path < b.path || (path == b.path && constraints < b.constraints)) + ? -1 + : 1; + } +}; + +class Summary { +public: + void addLemma(ref lemma); + void dumpToFile(KModule *km); + void readFromFile(KModule *km, ArrayCache *cache); + + Summary(InterpreterHandler *ih) : ih(ih) {} + +private: + std::set> lemmas; + std::set> dumped; + + std::string getFilename(); + + InterpreterHandler *ih; +}; + +}; // namespace klee + +#endif diff --git a/include/klee/Expr/Parser/Lexer.h b/include/klee/Expr/Parser/Lexer.h index 8b3fe53a30..96134657d7 100644 --- a/include/klee/Expr/Parser/Lexer.h +++ b/include/klee/Expr/Parser/Lexer.h @@ -31,6 +31,7 @@ struct Token { KWArray, ///< 'array' KWFalse, ///< 'false' KWQuery, ///< 'query' + KWLemma, ///< 'lemma' KWPath, ///< 'path' KWReserved, ///< fp[0-9]+([.].*)?, i[0-9]+ KWSymbolic, ///< 'symbolic' diff --git a/include/klee/Expr/Parser/Parser.h b/include/klee/Expr/Parser/Parser.h index 0f6ec291c0..44f8fc2693 100644 --- a/include/klee/Expr/Parser/Parser.h +++ b/include/klee/Expr/Parser/Parser.h @@ -48,12 +48,13 @@ class Decl { ExprVarDeclKind, VersionVarDeclKind, QueryCommandDeclKind, + LemmaCommandDeclKind, - DeclKindLast = QueryCommandDeclKind, + DeclKindLast = LemmaCommandDeclKind, VarDeclKindFirst = ExprVarDeclKind, VarDeclKindLast = VersionVarDeclKind, CommandDeclKindFirst = QueryCommandDeclKind, - CommandDeclKindLast = QueryCommandDeclKind + CommandDeclKindLast = LemmaCommandDeclKind }; private: @@ -187,6 +188,24 @@ class QueryCommand : public CommandDecl { static bool classof(const QueryCommand *) { return true; } }; +class LemmaCommand : public CommandDecl { +public: + ExprOrderedSet constraints; + Path path; + + LemmaCommand(ExprOrderedSet constraints, Path path) + : CommandDecl(LemmaCommandDeclKind), constraints(constraints), + path(path) {} + + // TODO + virtual void dump() {} + + static bool classof(const Decl *D) { + return D->getKind() == LemmaCommandDeclKind; + } + static bool classof(const LemmaCommand *) { return true; } +}; + /// Parser - Public interface for parsing a .kquery language file. class Parser { protected: diff --git a/include/klee/Module/CodeGraphDistance.h b/include/klee/Module/CodeGraphDistance.h index 014ed02e34..ab04663a12 100644 --- a/include/klee/Module/CodeGraphDistance.h +++ b/include/klee/Module/CodeGraphDistance.h @@ -65,6 +65,12 @@ class CodeGraphDistance { getSortedDistance(KFunction *kf); const std::vector> & getSortedBackwardDistance(KFunction *kf); + + KBlock *getNearestJoinBlock(KBlock *kb); + KBlock *getNearestJoinOrCallBlock(KBlock *kb); + + std::vector> + dismantle(KBlock *from, std::vector to); }; } // namespace klee diff --git a/include/klee/Module/SarifReport.h b/include/klee/Module/SarifReport.h index 40ea69d88f..bc42292dfb 100644 --- a/include/klee/Module/SarifReport.h +++ b/include/klee/Module/SarifReport.h @@ -62,7 +62,7 @@ static const char *ReachWithErrorNames[] = { }; const char *getErrorString(ReachWithError error); -std::string getErrorsString(const std::set &errors); +std::string getErrorsString(const std::vector &errors); struct FunctionInfo; struct KBlock; @@ -258,7 +258,7 @@ struct Result { std::vector> locations; std::vector> metadatas; unsigned id; - std::set errors; + std::vector errors; }; struct SarifReport { diff --git a/include/klee/Module/Target.h b/include/klee/Module/Target.h index fd43f27023..b6ba0ba01d 100644 --- a/include/klee/Module/Target.h +++ b/include/klee/Module/Target.h @@ -10,6 +10,8 @@ #ifndef KLEE_TARGET_H #define KLEE_TARGET_H +#include "klee/Module/TargetHash.h" + #include "klee/ADT/RNG.h" #include "klee/ADT/Ref.h" #include "klee/Module/KModule.h" @@ -28,10 +30,6 @@ #include namespace klee { -struct EquivTargetCmp; -struct TargetHash; -struct TargetCmp; - using nonstd::optional; struct ErrorLocation { @@ -43,29 +41,58 @@ struct ErrorLocation { struct Target { private: - typedef std::unordered_set - EquivTargetHashSet; - typedef std::unordered_set TargetHashSet; - static EquivTargetHashSet cachedTargets; - static TargetHashSet targets; KBlock *block; - std::set + std::vector errors; // None - if it is not terminated in error trace unsigned id; // 0 - if it is not terminated in error trace optional loc; // TODO(): only for check in reportTruePositive - explicit Target(const std::set &_errors, unsigned _id, + explicit Target(const std::vector &_errors, unsigned _id, optional _loc, KBlock *_block) - : block(_block), errors(_errors), id(_id), loc(_loc) {} + : block(_block), errors(_errors), id(_id), loc(_loc) { + std::sort(errors.begin(), errors.end()); + } + + static ref createCachedTarget(ref target); + +protected: + friend class ref; + friend class ref; + + struct TargetHash { + unsigned operator()(Target *const t) const { return t->hash(); } + }; + + struct TargetCmp { + bool operator()(Target *const a, Target *const b) const { + return a->equals(*b); + } + }; - static ref getFromCacheOrReturn(Target *target); + typedef std::unordered_set CacheType; + + struct TargetCacheSet { + CacheType cache; + ~TargetCacheSet() { + while (cache.size() != 0) { + ref tmp = *cache.begin(); + tmp->isCached = false; + cache.erase(cache.begin()); + } + } + }; + + static TargetCacheSet cachedTargets; + bool isCached = false; + bool toBeCleared = false; + + /// @brief Required by klee::ref-managed objects + mutable class ReferenceCounter _refCount; public: bool isReported = false; - /// @brief Required by klee::ref-managed objects - class ReferenceCounter _refCount; - static ref create(const std::set &_errors, + static ref create(const std::vector &_errors, unsigned _id, optional _loc, KBlock *_block); static ref create(KBlock *_block); @@ -88,13 +115,21 @@ struct Target { unsigned hash() const { return reinterpret_cast(block); } - const std::set &getErrors() const { return errors; } - bool isThatError(ReachWithError err) const { return errors.count(err) != 0; } + const std::vector &getErrors() const { return errors; } + bool isThatError(ReachWithError err) const { + return std::find(errors.begin(), errors.end(), err) != errors.end(); + } bool shouldFailOnThisTarget() const { - return errors.count(ReachWithError::None) == 0; + return std::find(errors.begin(), errors.end(), ReachWithError::None) == + errors.end(); } - bool isTheSameAsIn(KInstruction *instr) const; + bool isTheSameAsIn(const KInstruction *instr) const; + + /// returns true if we cannot use CFG reachability checks + /// from instr children to this target + /// to avoid solver calls + bool mustVisitForkBranches(KInstruction *instr) const; unsigned getId() const { return id; } diff --git a/include/klee/Module/TargetForest.h b/include/klee/Module/TargetForest.h index 91cd347309..3a03b7cb84 100644 --- a/include/klee/Module/TargetForest.h +++ b/include/klee/Module/TargetForest.h @@ -26,32 +26,120 @@ #include namespace klee { -struct RefTargetHash; -struct RefTargetCmp; -struct TargetsHistoryHash; -struct EquivTargetsHistoryCmp; -struct TargetsHistoryCmp; -struct UnorderedTargetsSetHash; -struct UnorderedTargetsSetCmp; -struct EquivUnorderedTargetsSetCmp; -struct RefUnorderedTargetsSetHash; -struct RefUnorderedTargetsSetCmp; +struct TargetHash; +struct TargetCmp; + +class TargetsHistory { +private: + unsigned hashValue; + unsigned sizeValue; + + explicit TargetsHistory(ref _target, + ref _visitedTargets) + : target(_target), visitedTargets(_visitedTargets) { + computeHash(); + computeSize(); + } + + void computeHash() { + unsigned res = 0; + if (target) { + res = target->hash() * Expr::MAGIC_HASH_CONSTANT; + } + if (visitedTargets) { + res ^= visitedTargets->hash() * Expr::MAGIC_HASH_CONSTANT; + } + hashValue = res; + } + + void computeSize() { + unsigned res = 0; + if (target) { + ++res; + } + if (visitedTargets) { + res += visitedTargets->size(); + } + sizeValue = res; + } + +protected: + friend class ref; + friend class ref; + + struct TargetsHistoryHash { + unsigned operator()(TargetsHistory *const t) const { return t->hash(); } + }; + + struct TargetsHistoryCmp { + bool operator()(TargetsHistory *const a, TargetsHistory *const b) const { + return a->equals(*b); + } + }; + + typedef std::unordered_set + CacheType; + + struct TargetHistoryCacheSet { + CacheType cache; + ~TargetHistoryCacheSet() { + while (cache.size() != 0) { + ref tmp = *cache.begin(); + tmp->isCached = false; + cache.erase(cache.begin()); + } + } + }; + + static TargetHistoryCacheSet cachedHistories; + bool isCached = false; + bool toBeCleared = false; + + /// @brief Required by klee::ref-managed objects + mutable class ReferenceCounter _refCount; -class TargetForest { public: - using TargetsSet = - std::unordered_set, RefTargetHash, RefTargetCmp>; + const ref target; + const ref visitedTargets; + + static ref create(ref _target, + ref _visitedTargets); + static ref create(ref _target); + static ref create(); + + ref add(ref _target) { + return TargetsHistory::create(_target, this); + } + + unsigned hash() const { return hashValue; } + unsigned size() const { return sizeValue; } + + int compare(const TargetsHistory &h) const; + bool equals(const TargetsHistory &h) const; + + void dump() const; + + ~TargetsHistory(); +}; + +using TargetHistoryTargetPair = + std::pair, ref>; +class TargetForest { +public: class UnorderedTargetsSet { public: static ref create(const ref &target); - static ref create(const TargetsSet &targets); + static ref create(const TargetHashSet &targets); ~UnorderedTargetsSet(); - std::size_t hash() const { return hashValue; } - bool operator==(const UnorderedTargetsSet &other) const { - return targetsVec == other.targetsVec; - } + unsigned hash() const { return hashValue; } + bool operator==(const UnorderedTargetsSet &other) const; + bool operator<(const UnorderedTargetsSet &other) const; + + int compare(const UnorderedTargetsSet &h) const; + bool equals(const UnorderedTargetsSet &h) const; const std::vector> &getTargets() const { return targetsVec; } @@ -66,54 +154,77 @@ class TargetForest { /// @brief Required by klee::ref-managed objects class ReferenceCounter _refCount; - private: - explicit UnorderedTargetsSet(const ref &target); - explicit UnorderedTargetsSet(const TargetsSet &targets); - static ref create(UnorderedTargetsSet *vec); + protected: + struct UnorderedTargetsSetHash { + unsigned operator()(UnorderedTargetsSet *const t) const { + return t->hash(); + } + }; + + struct UnorderedTargetsSetCmp { + bool operator()(UnorderedTargetsSet *const a, + UnorderedTargetsSet *const b) const { + return a->equals(*b); + } + }; - typedef std::unordered_set - EquivUnorderedTargetsSetHashSet; typedef std::unordered_set - UnorderedTargetsSetHashSet; + CacheType; + + struct UnorderedTargetsSetCacheSet { + CacheType cache; + ~UnorderedTargetsSetCacheSet() { + while (cache.size() != 0) { + ref tmp = *cache.begin(); + tmp->isCached = false; + cache.erase(cache.begin()); + } + } + }; + + static UnorderedTargetsSetCacheSet cachedVectors; + bool isCached = false; + bool toBeCleared = false; + + private: + explicit UnorderedTargetsSet(const ref &target); + explicit UnorderedTargetsSet(const TargetHashSet &targets); + static ref create(ref vec); - static EquivUnorderedTargetsSetHashSet cachedVectors; - static UnorderedTargetsSetHashSet vectors; std::vector> targetsVec; - std::size_t hashValue; + unsigned hashValue; - void sortAndComputeHash(); + unsigned sortAndComputeHash(); }; - struct RefUnorderedTargetsSetHash { + struct UnorderedTargetsSetHash { unsigned operator()(const ref &t) const { return t->hash(); } }; - struct RefUnorderedTargetsSetCmp { + struct UnorderedTargetsSetCmp { bool operator()(const ref &a, const ref &b) const { - return a.get() == b.get(); + return a == b; } }; private: using TargetToStateSetMap = std::unordered_map, std::unordered_set, - RefTargetHash, RefTargetCmp>; + TargetHash, TargetCmp>; class Layer { using InternalLayer = std::unordered_map, ref, - RefUnorderedTargetsSetHash, - RefUnorderedTargetsSetCmp>; + UnorderedTargetsSetHash, UnorderedTargetsSetCmp>; InternalLayer forest; using TargetsToVector = std::unordered_map< ref, - std::unordered_set, RefUnorderedTargetsSetHash, - RefUnorderedTargetsSetCmp>, - RefTargetHash, RefTargetCmp>; + std::unordered_set, UnorderedTargetsSetHash, + UnorderedTargetsSetCmp>, + TargetHash, TargetCmp>; TargetsToVector targetsToVector; /// @brief Confidence in % that this layer (i.e., parent target node) can be @@ -124,6 +235,7 @@ class TargetForest { confidence::ty confidence) : forest(forest), targetsToVector(targetsToVector), confidence(confidence) {} + explicit Layer(const Layer *layer) : Layer(layer->forest, layer->targetsToVector, layer->confidence) {} explicit Layer(const ref layer) : Layer(layer.get()) {} @@ -136,11 +248,6 @@ class TargetForest { return confidence::min(parentConfidence, confidence); } - void collectHowManyEventsInTracesWereReached( - std::unordered_map> - &traceToEventCount, - unsigned reached, unsigned total) const; - public: using iterator = TargetsToVector::const_iterator; @@ -166,8 +273,8 @@ class TargetForest { Layer *replaceChildWith( ref child, const std::unordered_set, - RefUnorderedTargetsSetHash, - RefUnorderedTargetsSetCmp> &other) const; + UnorderedTargetsSetHash, + UnorderedTargetsSetCmp> &other) const; Layer *replaceChildWith(ref child, Layer *other) const; Layer *removeChild(ref child) const; Layer *addChild(ref child) const; @@ -175,13 +282,19 @@ class TargetForest { ref leaf) const; Layer *blockLeafInChild(ref child, ref leaf) const; Layer *blockLeaf(ref leaf) const; - bool allNodesRefCountOne() const; + TargetHashSet getTargets() const { + TargetHashSet targets; + for (auto &targetVect : targetsToVector) { + targets.insert(targetVect.first); + } + return targets; + } + void dump(unsigned n) const; void addLeafs( std::vector, confidence::ty>> &leafs, confidence::ty parentConfidence) const; void propagateConfidenceToChildren(); - void addTargetWithConfidence(ref target, confidence::ty confidence); ref deepCopy(); Layer *copy(); void divideConfidenceBy(unsigned factor); @@ -190,11 +303,6 @@ class TargetForest { confidence::ty getConfidence() const { return getConfidence(confidence::MaxConfidence); } - void collectHowManyEventsInTracesWereReached( - std::unordered_map> - &traceToEventCount) const { - collectHowManyEventsInTracesWereReached(traceToEventCount, 0, 0); - } void addTrace( const Result &result, @@ -204,71 +312,13 @@ class TargetForest { ref forest; - bool allNodesRefCountOne() const; - -public: - class History { - private: - typedef std::unordered_set - EquivTargetsHistoryHashSet; - typedef std::unordered_set - TargetsHistoryHashSet; - - static EquivTargetsHistoryHashSet cachedHistories; - static TargetsHistoryHashSet histories; - unsigned hashValue; - - explicit History(ref _target, ref _visitedTargets) - : target(_target), visitedTargets(_visitedTargets) { - computeHash(); - } - - public: - const ref target; - const ref visitedTargets; - - static ref create(ref _target, - ref _visitedTargets); - static ref create(ref _target); - static ref create(); - - ref add(ref _target) { - return History::create(_target, this); - } - - unsigned hash() const { return hashValue; } - - int compare(const History &h) const; - bool equals(const History &h) const; - - void computeHash() { - unsigned res = 0; - if (target) { - res = target->hash() * Expr::MAGIC_HASH_CONSTANT; - } - if (visitedTargets) { - res ^= visitedTargets->hash() * Expr::MAGIC_HASH_CONSTANT; - } - hashValue = res; - } - - void dump() const; - - ~History(); - - /// @brief Required by klee::ref-managed objects - class ReferenceCounter _refCount; - }; - private: - ref history; + ref history; KFunction *entryFunction; public: /// @brief Required by klee::ref-managed objects class ReferenceCounter _refCount; - unsigned getDebugReferenceCount() { return forest->_refCount.getCount(); } KFunction *getEntryFunction() { return entryFunction; } void addTrace( @@ -279,7 +329,7 @@ class TargetForest { } TargetForest(ref layer, KFunction *entryFunction) - : forest(layer), history(History::create()), + : forest(layer), history(TargetsHistory::create()), entryFunction(entryFunction) {} TargetForest() : TargetForest(new Layer(), nullptr) {} TargetForest(KFunction *entryFunction) @@ -298,14 +348,13 @@ class TargetForest { void add(ref); void remove(ref); void blockIn(ref, ref); - const ref getHistory() { return history; }; - const ref getTargets() { return forest; }; + void block(const ref &); + const ref getHistory() { return history; }; + const ref getTopLayer() { return forest; }; + const TargetHashSet getTargets() const { return forest->getTargets(); } void dump() const; std::vector, confidence::ty>> leafs() const; - void addTargetWithConfidence(ref target, confidence::ty confidence) { - forest->addTargetWithConfidence(target, confidence); - } ref deepCopy(); void divideConfidenceBy(unsigned factor) { forest->divideConfidenceBy(factor); @@ -313,45 +362,29 @@ class TargetForest { void divideConfidenceBy(const TargetToStateSetMap &reachableStatesOfTarget) { forest = forest->divideConfidenceBy(reachableStatesOfTarget); } - void collectHowManyEventsInTracesWereReached( - std::unordered_map> - &traceToEventCount) const { - forest->collectHowManyEventsInTracesWereReached(traceToEventCount); - } }; struct TargetsHistoryHash { - unsigned operator()(const TargetForest::History *t) const { - return t ? t->hash() : 0; - } + unsigned operator()(const ref &t) const { return t->hash(); } }; struct TargetsHistoryCmp { - bool operator()(const TargetForest::History *a, - const TargetForest::History *b) const { + bool operator()(const ref &a, + const ref &b) const { return a == b; } }; -struct EquivTargetsHistoryCmp { - bool operator()(const TargetForest::History *a, - const TargetForest::History *b) const { - if (a == NULL || b == NULL) - return false; - return a->compare(*b) == 0; - } -}; - -struct RefTargetsHistoryHash { - unsigned operator()(const ref &t) const { - return t->hash(); +struct TargetHistoryTargetHash { + unsigned operator()(const TargetHistoryTargetPair &t) const { + return t.first->hash() * Expr::MAGIC_HASH_CONSTANT + t.second->hash(); } }; -struct RefTargetsHistoryCmp { - bool operator()(const ref &a, - const ref &b) const { - return a.get() == b.get(); +struct TargetHistoryTargetCmp { + bool operator()(const TargetHistoryTargetPair &a, + const TargetHistoryTargetPair &b) const { + return a.first == b.first && a.second == b.second; } }; diff --git a/include/klee/Module/TargetHash.h b/include/klee/Module/TargetHash.h index eccac7db77..2ceb35c747 100644 --- a/include/klee/Module/TargetHash.h +++ b/include/klee/Module/TargetHash.h @@ -14,6 +14,7 @@ #include #include +#include namespace llvm { class BasicBlock; @@ -23,22 +24,10 @@ namespace klee { struct Target; struct TargetHash { - unsigned operator()(const Target *t) const; -}; - -struct TargetCmp { - bool operator()(const Target *a, const Target *b) const; -}; - -struct EquivTargetCmp { - bool operator()(const Target *a, const Target *b) const; -}; - -struct RefTargetHash { unsigned operator()(const ref &t) const; }; -struct RefTargetCmp { +struct TargetCmp { bool operator()(const ref &a, const ref &b) const; }; @@ -48,11 +37,16 @@ struct TransitionHash { std::size_t operator()(const Transition &p) const; }; -struct RefTargetLess { +struct TargetLess { bool operator()(const ref &a, const ref &b) const { - return a.get() < b.get(); + return a < b; } }; +template +using TargetHashMap = std::unordered_map, T, TargetHash, TargetCmp>; + +using TargetHashSet = std::unordered_set, TargetHash, TargetCmp>; + } // namespace klee #endif /* KLEE_TARGETHASH_H */ diff --git a/include/klee/Solver/Common.h b/include/klee/Solver/Common.h index 48049b847c..19733696ca 100644 --- a/include/klee/Solver/Common.h +++ b/include/klee/Solver/Common.h @@ -15,7 +15,6 @@ #define KLEE_COMMON_H #include "klee/Solver/AddressGenerator.h" -#include "klee/Solver/ConcretizationManager.h" #include "klee/Solver/Solver.h" #include @@ -30,7 +29,6 @@ Solver *constructSolverChain(Solver *coreSolver, std::string querySMT2LogPath, std::string baseSolverQuerySMT2LogPath, std::string queryKQueryLogPath, std::string baseSolverQueryKQueryLogPath, - ConcretizationManager *concretizationManager, AddressGenerator *addressGenerator); } // namespace klee diff --git a/include/klee/Solver/ConcretizationManager.h b/include/klee/Solver/ConcretizationManager.h deleted file mode 100644 index 47c99d28f0..0000000000 --- a/include/klee/Solver/ConcretizationManager.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef KLEE_CONCRETIZATIONMANAGER_H -#define KLEE_CONCRETIZATIONMANAGER_H - -#include "klee/ADT/MapOfSets.h" -#include "klee/Expr/Assignment.h" -#include "klee/Expr/Constraints.h" -#include - -namespace klee { -struct CacheEntry; -struct CacheEntryHash; -struct Query; - -class ConcretizationManager { - using cs_entry = constraints_ty; - using sm_entry = symcretes_ty; - -private: - struct CacheEntry { - public: - CacheEntry(const cs_entry &c, const sm_entry &s, ref q) - : constraints(c), symcretes(s), query(q) {} - - CacheEntry(const CacheEntry &ce) - : constraints(ce.constraints), symcretes(ce.symcretes), - query(ce.query) {} - - cs_entry constraints; - sm_entry symcretes; - ref query; - - bool operator==(const CacheEntry &b) const { - return constraints == b.constraints && symcretes == b.symcretes && - query == b.query; - } - }; - - struct CacheEntryHash { - public: - unsigned operator()(const CacheEntry &ce) const { - unsigned result = ce.query->hash(); - - for (auto const &constraint : ce.constraints) { - result ^= constraint->hash(); - } - - return result; - } - }; - - struct CacheEntryCmp { - public: - bool operator()(const CacheEntry &ce1, const CacheEntry &ce2) const { - return ce1 == ce2; - } - }; - - typedef std::unordered_map - concretizations_map; - concretizations_map concretizations; - bool simplifyExprs; - -public: - ConcretizationManager(bool simplifyExprs) : simplifyExprs(simplifyExprs) {} - - std::pair get(const ConstraintSet &set, ref query); - bool contains(const ConstraintSet &set, ref query); - void add(const Query &q, const Assignment &assign); -}; - -}; // namespace klee - -#endif /* KLEE_CONCRETIZATIONMANAGER_H */ diff --git a/include/klee/Solver/Solver.h b/include/klee/Solver/Solver.h index bbedd2d604..e9f21b7c89 100644 --- a/include/klee/Solver/Solver.h +++ b/include/klee/Solver/Solver.h @@ -12,7 +12,6 @@ #include "klee/ADT/SparseStorage.h" #include "klee/Expr/Expr.h" -#include "klee/Solver/ConcretizationManager.h" #include "klee/Solver/SolverCmdLine.h" #include "klee/Solver/SolverUtil.h" #include "klee/System/Time.h" @@ -258,9 +257,7 @@ Solver *createDummySolver(); // Create a solver based on the supplied ``CoreSolverType``. Solver *createCoreSolver(CoreSolverType cst); -Solver *createConcretizingSolver(Solver *s, - ConcretizationManager *concretizationManager, - AddressGenerator *addressGenerator); +Solver *createConcretizingSolver(Solver *s, AddressGenerator *addressGenerator); } // namespace klee #endif /* KLEE_SOLVER_H */ diff --git a/include/klee/Solver/SolverCmdLine.h b/include/klee/Solver/SolverCmdLine.h index b22aab4e93..7b8889dfd4 100644 --- a/include/klee/Solver/SolverCmdLine.h +++ b/include/klee/Solver/SolverCmdLine.h @@ -70,7 +70,7 @@ extern llvm::cl::opt DebugCrossCheckCoreSolverWith; extern llvm::cl::opt ProduceUnsatCore; -extern llvm::cl::opt SymbolicAllocationThreshhold; +extern llvm::cl::opt SymbolicAllocationThreshold; #ifdef ENABLE_METASMT diff --git a/include/klee/Support/DebugFlags.h b/include/klee/Support/DebugFlags.h new file mode 100644 index 0000000000..49ef7e89ed --- /dev/null +++ b/include/klee/Support/DebugFlags.h @@ -0,0 +1,27 @@ +#ifndef KLEE_DEBUGFLAGS_H +#define KLEE_DEBUGFLAGS_H + +#include "llvm/Support/CommandLine.h" + +using namespace llvm; + +namespace klee { + +enum class DebugPrint { + Forward, + Backward, + Init, + Reached, + Lemma, + RootPob, + ClosePob, + Conflict, + PathForest +}; + +extern cl::bits debugPrints; +extern cl::bits debugConstraints; + +}; + +#endif /* KLEE_DEBUGFLAGS_H */ diff --git a/lib/Core/AddressSpace.cpp b/lib/Core/AddressSpace.cpp index 7612ead8f9..40820835fa 100644 --- a/lib/Core/AddressSpace.cpp +++ b/lib/Core/AddressSpace.cpp @@ -274,12 +274,8 @@ int AddressSpace::checkPointerInObject(ExecutionState &state, // mustBeTrue before mayBeTrue for the first result. easy // to add I just want to have a nice symbolic test case first. const MemoryObject *mo = op.first; - ref base = state.isGEPExpr(p) ? state.gepExprBases.at(p).first : p; ref inBounds = mo->getBoundsCheckPointer(p); - inBounds = AndExpr::create( - inBounds, - mo->getBoundsCheckPointer(base)); // FIXME: remove when segmented memory - // model will be implemented + bool mayBeTrue; if (!solver->mayBeTrue(state.constraints.cs(), inBounds, mayBeTrue, state.queryMetaData)) { diff --git a/lib/Core/BackwardSearcher.cpp b/lib/Core/BackwardSearcher.cpp new file mode 100644 index 0000000000..c1b1765299 --- /dev/null +++ b/lib/Core/BackwardSearcher.cpp @@ -0,0 +1,46 @@ +#include "BackwardSearcher.h" +#include "ExecutionState.h" +#include "SearcherUtil.h" +#include +#include +#include +#include + +namespace klee { +bool RecencyRankedSearcher::empty() { return propagations.empty(); } + +Propagation RecencyRankedSearcher::selectAction() { + unsigned leastUsed = UINT_MAX; + Propagation chosen = {0, 0}; + for (auto i : propagations) { + if (i.pob->propagationCount[i.state] < leastUsed) { + leastUsed = i.pob->propagationCount[i.state]; + chosen = i; + if (leastUsed == 0) { + break; + } + } + } + + assert(chosen.pob && chosen.state); + + return chosen; +} + +void RecencyRankedSearcher::update(const propagations_ty &addedPropagations, + const propagations_ty &removedPropagations) { + for (auto propagation : removedPropagations) { + propagations.remove(propagation); + pausedPropagations.remove(propagation); + } + for (auto propagation : addedPropagations) { + if (propagation.pob->propagationCount[propagation.state] <= + maxPropagations) { + propagations.push_back(propagation); + } else { + pausedPropagations.push_back(propagation); + } + } +} + +}; // namespace klee diff --git a/lib/Core/BackwardSearcher.h b/lib/Core/BackwardSearcher.h new file mode 100644 index 0000000000..eb0892f042 --- /dev/null +++ b/lib/Core/BackwardSearcher.h @@ -0,0 +1,41 @@ +// -*- C++ -*- +#ifndef KLEE_BACKWARDSEARCHER_H +#define KLEE_BACKWARDSEARCHER_H + +#include "ExecutionState.h" +#include "ProofObligation.h" +#include "SearcherUtil.h" + +#include + +namespace klee { + +class BackwardSearcher { +public: + virtual ~BackwardSearcher() = default; + virtual Propagation selectAction() = 0; + virtual void update(const propagations_ty &addedPropagations, + const propagations_ty &removedPropagations) = 0; + virtual bool empty() = 0; +}; + +class RecencyRankedSearcher : public BackwardSearcher { +private: + unsigned maxPropagations; + +public: + RecencyRankedSearcher(unsigned _maxPropagation) + : maxPropagations(_maxPropagation) {} + Propagation selectAction() override; + void update(const propagations_ty &addedPropagations, + const propagations_ty &removedPropagations) override; + bool empty() override; + +private: + std::list propagations; + std::list pausedPropagations; +}; + +}; // namespace klee + +#endif diff --git a/lib/Core/BidirectionalSearcher.cpp b/lib/Core/BidirectionalSearcher.cpp new file mode 100644 index 0000000000..d09ac55249 --- /dev/null +++ b/lib/Core/BidirectionalSearcher.cpp @@ -0,0 +1,162 @@ +#include "BidirectionalSearcher.h" +#include "BackwardSearcher.h" +#include "ExecutionState.h" +#include "Executor.h" +#include "Initializer.h" +#include "ObjectManager.h" +#include "ProofObligation.h" +#include "Searcher.h" +#include "SearcherUtil.h" +#include "UserSearcher.h" +#include "klee/Core/Interpreter.h" +#include "klee/Module/KModule.h" +#include "klee/Support/ErrorHandling.h" +#include "klee/Support/OptionCategories.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/IR/Instructions.h" + +#include +#include +#include +#include + +#include + +namespace {} // namespace + +namespace klee { + +BidirectionalSearcher::StepKind BidirectionalSearcher::selectStep() { + size_t initial_choice = ticker.getCurrent(); + size_t choice = initial_choice; + + do { + switch (choice) { + case 0: { + if (!forward->empty()) { + return StepKind::Forward; + } + break; + } + case 1: { + if (!branch->empty()) { + return StepKind::Branch; + } + break; + } + case 2: { + if (!backward->empty()) { + return StepKind::Backward; + } + break; + } + case 3: { + if (!initializer->empty()) { + return StepKind::Initialize; + } + break; + } + } + ticker.moveToNext(); + choice = ticker.getCurrent(); + } while (choice != initial_choice); + + assert(0 && "Empty searcher queried for an action"); +} + +ref BidirectionalSearcher::selectAction() { + ref action; + + while (action.isNull()) { + switch (selectStep()) { + + case StepKind::Forward: { + auto &state = forward->selectState(); + action = new ForwardAction(&state); + break; + } + + case StepKind::Branch: { + auto &state = branch->selectState(); + action = new ForwardAction(&state); + break; + } + + case StepKind::Backward: { + auto propagation = backward->selectAction(); + action = new BackwardAction(propagation); + break; + } + + case StepKind::Initialize: { + auto initAndTargets = initializer->selectAction(); + action = + new InitializeAction(initAndTargets.first, initAndTargets.second); + break; + } + } + } + return action; +} + +void BidirectionalSearcher::update(ref e) { + if (auto states = dyn_cast(e)) { + if (states->isolated) { + branch->update(states->modified, states->added, states->removed); + } else { + forward->update(states->modified, states->added, states->removed); + } + } else if (auto props = dyn_cast(e)) { + backward->update(props->added, props->removed); + } else if (auto pobs = dyn_cast(e)) { + initializer->update(pobs->added, pobs->removed); + } else if (auto conflicts = dyn_cast(e)) { + for (auto conflict : conflicts->conflicts) { + initializer->addConflictInit(conflict->conflict, conflict->target); + } + } else { + assert(0 && "Unknown event"); + } +} + +bool BidirectionalSearcher::empty() { + return forward->empty() && branch->empty() && backward->empty() && + initializer->empty(); +} + +BidirectionalSearcher::BidirectionalSearcher(Searcher *_forward, + Searcher *_branch, + BackwardSearcher *_backward, + Initializer *_initializer) + : ticker({80, 10, 5, 5}), forward(_forward), branch(_branch), + backward(_backward), initializer(_initializer) {} + +BidirectionalSearcher::~BidirectionalSearcher() { + delete forward; + delete branch; + delete backward; + delete initializer; +} + +ref ForwardOnlySearcher::selectAction() { + return new ForwardAction(&searcher->selectState()); +} + +bool ForwardOnlySearcher::empty() { return searcher->empty(); } + +void ForwardOnlySearcher::update(ref e) { + if (auto statesEvent = dyn_cast(e)) { + assert(!statesEvent->isolated); + searcher->update(statesEvent->modified, statesEvent->added, + statesEvent->removed); + } +} + +ForwardOnlySearcher::ForwardOnlySearcher(Searcher *_searcher) { + searcher = _searcher; +} + +ForwardOnlySearcher::~ForwardOnlySearcher() { delete searcher; } + +} // namespace klee diff --git a/lib/Core/BidirectionalSearcher.h b/lib/Core/BidirectionalSearcher.h new file mode 100644 index 0000000000..6e539c7cb7 --- /dev/null +++ b/lib/Core/BidirectionalSearcher.h @@ -0,0 +1,68 @@ +#ifndef KLEE_BIDIRECTIONALSEARCHER_H +#define KLEE_BIDIRECTIONALSEARCHER_H + +#include "BackwardSearcher.h" +#include "Initializer.h" +#include "ProofObligation.h" +#include "Searcher.h" +#include "SearcherUtil.h" + +#include "ObjectManager.h" + +#include "klee/ADT/Ticker.h" +#include "klee/Module/KModule.h" +#include +#include +#include + +namespace klee { + +class IBidirectionalSearcher : public Subscriber { +public: + virtual ref selectAction() = 0; + virtual bool empty() = 0; + virtual ~IBidirectionalSearcher() {} +}; + +class BidirectionalSearcher : public IBidirectionalSearcher { + enum class StepKind { Forward, Branch, Backward, Initialize }; + +public: + ref selectAction() override; + void update(ref e) override; + bool empty() override; + + // Assumes ownership + explicit BidirectionalSearcher(Searcher *_forward, Searcher *_branch, + BackwardSearcher *_backward, + Initializer *_initializer); + + ~BidirectionalSearcher() override; + +private: + Ticker ticker; + + Searcher *forward; + Searcher *branch; + BackwardSearcher *backward; + Initializer *initializer; + +private: + StepKind selectStep(); +}; + +class ForwardOnlySearcher : public IBidirectionalSearcher { +public: + ref selectAction() override; + void update(ref) override; + bool empty() override; + explicit ForwardOnlySearcher(Searcher *searcher); + ~ForwardOnlySearcher() override; + +private: + Searcher *searcher; +}; + +} // namespace klee + +#endif diff --git a/lib/Core/CMakeLists.txt b/lib/Core/CMakeLists.txt index 202fa2fcec..9c46608319 100644 --- a/lib/Core/CMakeLists.txt +++ b/lib/Core/CMakeLists.txt @@ -9,26 +9,35 @@ klee_add_component(kleeCore AddressManager.cpp AddressSpace.cpp + BackwardSearcher.cpp + BidirectionalSearcher.cpp CallPathManager.cpp + Composer.cpp Context.cpp CoreStats.cpp CXXTypeSystem/CXXTypeManager.cpp + DistanceCalculator.cpp ExecutionState.cpp Executor.cpp ExecutorUtil.cpp ExternalDispatcher.cpp ImpliedValue.cpp + Initializer.cpp Memory.cpp MemoryManager.cpp + ObjectManager.cpp PForest.cpp PTree.cpp + ProofObligation.cpp Searcher.cpp SeedInfo.cpp + SeedMap.cpp SpecialFunctionHandler.cpp StatsTracker.cpp TargetCalculator.cpp TargetedExecutionReporter.cpp TargetedExecutionManager.cpp + TargetManager.cpp TimingSolver.cpp TypeManager.cpp UserSearcher.cpp diff --git a/lib/Core/Composer.cpp b/lib/Core/Composer.cpp new file mode 100644 index 0000000000..419f065bd8 --- /dev/null +++ b/lib/Core/Composer.cpp @@ -0,0 +1,383 @@ +#include "Composer.h" + +#include "klee/Expr/ArrayExprVisitor.h" + +#include "TypeManager.h" + +using namespace klee; +using namespace llvm; + +bool ComposeHelper::collectMemoryObjects( + ExecutionState &state, ref address, KType *targetType, + KInstruction *target, ref &guard, + std::vector> &resolveConditions, + std::vector> &unboundConditions, + std::vector &resolvedMemoryObjects) { + bool mayBeOutOfBound = true; + bool hasLazyInitialized = false; + bool incomplete = false; + std::vector mayBeResolvedMemoryObjects; + + if (!resolveMemoryObjects(state, address, targetType, target, 0, + mayBeResolvedMemoryObjects, mayBeOutOfBound, + hasLazyInitialized, incomplete)) { + return false; + } + + ref checkOutOfBounds; + if (!checkResolvedMemoryObjects( + state, address, target, 0, mayBeResolvedMemoryObjects, + hasLazyInitialized, resolvedMemoryObjects, resolveConditions, + unboundConditions, checkOutOfBounds, mayBeOutOfBound)) { + return false; + } + + bool mayBeInBounds; + if (!makeGuard(state, resolveConditions, unboundConditions, checkOutOfBounds, + hasLazyInitialized, guard, mayBeInBounds)) { + return false; + } + return true; +} + +bool ComposeHelper::tryResolveAddress(ExecutionState &state, ref address, + std::pair, ref> &result) { + ref guard; + std::vector> resolveConditions; + std::vector> unboundConditions; + std::vector resolvedMemoryObjects; + KType *targetType = executor->typeSystemManager->getUnknownType(); + KInstruction *target = nullptr; + + if (!collectMemoryObjects(state, address, targetType, target, guard, + resolveConditions, unboundConditions, + resolvedMemoryObjects)) { + return false; + } + + result.first = guard; + if (resolvedMemoryObjects.size() > 0) { + ref resultAddress = + state.addressSpace + .findObject(resolvedMemoryObjects.at(resolveConditions.size() - 1)) + .first->getBaseExpr(); + for (unsigned int i = 0; i < resolveConditions.size(); ++i) { + unsigned int index = resolveConditions.size() - 1 - i; + const MemoryObject *mo = + state.addressSpace.findObject(resolvedMemoryObjects.at(index)).first; + resultAddress = SelectExpr::create(resolveConditions[index], + mo->getBaseExpr(), resultAddress); + } + result.second = resultAddress; + } else { + result.second = Expr::createPointer(0); + } + return true; +} + +bool ComposeHelper::tryResolveSize(ExecutionState &state, ref address, + std::pair, ref> &result) { + ref guard; + std::vector> resolveConditions; + std::vector> unboundConditions; + std::vector resolvedMemoryObjects; + KType *targetType = executor->typeSystemManager->getUnknownType(); + KInstruction *target = nullptr; + + if (!collectMemoryObjects(state, address, targetType, target, guard, + resolveConditions, unboundConditions, + resolvedMemoryObjects)) { + return false; + } + + result.first = guard; + if (resolvedMemoryObjects.size() > 0) { + ref resultSize = + state.addressSpace + .findObject(resolvedMemoryObjects.at(resolveConditions.size() - 1)) + .first->getSizeExpr(); + for (unsigned int i = 0; i < resolveConditions.size(); ++i) { + unsigned int index = resolveConditions.size() - 1 - i; + const MemoryObject *mo = + state.addressSpace.findObject(resolvedMemoryObjects.at(index)).first; + resultSize = SelectExpr::create(resolveConditions[index], + mo->getSizeExpr(), resultSize); + } + result.second = resultSize; + } else { + result.second = Expr::createPointer(0); + } + return true; +} + +bool ComposeHelper::tryResolveContent( + ExecutionState &state, ref base, ref offset, Expr::Width type, + unsigned size, + std::pair, std::vector, ref>>> + &result) { + bool mayBeOutOfBound = true; + bool hasLazyInitialized = false; + bool incomplete = false; + std::vector mayBeResolvedMemoryObjects; + KType *baseType = executor->typeSystemManager->getUnknownType(); + KInstruction *target = nullptr; + + if (!resolveMemoryObjects(state, base, baseType, target, 0, + mayBeResolvedMemoryObjects, mayBeOutOfBound, + hasLazyInitialized, incomplete)) { + return false; + } + + ref checkOutOfBounds; + std::vector> resolveConditions; + std::vector> unboundConditions; + std::vector resolvedMemoryObjects; + ref address = AddExpr::create(base, offset); + + if (!checkResolvedMemoryObjects( + state, address, target, size, mayBeResolvedMemoryObjects, + hasLazyInitialized, resolvedMemoryObjects, resolveConditions, + unboundConditions, checkOutOfBounds, mayBeOutOfBound)) { + return false; + } + + ref guard; + std::vector resolveConcretizations; + bool mayBeInBounds; + + if (!collectConcretizations(state, resolveConditions, unboundConditions, + resolvedMemoryObjects, checkOutOfBounds, + hasLazyInitialized, guard, resolveConcretizations, + mayBeInBounds)) { + return false; + } + + std::vector> resolvedObjectStates; + collectObjectStates(state, address, type, size, resolvedMemoryObjects, + resolveConcretizations, resolvedObjectStates); + + result.first = guard; + for (unsigned int i = 0; i < resolvedObjectStates.size(); ++i) { + result.second.push_back( + std::make_pair(resolveConditions.at(i), resolvedObjectStates.at(i))); + } + return true; +} + +std::pair, ref> ComposeHelper::fillLazyInitializationAddress( + ExecutionState &state, + ref lazyInitializationAddressSource, + ref pointer, Expr::Width width) { + std::pair, ref> result; + if (!tryResolveAddress(state, pointer, result)) { + return std::make_pair(Expr::createFalse(), ConstantExpr::create(0, width)); + } + return result; +} + +std::pair, ref> ComposeHelper::fillLazyInitializationSize( + ExecutionState &state, + ref lazyInitializationSizeSource, + ref pointer, Expr::Width width) { + std::pair, ref> result; + if (!tryResolveSize(state, pointer, result)) { + return std::make_pair(Expr::createFalse(), ConstantExpr::create(0, width)); + } + return result; +} + +std::pair, std::vector, ref>>> +ComposeHelper::fillLazyInitializationContent( + ExecutionState &state, + ref lazyInitializationContentSource, + ref pointer, unsigned concreteSize, ref offset, + Expr::Width width) { + std::pair, std::vector, ref>>> + result; + if (!tryResolveContent(state, pointer, offset, width, concreteSize, result)) { + return std::make_pair( + Expr::createFalse(), + std::vector, ref>>()); + } + return result; +} + +ExprVisitor::Action ComposeVisitor::visitRead(const ReadExpr &read) { + return Action::changeTo(processRead(read.updates.root, read.updates, + read.index, read.getWidth())); +} + +ExprVisitor::Action ComposeVisitor::visitConcat(const ConcatExpr &concat) { + const ReadExpr *base = ArrayExprHelper::hasOrderedReads(concat); + if (base) { + return Action::changeTo(processRead(base->updates.root, base->updates, + base->index, concat.getWidth())); + } else { + return Action::doChildren(); + } +} + +ExprVisitor::Action ComposeVisitor::visitSelect(const SelectExpr &select) { + return Action::changeTo( + processSelect(select.cond, select.trueExpr, select.falseExpr)); +} + +void ComposeVisitor::shareUpdates(ref os, + const UpdateList &updates) { + std::stack> forward; + + for (auto it = updates.head; !it.isNull(); it = it->next) { + forward.push(it); + } + + while (!forward.empty()) { + ref UNode = forward.top(); + forward.pop(); + ref newIndex = visit(UNode->index); + ref newValue = visit(UNode->value); + os->write(newIndex, newValue); + } +} + +ref ComposeVisitor::processRead(const Array *root, + const UpdateList &updates, + ref index, Expr::Width width) { + index = visit(index); + ref size = visit(root->getSize()); + Expr::Width concreteSizeInBits = 0; + concreteSizeInBits = width; + unsigned concreteSize = concreteSizeInBits / CHAR_BIT; + concreteSize += (concreteSizeInBits % CHAR_BIT == 0) ? 0 : 1; + ref res; + switch (root->source->getKind()) { + case SymbolicSource::Kind::Argument: + case SymbolicSource::Kind::Instruction: { + assert(updates.getSize() == 0); + return helper.fillValue(state, cast(root->source), size, + width); + } + + case SymbolicSource::Kind::MakeSymbolic: { + ref os = helper.fillMakeSymbolic( + state, cast(root->source), size, concreteSize); + shareUpdates(os, updates); + return os->read(index, width); + } + case SymbolicSource::Kind::Constant: { + ref os = + helper.fillConstant(state, cast(root->source), size); + shareUpdates(os, updates); + return os->read(index, width); + } + case SymbolicSource::Kind::SymbolicSizeConstant: { + ref os = helper.fillSymbolicSizeConstant( + state, cast(root->source), size, + concreteSize); + shareUpdates(os, updates); + return os->read(index, width); + } + case SymbolicSource::Kind::SymbolicSizeConstantAddress: { + assert(updates.getSize() == 0); + ref address = helper.fillSymbolicSizeConstantAddress( + state, cast(root->source), size, + width); + if (!state.constraints.isSymcretized(address)) { + std::pair, ref> sizeAddress = + helper.getSymbolicSizeConstantSizeAddressPair( + state, cast(root->source), + size, width); + ref oldAddress = Expr::createTempRead(root, width); + address = helper.fillSizeAddressSymcretes( + state, oldAddress, sizeAddress.first, sizeAddress.second); + } + return address; + } + case SymbolicSource::Kind::LazyInitializationAddress: { + assert(updates.getSize() == 0); + ref pointer = + visit(cast(root->source)->pointer); + std::pair, ref> guardedAddress = + helper.fillLazyInitializationAddress( + state, cast(root->source), pointer, + width); + safetyConstraints.insert(guardedAddress.first); + return guardedAddress.second; + } + case SymbolicSource::Kind::LazyInitializationSize: { + assert(updates.getSize() == 0); + ref pointer = + visit(cast(root->source)->pointer); + std::pair, ref> guardedSize = + helper.fillLazyInitializationSize( + state, cast(root->source), pointer, + width); + safetyConstraints.insert(guardedSize.first); + return guardedSize.second; + } + case SymbolicSource::Kind::LazyInitializationContent: { + ref pointer = + visit(cast(root->source)->pointer); + std::pair, std::vector, ref>>> + guardedContent = helper.fillLazyInitializationContent( + state, cast(root->source), pointer, + concreteSize, Expr::createZExtToPointerWidth(index), width); + safetyConstraints.insert(guardedContent.first); + + std::vector> results; + std::vector> guards; + for (unsigned int i = 0; i < guardedContent.second.size(); ++i) { + ref guard = guardedContent.second[i].first; + ref os = guardedContent.second[i].second; + shareUpdates(os, updates); + + ref result = os->read(index, width); + results.push_back(result); + guards.push_back(guard); + } + + ref result; + if (results.size() > 0) { + result = results[guards.size() - 1]; + for (unsigned int i = 0; i < guards.size(); ++i) { + unsigned int index = guards.size() - 1 - i; + result = SelectExpr::create(guards[index], results[index], result); + } + } else { + result = ConstantExpr::create(0, width); + } + + return result; + } + + default: + assert(0 && "not implemented"); + } + return res; +} + +ref ComposeVisitor::processSelect(ref cond, ref trueExpr, + ref falseExpr) { + cond = visit(cond); + if (ConstantExpr *CE = dyn_cast(cond)) { + return CE->isTrue() ? visit(trueExpr) : visit(falseExpr); + } + PartialValidity res; + if (!helper.evaluate(state, cond, res, state.queryMetaData)) { + safetyConstraints.insert(Expr::createFalse()); + return ConstantExpr::create(0, trueExpr->getWidth()); + } + switch (res) { + case PValidity::MustBeTrue: + return visit(trueExpr); + + case PValidity::MustBeFalse: + return visit(falseExpr); + + default: { + trueExpr = visit(trueExpr); + falseExpr = visit(falseExpr); + ref result = SelectExpr::create(cond, trueExpr, falseExpr); + return result; + } + } +} diff --git a/lib/Core/Composer.h b/lib/Core/Composer.h new file mode 100644 index 0000000000..77baa97bc2 --- /dev/null +++ b/lib/Core/Composer.h @@ -0,0 +1,224 @@ +#ifndef KLEE_COMPOSER_H +#define KLEE_COMPOSER_H + +#include "klee/Expr/Expr.h" +#include "klee/Expr/ExprHashMap.h" +#include "klee/Expr/ExprVisitor.h" +#include "klee/Module/KModule.h" + +#include "ExecutionState.h" +#include "Executor.h" +#include "Memory.h" +#include "TimingSolver.h" + +namespace klee { +struct ComposeHelper { +private: + Executor *executor; + +public: + ComposeHelper(Executor *_executor) : executor(_executor) {} + + bool getResponse(const ExecutionState &state, ref expr, + ref &queryResult, + SolverQueryMetaData &metaData) { + executor->solver->setTimeout(executor->coreSolverTimeout); + bool success = executor->solver->getResponse( + state.constraints.cs(), expr, queryResult, state.queryMetaData); + executor->solver->setTimeout(time::Span()); + return success; + } + + bool evaluate(const ExecutionState &state, ref expr, + PartialValidity &res, SolverQueryMetaData &metaData) { + executor->solver->setTimeout(executor->coreSolverTimeout); + bool success = executor->solver->evaluate(state.constraints.cs(), expr, res, + state.queryMetaData); + executor->solver->setTimeout(time::Span()); + return success; + } + + bool resolveMemoryObjects(ExecutionState &state, ref address, + KType *targetType, KInstruction *target, + unsigned bytes, + std::vector &mayBeResolvedMemoryObjects, + bool &mayBeOutOfBound, bool &mayLazyInitialize, + bool &incomplete) { + return executor->resolveMemoryObjects( + state, address, targetType, target, bytes, mayBeResolvedMemoryObjects, + mayBeOutOfBound, mayLazyInitialize, incomplete); + } + + bool checkResolvedMemoryObjects( + ExecutionState &state, ref address, KInstruction *target, + unsigned bytes, const std::vector &mayBeResolvedMemoryObjects, + bool hasLazyInitialized, std::vector &resolvedMemoryObjects, + std::vector> &resolveConditions, + std::vector> &unboundConditions, ref &checkOutOfBounds, + bool &mayBeOutOfBound) { + return executor->checkResolvedMemoryObjects( + state, address, target, bytes, mayBeResolvedMemoryObjects, + hasLazyInitialized, resolvedMemoryObjects, resolveConditions, + unboundConditions, checkOutOfBounds, mayBeOutOfBound); + } + + bool makeGuard(ExecutionState &state, + const std::vector> &resolveConditions, + const std::vector> &unboundConditions, + ref checkOutOfBounds, bool hasLazyInitialized, + ref &guard, bool &mayBeInBounds) { + return executor->makeGuard(state, resolveConditions, unboundConditions, + checkOutOfBounds, hasLazyInitialized, guard, + mayBeInBounds); + } + + bool collectConcretizations(ExecutionState &state, + const std::vector> &resolveConditions, + const std::vector> &unboundConditions, + const std::vector &resolvedMemoryObjects, + ref checkOutOfBounds, + bool hasLazyInitialized, ref &guard, + std::vector &resolveConcretizations, + bool &mayBeInBounds) { + return executor->collectConcretizations( + state, resolveConditions, unboundConditions, resolvedMemoryObjects, + checkOutOfBounds, hasLazyInitialized, guard, resolveConcretizations, + mayBeInBounds); + } + + bool collectMemoryObjects(ExecutionState &state, ref address, + KType *targetType, KInstruction *target, + ref &guard, + std::vector> &resolveConditions, + std::vector> &unboundConditions, + std::vector &resolvedMemoryObjects); + + void collectReads(ExecutionState &state, ref address, KType *targetType, + Expr::Width type, unsigned bytes, + const std::vector &resolvedMemoryObjects, + const std::vector &resolveConcretizations, + std::vector> &results) { + executor->collectReads(state, address, targetType, type, bytes, + resolvedMemoryObjects, resolveConcretizations, + results); + } + + void + collectObjectStates(ExecutionState &state, ref address, + Expr::Width type, unsigned bytes, + const std::vector &resolvedMemoryObjects, + const std::vector &resolveConcretizations, + std::vector> &results) { + executor->collectObjectStates(state, address, type, bytes, + resolvedMemoryObjects, resolveConcretizations, + results); + } + + bool tryResolveAddress(ExecutionState &state, ref address, + std::pair, ref> &result); + bool tryResolveSize(ExecutionState &state, ref address, + std::pair, ref> &result); + bool tryResolveContent( + ExecutionState &state, ref address, ref offset, + Expr::Width type, unsigned size, + std::pair, std::vector, ref>>> + &result); + + ref fillValue(ExecutionState &state, ref valueSource, + ref size, Expr::Width width) { + return executor->fillValue(state, valueSource, size, width); + } + ref fillMakeSymbolic(ExecutionState &state, + ref makeSymbolicSource, + ref size, unsigned concreteSize) { + return executor->fillMakeSymbolic(state, makeSymbolicSource, size, + concreteSize); + } + ref fillConstant(ExecutionState &state, + ref constanSource, + ref size) { + return executor->fillConstant(state, constanSource, size); + } + ref fillSymbolicSizeConstant( + ExecutionState &state, + ref symbolicSizeConstantSource, + ref size, unsigned concreteSize) { + return executor->fillSymbolicSizeConstant(state, symbolicSizeConstantSource, + size, concreteSize); + } + ref fillSymbolicSizeConstantAddress( + ExecutionState &state, + ref symbolicSizeConstantAddressSource, + ref size, Expr::Width width) { + return executor->fillSymbolicSizeConstantAddress( + state, symbolicSizeConstantAddressSource, size, width); + } + + std::pair, ref> getSymbolicSizeConstantSizeAddressPair( + ExecutionState &state, + ref symbolicSizeConstantAddressSource, + ref size, Expr::Width width) { + return executor->getSymbolicSizeConstantSizeAddressPair( + state, symbolicSizeConstantAddressSource, size, width); + } + + ref fillSizeAddressSymcretes(ExecutionState &state, + ref oldAddress, ref newAddress, + ref size) { + return executor->fillSizeAddressSymcretes(state, oldAddress, newAddress, + size); + } + + std::pair, ref> fillLazyInitializationAddress( + ExecutionState &state, + ref lazyInitializationAddressSource, + ref pointer, Expr::Width width); + std::pair, ref> fillLazyInitializationSize( + ExecutionState &state, + ref lazyInitializationSizeSource, + ref pointer, Expr::Width width); + std::pair, std::vector, ref>>> + fillLazyInitializationContent( + ExecutionState &state, + ref lazyInitializationContentSource, + ref pointer, unsigned concreteSize, ref offset, + Expr::Width width); +}; + +class ComposeVisitor : public ExprVisitor { +private: + const ExecutionState &original; + ComposeHelper helper; + ExprOrderedSet safetyConstraints; + +public: + ExecutionState &state; + + ComposeVisitor() = delete; + explicit ComposeVisitor(const ExecutionState &_state, ComposeHelper _helper) + : ExprVisitor(false), original(_state), helper(_helper), + state(*original.copy()) {} + ~ComposeVisitor() { delete &state; } + + std::pair, ref> compose(ref expr) { + ref result = visit(expr); + ref safetyCondition = Expr::createTrue(); + for (auto expr : safetyConstraints) { + safetyCondition = AndExpr::create(safetyCondition, expr); + } + return std::make_pair(safetyCondition, result); + } + +private: + ExprVisitor::Action visitRead(const ReadExpr &) override; + ExprVisitor::Action visitConcat(const ConcatExpr &concat) override; + ExprVisitor::Action visitSelect(const SelectExpr &) override; + ref processRead(const Array *root, const UpdateList &updates, + ref index, Expr::Width width); + ref processSelect(ref cond, ref trueExpr, + ref falseExpr); + void shareUpdates(ref, const UpdateList &updates); +}; +} // namespace klee + +#endif // KLEE_COMPOSITION_H diff --git a/lib/Core/DistanceCalculator.cpp b/lib/Core/DistanceCalculator.cpp new file mode 100644 index 0000000000..8702ee57b0 --- /dev/null +++ b/lib/Core/DistanceCalculator.cpp @@ -0,0 +1,254 @@ +//===-- DistanceCalculator.cpp --------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DistanceCalculator.h" +#include "ExecutionState.h" +#include "klee/Module/CodeGraphDistance.h" +#include "klee/Module/KInstruction.h" +#include "klee/Module/Target.h" + +#include + +using namespace llvm; +using namespace klee; + +bool DistanceResult::operator<(const DistanceResult &b) const { + if (isInsideFunction != b.isInsideFunction) + return isInsideFunction; + if (result == WeightResult::Continue && b.result == WeightResult::Continue) + return weight < b.weight; + return result < b.result; +} + +std::string DistanceResult::toString() const { + std::ostringstream out; + out << "(" << (int)!isInsideFunction << ", " << (int)result << ", " << weight + << ")"; + return out.str(); +} + +unsigned DistanceCalculator::SpeculativeState::computeHash() { + unsigned res = + (reinterpret_cast(kb) * SymbolicSource::MAGIC_HASH_CONSTANT) + + kind; + res = (res * SymbolicSource::MAGIC_HASH_CONSTANT) + error; + hashValue = res; + return hashValue; +} + +DistanceResult DistanceCalculator::getDistance(const ExecutionState &state, + ref target) { + return getDistance(state.prevPC, state.pc, state.stack.uniqueFrames(), + state.error, target); +} + +DistanceResult DistanceCalculator::getDistance(KBlock *kb, TargetKind kind, + ReachWithError error, + ref target) { + SpeculativeState specState(kb, kind, error); + if (distanceResultCache.count(target) == 0 || + distanceResultCache.at(target).count(specState) == 0) { + auto result = computeDistance(kb, kind, error, target); + distanceResultCache[target][specState] = result; + } + return distanceResultCache.at(target).at(specState); +} + +DistanceResult DistanceCalculator::computeDistance(KBlock *kb, TargetKind kind, + ReachWithError error, + ref target) const { + const auto &distanceToTargetFunction = + codeGraphDistance.getBackwardDistance(target->getBlock()->parent); + weight_type weight = 0; + WeightResult res = Miss; + bool isInsideFunction = true; + switch (kind) { + case LocalTarget: + res = tryGetTargetWeight(kb, weight, target); + break; + + case PreTarget: + res = tryGetPreTargetWeight(kb, weight, distanceToTargetFunction, target); + isInsideFunction = false; + break; + + case PostTarget: + res = tryGetPostTargetWeight(kb, weight, target); + isInsideFunction = false; + break; + + case NoneTarget: + break; + } + if (Done == res && target->shouldFailOnThisTarget()) { + if (!target->isThatError(error)) { + res = Continue; + } + } + return DistanceResult(res, weight, isInsideFunction); +} + +DistanceResult +DistanceCalculator::getDistance(const KInstruction *prevPC, + const KInstruction *pc, + const ExecutionStack::call_stack_ty &frames, + ReachWithError error, ref target) { + weight_type weight = 0; + + if (!target->shouldFailOnThisTarget() && target->atReturn()) { + if (prevPC->parent == target->getBlock() && + prevPC == target->getBlock()->getLastInstruction()) { + return DistanceResult(Done); + } else if (pc->parent == target->getBlock()) { + return DistanceResult(Continue); + } + } + + if (target->shouldFailOnThisTarget() && target->isTheSameAsIn(prevPC) && + target->isThatError(error)) { + return DistanceResult(Done); + } + + KBlock *kb = pc->parent; + const auto &distanceToTargetFunction = + codeGraphDistance.getBackwardDistance(target->getBlock()->parent); + unsigned int minCallWeight = UINT_MAX, minSfNum = UINT_MAX, sfNum = 0; + for (auto sfi = frames.rbegin(), sfe = frames.rend(); sfi != sfe; sfi++) { + unsigned callWeight; + if (distanceInCallGraph(sfi->kf, kb, callWeight, distanceToTargetFunction, + target)) { + callWeight *= 2; + callWeight += sfNum; + + if (callWeight < UINT_MAX) { + minCallWeight = callWeight; + minSfNum = sfNum; + } + } + + if (sfi->caller) { + kb = sfi->caller->parent; + } + sfNum++; + + if (minCallWeight < UINT_MAX) + break; + } + + TargetKind kind = NoneTarget; + if (minCallWeight == 0) { + kind = LocalTarget; + } else if (minSfNum == 0) { + kind = PreTarget; + } else if (minSfNum != UINT_MAX) { + kind = PostTarget; + } + + return getDistance(pc->parent, kind, error, target); +} + +bool DistanceCalculator::distanceInCallGraph( + KFunction *kf, KBlock *kb, unsigned int &distance, + const std::unordered_map + &distanceToTargetFunction, + ref target) const { + distance = UINT_MAX; + const std::unordered_map &dist = + codeGraphDistance.getDistance(kb); + KBlock *targetBB = target->getBlock(); + KFunction *targetF = targetBB->parent; + + if (kf == targetF && dist.count(targetBB) != 0) { + distance = 0; + return true; + } + + for (auto &kCallBlock : kf->kCallBlocks) { + if (dist.count(kCallBlock) != 0) { + for (auto &calledFunction : kCallBlock->calledFunctions) { + KFunction *calledKFunction = kf->parent->functionMap[calledFunction]; + if (distanceToTargetFunction.count(calledKFunction) != 0 && + distance > distanceToTargetFunction.at(calledKFunction) + 1) { + distance = distanceToTargetFunction.at(calledKFunction) + 1; + } + } + } + } + return distance != UINT_MAX; +} + +WeightResult +DistanceCalculator::tryGetLocalWeight(KBlock *kb, weight_type &weight, + const std::vector &localTargets, + ref target) const { + KFunction *currentKF = kb->parent; + KBlock *currentKB = kb; + const std::unordered_map &dist = + codeGraphDistance.getDistance(currentKB); + weight = UINT_MAX; + for (auto &end : localTargets) { + if (dist.count(end) > 0) { + unsigned int w = dist.at(end); + weight = std::min(w, weight); + } + } + + if (weight == UINT_MAX) + return Miss; + if (weight == 0) { + return Done; + } + + return Continue; +} + +WeightResult DistanceCalculator::tryGetPreTargetWeight( + KBlock *kb, weight_type &weight, + const std::unordered_map + &distanceToTargetFunction, + ref target) const { + KFunction *currentKF = kb->parent; + std::vector localTargets; + for (auto &kCallBlock : currentKF->kCallBlocks) { + for (auto &calledFunction : kCallBlock->calledFunctions) { + KFunction *calledKFunction = + currentKF->parent->functionMap[calledFunction]; + if (distanceToTargetFunction.count(calledKFunction) > 0) { + localTargets.push_back(kCallBlock); + } + } + } + + if (localTargets.empty()) + return Miss; + + WeightResult res = tryGetLocalWeight(kb, weight, localTargets, target); + return res == Done ? Continue : res; +} + +WeightResult +DistanceCalculator::tryGetPostTargetWeight(KBlock *kb, weight_type &weight, + ref target) const { + KFunction *currentKF = kb->parent; + std::vector &localTargets = currentKF->returnKBlocks; + + if (localTargets.empty()) + return Miss; + + WeightResult res = tryGetLocalWeight(kb, weight, localTargets, target); + return res == Done ? Continue : res; +} + +WeightResult DistanceCalculator::tryGetTargetWeight(KBlock *kb, + weight_type &weight, + ref target) const { + std::vector localTargets = {target->getBlock()}; + WeightResult res = tryGetLocalWeight(kb, weight, localTargets, target); + return res; +} diff --git a/lib/Core/DistanceCalculator.h b/lib/Core/DistanceCalculator.h new file mode 100644 index 0000000000..37830c967e --- /dev/null +++ b/lib/Core/DistanceCalculator.h @@ -0,0 +1,133 @@ +//===-- DistanceCalculator.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_DISTANCE_CALCULATOR_H +#define KLEE_DISTANCE_CALCULATOR_H + +#include "ExecutionState.h" + +namespace llvm { +class BasicBlock; +} // namespace llvm + +namespace klee { +class CodeGraphDistance; +struct Target; + +enum WeightResult : std::uint8_t { + Done = 0U, + Continue = 1U, + Miss = 2U, +}; + +using weight_type = unsigned; + +struct DistanceResult { + WeightResult result; + weight_type weight; + bool isInsideFunction; + + explicit DistanceResult(WeightResult result_ = WeightResult::Miss, + weight_type weight_ = 0, + bool isInsideFunction_ = true) + : result(result_), weight(weight_), isInsideFunction(isInsideFunction_){}; + + bool operator<(const DistanceResult &b) const; + + std::string toString() const; +}; + +class DistanceCalculator { +public: + explicit DistanceCalculator(CodeGraphDistance &codeGraphDistance_) + : codeGraphDistance(codeGraphDistance_) {} + + DistanceResult getDistance(const ExecutionState &es, ref target); + + DistanceResult getDistance(const KInstruction *prevPC, const KInstruction *pc, + const ExecutionStack::call_stack_ty &frames, + ReachWithError error, ref target); + +private: + enum TargetKind : std::uint8_t { + LocalTarget = 0U, + PreTarget = 1U, + PostTarget = 2U, + NoneTarget = 3U, + }; + + struct SpeculativeState { + private: + unsigned hashValue; + + unsigned computeHash(); + + public: + KBlock *kb; + TargetKind kind; + ReachWithError error; + SpeculativeState(KBlock *kb_, TargetKind kind_, ReachWithError error_) + : kb(kb_), kind(kind_), error(error_) { + computeHash(); + } + ~SpeculativeState() = default; + unsigned hash() const { return hashValue; } + }; + + struct SpeculativeStateHash { + bool operator()(const SpeculativeState &a) const { return a.hash(); } + }; + + struct SpeculativeStateCompare { + bool operator()(const SpeculativeState &a, + const SpeculativeState &b) const { + return a.kb == b.kb && a.error == b.error && a.kind == b.kind; + } + }; + + using SpeculativeStateToDistanceResultMap = + std::unordered_map; + using TargetToSpeculativeStateToDistanceResultMap = + std::unordered_map, SpeculativeStateToDistanceResultMap, + TargetHash, TargetCmp>; + + using StatesSet = std::unordered_set; + + CodeGraphDistance &codeGraphDistance; + TargetToSpeculativeStateToDistanceResultMap distanceResultCache; + StatesSet localStates; + + DistanceResult getDistance(KBlock *kb, TargetKind kind, ReachWithError error, + ref target); + + DistanceResult computeDistance(KBlock *kb, TargetKind kind, + ReachWithError error, + ref target) const; + + bool distanceInCallGraph(KFunction *kf, KBlock *kb, unsigned int &distance, + const std::unordered_map + &distanceToTargetFunction, + ref target) const; + WeightResult tryGetLocalWeight(KBlock *kb, weight_type &weight, + const std::vector &localTargets, + ref target) const; + WeightResult + tryGetPreTargetWeight(KBlock *kb, weight_type &weight, + const std::unordered_map + &distanceToTargetFunction, + ref target) const; + WeightResult tryGetTargetWeight(KBlock *kb, weight_type &weight, + ref target) const; + WeightResult tryGetPostTargetWeight(KBlock *kb, weight_type &weight, + ref target) const; +}; +} // namespace klee + +#endif /* KLEE_DISTANCE_CALCULATOR_H */ diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index 909d4b64f0..daecb3286f 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -36,12 +36,21 @@ using namespace llvm; using namespace klee; +namespace klee { +cl::opt MaxCyclesBeforeStuck( + "max-cycles-before-stuck", + cl::desc("Set target after after state visiting some basic block this " + "amount of times (default=10)."), + cl::init(10), cl::cat(TerminationCat)); +} + namespace { cl::opt UseGEPOptimization( "use-gep-opt", cl::init(true), cl::desc("Lazily initialize whole objects referenced by gep expressions " "instead of only the referenced parts (default=true)"), cl::cat(ExecCat)); + } // namespace /***/ @@ -50,37 +59,80 @@ std::uint32_t ExecutionState::nextID = 1; /***/ -StackFrame::StackFrame(KInstIterator _caller, KFunction *_kf) - : caller(_caller), kf(_kf), callPathNode(0), minDistToUncoveredOnReturn(0), - varargs(0) { +void ExecutionStack::pushFrame(KInstIterator caller, KFunction *kf) { + valueStack_.emplace_back(StackFrame(kf)); + if (std::find(callStack_.begin(), callStack_.end(), + CallStackFrame(caller, kf)) == callStack_.end()) { + uniqueFrames_.emplace_back(CallStackFrame(caller, kf)); + } + callStack_.emplace_back(CallStackFrame(caller, kf)); + infoStack_.emplace_back(InfoStackFrame(kf)); + ++stackBalance_; + assert(valueStack_.size() == callStack_.size()); + assert(valueStack_.size() == infoStack_.size()); +} + +void ExecutionStack::popFrame() { + assert(callStack_.size() > 0); + KInstIterator caller = callStack_.back().caller; + KFunction *kf = callStack_.back().kf; + valueStack_.pop_back(); + callStack_.pop_back(); + infoStack_.pop_back(); + auto it = std::find(callStack_.begin(), callStack_.end(), + CallStackFrame(caller, kf)); + if (it == callStack_.end()) { + uniqueFrames_.pop_back(); + } + --stackBalance_; + assert(valueStack_.size() == callStack_.size()); + assert(valueStack_.size() == infoStack_.size()); +} + +bool CallStackFrame::equals(const CallStackFrame &other) const { + return kf == other.kf && caller == other.caller; +} + +StackFrame::StackFrame(KFunction *kf) : kf(kf), 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++) + : kf(s.kf), allocas(s.allocas), varargs(s.varargs) { + locals = new Cell[kf->numRegisters]; + for (unsigned i = 0; i < kf->numRegisters; i++) locals[i] = s.locals[i]; } StackFrame::~StackFrame() { delete[] locals; } +CallStackFrame::CallStackFrame(const CallStackFrame &s) + : caller(s.caller), kf(s.kf) {} + +InfoStackFrame::InfoStackFrame(KFunction *kf) : kf(kf) {} + +InfoStackFrame::InfoStackFrame(const InfoStackFrame &s) + : kf(s.kf), callPathNode(s.callPathNode), + minDistToUncoveredOnReturn(s.minDistToUncoveredOnReturn) {} + /***/ ExecutionState::ExecutionState() : initPC(nullptr), pc(nullptr), prevPC(nullptr), incomingBBIndex(-1), depth(0), ptreeNode(nullptr), steppedInstructions(0), steppedMemoryInstructions(0), instsSinceCovNew(0), roundingMode(llvm::APFloat::rmNearestTiesToEven), coveredNew(false), - forkDisabled(false) { + forkDisabled(false), prevHistory_(TargetsHistory::create()), + history_(TargetsHistory::create()) { setID(); } ExecutionState::ExecutionState(KFunction *kf) - : initPC(kf->instructions), pc(initPC), prevPC(pc), - roundingMode(llvm::APFloat::rmNearestTiesToEven) { + : initPC(kf->instructions), pc(initPC), prevPC(pc), incomingBBIndex(-1), + depth(0), ptreeNode(nullptr), steppedInstructions(0), + steppedMemoryInstructions(0), instsSinceCovNew(0), + roundingMode(llvm::APFloat::rmNearestTiesToEven), coveredNew(false), + forkDisabled(false), prevHistory_(TargetsHistory::create()), + history_(TargetsHistory::create()) { pushFrame(nullptr, kf); setID(); } @@ -90,7 +142,8 @@ ExecutionState::ExecutionState(KFunction *kf, KBlock *kb) depth(0), ptreeNode(nullptr), steppedInstructions(0), steppedMemoryInstructions(0), instsSinceCovNew(0), roundingMode(llvm::APFloat::rmNearestTiesToEven), coveredNew(false), - forkDisabled(false) { + forkDisabled(false), prevHistory_(TargetsHistory::create()), + history_(TargetsHistory::create()) { pushFrame(nullptr, kf); setID(); } @@ -102,9 +155,9 @@ ExecutionState::~ExecutionState() { ExecutionState::ExecutionState(const ExecutionState &state) : initPC(state.initPC), pc(state.pc), prevPC(state.prevPC), - stack(state.stack), stackBalance(state.stackBalance), - incomingBBIndex(state.incomingBBIndex), depth(state.depth), - multilevel(state.multilevel), level(state.level), + stack(state.stack), incomingBBIndex(state.incomingBBIndex), + depth(state.depth), multilevel(state.multilevel), + multilevelCount(state.multilevelCount), level(state.level), transitionLevel(state.transitionLevel), addressSpace(state.addressSpace), constraints(state.constraints), targetForest(state.targetForest), pathOS(state.pathOS), symPathOS(state.symPathOS), @@ -119,7 +172,10 @@ ExecutionState::ExecutionState(const ExecutionState &state) ? state.unwindingInformation->clone() : nullptr), coveredNew(state.coveredNew), forkDisabled(state.forkDisabled), - returnValue(state.returnValue), gepExprBases(state.gepExprBases) {} + isolated(state.isolated), returnValue(state.returnValue), + gepExprBases(state.gepExprBases), prevTargets_(state.prevTargets_), + targets_(state.targets_), prevHistory_(state.prevHistory_), + history_(state.history_), isTargeted_(state.isTargeted_) {} ExecutionState *ExecutionState::branch() { depth++; @@ -157,7 +213,7 @@ ExecutionState *ExecutionState::withKInstruction(KInstruction *ki) const { ExecutionState *newState = new ExecutionState(*this); newState->setID(); newState->pushFrame(nullptr, ki->parent->parent); - newState->stackBalance = 0; + newState->stack.SETSTACKBALANCETOZERO(); newState->initPC = ki->parent->instructions; while (newState->initPC != ki) { ++newState->initPC; @@ -178,20 +234,18 @@ ExecutionState *ExecutionState::copy() const { } void ExecutionState::pushFrame(KInstIterator caller, KFunction *kf) { - stack.emplace_back(StackFrame(caller, kf)); - ++stackBalance; + stack.pushFrame(caller, kf); } void ExecutionState::popFrame() { - const StackFrame &sf = stack.back(); + const StackFrame &sf = stack.valueStack().back(); for (const auto id : sf.allocas) { const MemoryObject *memoryObject = addressSpace.findObject(id).first; assert(memoryObject); removePointerResolutions(memoryObject); addressSpace.unbindObject(memoryObject); } - stack.pop_back(); - --stackBalance; + stack.popFrame(); } void ExecutionState::addSymbolic(const MemoryObject *mo, const Array *array, @@ -277,6 +331,14 @@ void ExecutionState::removePointerResolutions(const MemoryObject *mo) { } } +void ExecutionState::removePointerResolutions(ref address, + unsigned size) { + if (!isa(address)) { + resolvedPointers[address].clear(); + resolvedSubobjects[MemorySubobject(address, size)].clear(); + } +} + // base address mo and ignore non pure reads in setinitializationgraph void ExecutionState::addPointerResolution(ref address, const MemoryObject *mo, @@ -287,6 +349,16 @@ void ExecutionState::addPointerResolution(ref address, } } +void ExecutionState::addUniquePointerResolution(ref address, + const MemoryObject *mo, + unsigned size) { + if (!isa(address)) { + removePointerResolutions(address, size); + resolvedPointers[address].insert(mo->id); + resolvedSubobjects[MemorySubobject(address, size)].insert(mo->id); + } +} + bool ExecutionState::resolveOnSymbolics(const ref &addr, IDType &result) const { uint64_t address = addr->getZExtValue(); @@ -324,15 +396,15 @@ llvm::raw_ostream &klee::operator<<(llvm::raw_ostream &os, } void ExecutionState::dumpStack(llvm::raw_ostream &out) const { - unsigned idx = 0; const KInstruction *target = prevPC; - for (ExecutionState::stack_ty::const_reverse_iterator it = stack.rbegin(), - ie = stack.rend(); - it != ie; ++it) { - const StackFrame &sf = *it; - Function *f = sf.kf->function; + for (unsigned i = 0; i < stack.size(); ++i) { + unsigned ri = stack.size() - 1 - i; + const CallStackFrame &csf = stack.callStack().at(ri); + const StackFrame &sf = stack.valueStack().at(ri); + + Function *f = csf.kf->function; const InstructionInfo &ii = *target->info; - out << "\t#" << idx++; + out << "\t#" << i; if (ii.assemblyLine.hasValue()) { std::stringstream AssStream; AssStream << std::setw(8) << std::setfill('0') @@ -349,7 +421,7 @@ void ExecutionState::dumpStack(llvm::raw_ostream &out) const { out << ai->getName().str(); // XXX should go through function - ref value = sf.locals[sf.kf->getArgRegister(index++)].value; + ref value = sf.locals[csf.kf->getArgRegister(index++)].value; if (isa_and_nonnull(value)) out << "=" << value; } @@ -357,10 +429,14 @@ void ExecutionState::dumpStack(llvm::raw_ostream &out) const { if (ii.file != "") out << " at " << ii.file << ":" << ii.line; out << "\n"; - target = sf.caller; + target = csf.caller; } } +std::string ExecutionState::pathAndPCToString() const { + return constraints.path().toString() + " at " + pc->toString(); +} + void ExecutionState::addConstraint(ref e, const Assignment &delta) { constraints.addConstraint(e, delta); } @@ -386,14 +462,15 @@ void ExecutionState::increaseLevel() { KModule *kmodule = kf->parent; if (prevPC->inst->isTerminator() && kmodule->inMainModule(kf->function)) { - multilevel.insert(srcbb); + ++multilevel[srcbb]; + multilevelCount++; level.insert(srcbb); } - transitionLevel.insert(std::make_pair(srcbb, dstbb)); + if (srcbb != dstbb) { + transitionLevel.insert(std::make_pair(srcbb, dstbb)); + } } -bool ExecutionState::isTransfered() { return getPrevPCBlock() != getPCBlock(); } - bool ExecutionState::isGEPExpr(ref expr) const { return UseGEPOptimization && gepExprBases.find(expr) != gepExprBases.end(); } @@ -402,13 +479,13 @@ bool ExecutionState::visited(KBlock *block) const { return level.find(block->basicBlock) != level.end(); } -bool ExecutionState::reachedTarget(Target target) const { +bool ExecutionState::reachedTarget(ref target) const { if (constraints.path().KBlockSize() == 0) { return false; } - if (target.atReturn()) { - return prevPC == target.getBlock()->getLastInstruction(); + if (target->atReturn()) { + return prevPC == target->getBlock()->getLastInstruction(); } else { - return pc == target.getBlock()->getFirstInstruction(); + return pc == target->getBlock()->getFirstInstruction(); } } diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index 8d9db68d2b..3f2466621c 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -20,6 +20,7 @@ #include "klee/Expr/Expr.h" #include "klee/Expr/ExprHashMap.h" #include "klee/Module/KInstIterator.h" +#include "klee/Module/KInstruction.h" #include "klee/Module/Target.h" #include "klee/Module/TargetForest.h" #include "klee/Module/TargetHash.h" @@ -31,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -51,21 +53,27 @@ struct TranstionHash; llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const MemoryMap &mm); -struct StackFrame { +extern llvm::cl::opt MaxCyclesBeforeStuck; + +struct CallStackFrame { KInstIterator caller; KFunction *kf; - CallPathNode *callPathNode; + CallStackFrame(KInstIterator caller_, KFunction *kf_) + : caller(caller_), kf(kf_) {} + ~CallStackFrame() = default; + CallStackFrame(const CallStackFrame &s); + + bool equals(const CallStackFrame &other) const; + + bool operator==(const CallStackFrame &other) const { return equals(other); } +}; + +struct StackFrame { + KFunction *kf; std::vector allocas; Cell *locals; - /// Minimum distance to an uncovered instruction once the function - /// returns. This is not a good place for this but is used to - /// quickly compute the context sensitive minimum distance to an - /// uncovered instruction. This value is updated by the StatsTracker - /// periodically. - unsigned minDistToUncoveredOnReturn; - // For vararg functions: arguments not passed via parameter are // stored (packed tightly) in a local (alloca) memory object. This // is set up to match the way the front-end generates vaarg code (it @@ -73,11 +81,55 @@ struct StackFrame { // of intrinsic lowering. MemoryObject *varargs; - StackFrame(KInstIterator caller, KFunction *kf); + StackFrame(KFunction *kf); StackFrame(const StackFrame &s); ~StackFrame(); }; +struct InfoStackFrame { + KFunction *kf; + CallPathNode *callPathNode = nullptr; + + /// Minimum distance to an uncovered instruction once the function + /// returns. This is not a good place for this but is used to + /// quickly compute the context sensitive minimum distance to an + /// uncovered instruction. This value is updated by the StatsTracker + /// periodically. + unsigned minDistToUncoveredOnReturn = 0; + + InfoStackFrame(KFunction *kf); + InfoStackFrame(const InfoStackFrame &s); + ~InfoStackFrame() = default; +}; + +struct ExecutionStack { +public: + using value_stack_ty = std::vector; + using call_stack_ty = std::vector; + using info_stack_ty = std::vector; + +private: + value_stack_ty valueStack_; + call_stack_ty callStack_; + info_stack_ty infoStack_; + call_stack_ty uniqueFrames_; + int stackBalance_ = 0; + +public: + void pushFrame(KInstIterator caller, KFunction *kf); + void popFrame(); + int stackBalance() const { return stackBalance_; } + void SETSTACKBALANCETOZERO() { stackBalance_ = 0; } + inline value_stack_ty &valueStack() { return valueStack_; } + inline const value_stack_ty &valueStack() const { return valueStack_; } + inline const call_stack_ty &callStack() const { return callStack_; } + inline info_stack_ty &infoStack() { return infoStack_; } + inline const call_stack_ty &uniqueFrames() const { return uniqueFrames_; } + + inline unsigned size() const { return callStack_.size(); } + inline bool empty() const { return callStack_.empty(); } +}; + /// Contains information related to unwinding (Itanium ABI/2-Phase unwinding) class UnwindingInformation { public: @@ -202,8 +254,6 @@ class ExecutionState { ExecutionState(const ExecutionState &state); public: - using stack_ty = std::vector; - // Execution - Control Flow specific /// @brief Pointer to initial instruction @@ -216,10 +266,8 @@ class ExecutionState { /// @brief Pointer to instruction which is currently executed KInstIterator prevPC; - /// @brief Stack representing the current instruction stream - stack_ty stack; - - int stackBalance = 0; + /// @brief Execution stack representing the current instruction stream + ExecutionStack stack; /// @brief Remember from which Basic Block control flow arrived /// (i.e. to select the right phi values) @@ -232,7 +280,8 @@ class ExecutionState { std::uint32_t depth = 0; /// @brief Exploration level, i.e., number of times KLEE cycled for this state - std::unordered_multiset multilevel; + std::unordered_map multilevel; + unsigned long multilevelCount = 0; std::unordered_set level; std::unordered_set transitionLevel; @@ -317,6 +366,8 @@ class ExecutionState { /// @brief Disables forking for this state. Set by user code bool forkDisabled = false; + bool isolated = false; + /// Needed for composition ref returnValue; @@ -326,6 +377,14 @@ class ExecutionState { std::atomic terminationReasonType{ HaltExecution::NotHalt}; +private: + TargetHashSet prevTargets_; + TargetHashSet targets_; + ref prevHistory_; + ref history_; + bool isTargeted_ = false; + bool areTargetsChanged_ = false; + public: // only to create the initial state explicit ExecutionState(); @@ -361,8 +420,11 @@ class ExecutionState { std::pair, ref> &resolution) const; void removePointerResolutions(const MemoryObject *mo); + void removePointerResolutions(ref address, unsigned size); void addPointerResolution(ref address, const MemoryObject *mo, unsigned size = 0); + void addUniquePointerResolution(ref address, const MemoryObject *mo, + unsigned size = 0); bool resolveOnSymbolics(const ref &addr, IDType &result) const; void addConstraint(ref e, const Assignment &c); @@ -370,6 +432,8 @@ class ExecutionState { void dumpStack(llvm::raw_ostream &out) const; + std::string pathAndPCToString() const; + bool visited(KBlock *block) const; std::uint32_t getID() const { return id; }; @@ -378,10 +442,48 @@ class ExecutionState { llvm::BasicBlock *getPrevPCBlock() const; llvm::BasicBlock *getPCBlock() const; void increaseLevel(); - bool isTransfered(); + + inline bool isTransfered() { return getPrevPCBlock() != getPCBlock(); } + bool isGEPExpr(ref expr) const; - bool reachedTarget(Target target) const; + inline const TargetHashSet &prevTargets() const { return prevTargets_; } + + inline const TargetHashSet &targets() const { return targets_; } + + inline ref prevHistory() const { return prevHistory_; } + + inline ref history() const { return history_; } + + inline bool isTargeted() const { return isTargeted_; } + + inline bool areTargetsChanged() const { return areTargetsChanged_; } + + void stepTargetsAndHistory() { + prevHistory_ = history_; + prevTargets_ = targets_; + areTargetsChanged_ = false; + } + + inline void setTargeted(bool targeted) { isTargeted_ = targeted; } + + inline void setTargets(const TargetHashSet &targets) { + targets_ = targets; + areTargetsChanged_ = true; + } + + inline void setHistory(ref history) { + history_ = history; + areTargetsChanged_ = true; + } + + bool reachedTarget(ref target) const; + + inline bool isStuck(unsigned long long bound) { + KInstruction *prevKI = prevPC; + return (prevKI->inst->isTerminator() && + multilevel[getPCBlock()] > bound - 1); + } }; struct ExecutionStateIDCompare { diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index e42a4a5bb8..b89c8eb483 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -11,8 +11,10 @@ #include "AddressManager.h" #include "CXXTypeSystem/CXXTypeManager.h" +#include "Composer.h" #include "Context.h" #include "CoreStats.h" +#include "DistanceCalculator.h" #include "ExecutionState.h" #include "ExternalDispatcher.h" #include "GetElementPtrTypeIterator.h" @@ -26,6 +28,7 @@ #include "SpecialFunctionHandler.h" #include "StatsTracker.h" #include "TargetCalculator.h" +#include "TargetManager.h" #include "TargetedExecutionManager.h" #include "TimingSolver.h" #include "TypeManager.h" @@ -36,6 +39,7 @@ #include "klee/Config/Version.h" #include "klee/Config/config.h" #include "klee/Core/Interpreter.h" +#include "klee/Core/TerminationTypes.h" #include "klee/Expr/ArrayExprOptimizer.h" #include "klee/Expr/ArrayExprVisitor.h" #include "klee/Expr/Assignment.h" @@ -45,6 +49,7 @@ #include "klee/Expr/ExprSMTLIBPrinter.h" #include "klee/Expr/ExprUtil.h" #include "klee/Expr/IndependentSet.h" +#include "klee/Expr/Lemma.h" #include "klee/Expr/Symcrete.h" #include "klee/Module/Cell.h" #include "klee/Module/CodeGraphDistance.h" @@ -54,11 +59,11 @@ #include "klee/Module/KModule.h" #include "klee/Module/KType.h" #include "klee/Solver/Common.h" -#include "klee/Solver/ConcretizationManager.h" #include "klee/Solver/Solver.h" #include "klee/Solver/SolverCmdLine.h" #include "klee/Statistics/TimerStatIncrementer.h" #include "klee/Support/Casting.h" +#include "klee/Support/DebugFlags.h" #include "klee/Support/ErrorHandling.h" #include "klee/Support/FileHandling.h" #include "klee/Support/FloatEvaluation.h" @@ -135,6 +140,9 @@ cl::OptionCategory SeedingCat( cl::OptionCategory TestGenCat("Test generation options", "These options impact test generation."); +cl::OptionCategory LazyInitCat("Lazy initialization option", + "These options configure lazy initialization."); + cl::opt TypeSystem("type-system", cl::desc("Use information about type system from specified " @@ -150,6 +158,12 @@ cl::opt MergedPointerDereference( cl::desc("Enable merged pointer dereference (default=false)"), cl::cat(ExecCat)); +cl::opt MaxFailedBranchings( + "max-failed-branchings", + cl::desc("start bidirectional execution after failing during some " + "branching this amount of times (default=1)."), + cl::init(1), cl::cat(ExecCat)); + cl::opt UseTBAA("use-tbaa", cl::desc("Turns on restrictions based on types compatibility for " @@ -162,12 +176,6 @@ cl::opt "to the used type system (default=true)"), cl::init(true), cl::cat(ExecCat)); -cl::opt - OutOfMemAllocs("out-of-mem-allocs", - cl::desc("Model malloc behavior, i.e. model NULL on 0 " - "or huge symbolic allocations."), - cl::init(false), cl::cat(ExecCat)); - cl::opt ExternCallsCanReturnNull("extern-calls-can-return-null", cl::init(false), cl::desc("Enable case when extern call can crash " @@ -207,7 +215,29 @@ cl::opt LazyInitialization( "Only lazy initilization without resolving."), clEnumValN(LazyInitializationPolicy::All, "all", "Enable lazy initialization (default).")), - cl::init(LazyInitializationPolicy::All), cl::cat(ExecCat)); + cl::init(LazyInitializationPolicy::All), cl::cat(LazyInitCat)); + +llvm::cl::opt UseSymbolicSizeLazyInit( + "use-sym-size-li", + llvm::cl::desc( + "Allows lazy initialize symbolic size objects (default false)"), + llvm::cl::init(false), llvm::cl::cat(LazyInitCat)); + +llvm::cl::opt MinNumberElementsLazyInit( + "min-number-elements-li", + llvm::cl::desc("Minimum number of array elements for one lazy " + "initialization (default 4)"), + llvm::cl::init(4), llvm::cl::cat(LazyInitCat)); + +cl::opt ExecutionMode( + "execution-mode", + cl::values(clEnumValN(ExecutionKind::Forward, "forward", + "Use basic klee symbolic execution"), + clEnumValN(ExecutionKind::Bidirectional, "bidirectional", + "Use bidirectional execution")), + cl::init(ExecutionKind::Forward), cl::desc("Execution mode"), + cl::cat(ExecCat)); + } // namespace klee namespace { @@ -240,9 +270,9 @@ cl::opt MaxSymArraySize( cl::init(0), cl::cat(SolvingCat)); cl::opt - SimplifySymIndices("simplify-sym-indices", cl::init(false), + SimplifySymIndices("simplify-sym-indices", cl::init(true), cl::desc("Simplify symbolic accesses using equalities " - "from other constraints (default=false)"), + "from other constraints (default=true)"), cl::cat(SolvingCat)); cl::opt @@ -408,7 +438,7 @@ bool allLeafsAreConstant(const ref &expr) { ArrayExprHelper::collectAlternatives(*sel, alternatives); for (auto leaf : alternatives) { - if (!isa(expr)) { + if (!isa(leaf)) { return false; } } @@ -420,6 +450,7 @@ bool allLeafsAreConstant(const ref &expr) { extern llvm::cl::opt MaxConstantAllocationSize; extern llvm::cl::opt MaxSymbolicAllocationSize; +extern llvm::cl::opt UseSymbolicSizeAllocation; // XXX hack extern "C" unsigned dumpStates, dumpPForest; @@ -441,18 +472,22 @@ const std::unordered_set Executor::modelledFPIntrinsics = { Executor::Executor(LLVMContext &ctx, const InterpreterOptions &opts, InterpreterHandler *ih) : Interpreter(opts), interpreterHandler(ih), searcher(nullptr), - externalDispatcher(new ExternalDispatcher(ctx)), statsTracker(0), - pathWriter(0), symPathWriter(0), + externalDispatcher(new ExternalDispatcher(ctx)), + summary(interpreterHandler), statsTracker(0), pathWriter(0), + symPathWriter(0), specialFunctionHandler(0), timers{time::Span(TimerInterval)}, - concretizationManager(new ConcretizationManager(EqualitySubstitution)), codeGraphDistance(new CodeGraphDistance()), - targetedExecutionManager(*codeGraphDistance), replayKTest(0), - replayPath(0), usingSeeds(0), atMemoryLimit(false), inhibitForking(false), - haltExecution(HaltExecution::NotHalt), ivcEnabled(false), - debugLogBuffer(debugBufferString) { + distanceCalculator(new DistanceCalculator(*codeGraphDistance)), + replayKTest(0), replayPath(0), usingSeeds(0), atMemoryLimit(false), + inhibitForking(false), haltExecution(HaltExecution::NotHalt), + ivcEnabled(false), debugLogBuffer(debugBufferString) { guidanceKind = opts.Guidance; + objectManager = std::make_unique(); + seedMap = std::make_unique(); + objectManager->addSubscriber(seedMap.get()); + const time::Span maxTime{MaxTime}; if (maxTime) timers.add(std::make_unique(maxTime, [&] { @@ -478,7 +513,7 @@ Executor::Executor(LLVMContext &ctx, const InterpreterOptions &opts, interpreterHandler->getOutputFilename(SOLVER_QUERIES_SMT2_FILE_NAME), interpreterHandler->getOutputFilename(ALL_QUERIES_KQUERY_FILE_NAME), interpreterHandler->getOutputFilename(SOLVER_QUERIES_KQUERY_FILE_NAME), - concretizationManager.get(), addressManager.get()); + addressManager.get()); this->solver = new TimingSolver(solver, optimizer, EqualitySubstitution); @@ -590,6 +625,13 @@ Executor::setModule(std::vector> &userModules, targetCalculator = std::unique_ptr( new TargetCalculator(*kmodule.get(), *codeGraphDistance.get())); + targetManager = std::unique_ptr(new TargetManager( + guidanceKind, *distanceCalculator.get(), *targetCalculator.get())); + + targetedExecutionManager = + std::unique_ptr(new TargetedExecutionManager( + *codeGraphDistance, *distanceCalculator, *targetManager)); + return kmodule->module.get(); } @@ -887,9 +929,16 @@ void Executor::initializeGlobalObjects(ExecutionState &state) { } } } else if (v.hasInitializer()) { - initializeGlobalObject(state, os, v.getInitializer(), 0); - if (v.isConstant()) - constantObjects.emplace_back(os); + if (!v.isConstant() && + MockMutableGlobals == MockMutableGlobalsPolicy::All) { + executeMakeSymbolic( + state, mo, typeSystemManager->getWrappedType(v.getType()), + SourceBuilder::irreproducible("mockMutableGlobalObject"), false); + } else { + initializeGlobalObject(state, os, v.getInitializer(), 0); + if (v.isConstant()) + constantObjects.emplace_back(os); + } } else { os->initializeToRandom(); } @@ -907,8 +956,9 @@ void Executor::initializeGlobalObjects(ExecutionState &state) { } bool Executor::branchingPermitted(const ExecutionState &state) const { - if ((MaxMemoryInhibit && atMemoryLimit) || state.forkDisabled || - inhibitForking || (MaxForks != ~0u && stats::forks >= MaxForks)) { + if (!state.isolated && + ((MaxMemoryInhibit && atMemoryLimit) || state.forkDisabled || + inhibitForking || (MaxForks != ~0u && stats::forks >= MaxForks))) { if (MaxMemoryInhibit && atMemoryLimit) klee_warning_once(0, "skipping fork (memory cap exceeded)"); @@ -943,16 +993,16 @@ void Executor::branch(ExecutionState &state, } } } else { - stats::forks += N - 1; + if (!state.isolated) { + 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.push_back(ns); + auto ns = objectManager->branchState(es, reason); result.push_back(ns); - processForest->attach(es->ptreeNode, ns, es, reason); } } @@ -961,10 +1011,10 @@ void Executor::branch(ExecutionState &state, // simple). std::map>::iterator it = - seedMap.find(&state); - if (it != seedMap.end()) { + seedMap->find(&state); + if (it != seedMap->end()) { std::vector seeds = it->second; - seedMap.erase(it); + seedMap->erase(it); // Assume each seed only satisfies one condition (necessarily true // when conditions are mutually exclusive and their conjunction is @@ -991,12 +1041,12 @@ void Executor::branch(ExecutionState &state, // Extra check in case we're replaying seeds with a max-fork if (result[i]) - seedMap[result[i]].push_back(*siit); + seedMap->at(result[i]).push_back(*siit); } if (OnlyReplaySeeds) { for (unsigned i = 0; i < N; ++i) { - if (result[i] && !seedMap.count(result[i])) { + if (result[i] && !seedMap->count(result[i])) { terminateStateEarly(*result[i], "Unseeded path during replay", StateTerminationType::Replay); result[i] = nullptr; @@ -1025,7 +1075,7 @@ ref Executor::maxStaticPctChecks(ExecutionState ¤t, return condition; StatisticManager &sm = *theStatisticManager; - CallPathNode *cpn = current.stack.back().callPathNode; + CallPathNode *cpn = current.stack.infoStack().back().callPathNode; bool reached_max_fork_limit = (MaxStaticForkPct < 1. && @@ -1066,12 +1116,29 @@ ref Executor::maxStaticPctChecks(ExecutionState ¤t, return condition; } +bool Executor::canReachSomeTargetFromBlock(ExecutionState &es, KBlock *block) { + if (interpreterOpts.Guidance != GuidanceKind::ErrorGuidance) + return true; + auto nextInstr = block->getFirstInstruction(); + for (const auto &p : *es.targetForest.getTopLayer()) { + auto target = p.first; + if (target->mustVisitForkBranches(es.prevPC)) + return true; + auto dist = distanceCalculator->getDistance( + es.prevPC, nextInstr, es.stack.uniqueFrames(), es.error, target); + if (dist.result != WeightResult::Miss) + return true; + } + return false; +} + Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, - bool isInternal, BranchType reason) { - PartialValidity res; + KBlock *ifTrueBlock, KBlock *ifFalseBlock, + BranchType reason) { + bool isInternal = ifTrueBlock == ifFalseBlock; std::map>::iterator it = - seedMap.find(¤t); - bool isSeeding = it != seedMap.end(); + seedMap->find(¤t); + bool isSeeding = it != seedMap->end(); if (!isSeeding) condition = maxStaticPctChecks(current, condition); @@ -1080,8 +1147,46 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, if (isSeeding) timeout *= static_cast(it->second.size()); solver->setTimeout(timeout); - bool success = solver->evaluate(current.constraints.cs(), condition, res, - current.queryMetaData); + + bool shouldCheckTrueBlock = true, shouldCheckFalseBlock = true; + if (!isInternal) { + shouldCheckTrueBlock = canReachSomeTargetFromBlock(current, ifTrueBlock); + shouldCheckFalseBlock = canReachSomeTargetFromBlock(current, ifFalseBlock); + } + PartialValidity res = PartialValidity::None; + bool terminateEverything = false, success = false; + if (!shouldCheckTrueBlock) { + assert(shouldCheckFalseBlock && + "current state cannot reach any targets itself!"); + // only solver->check-sat(!condition) + bool mayBeFalse; + success = solver->mayBeFalse(current.constraints.cs(), condition, + mayBeFalse, current.queryMetaData); + if (success && !mayBeFalse) + terminateEverything = true; + else + res = PartialValidity::MayBeFalse; + } else if (!shouldCheckFalseBlock) { + // only solver->check-sat(condition) + bool mayBeTrue; + success = solver->mayBeTrue(current.constraints.cs(), condition, mayBeTrue, + current.queryMetaData); + if (success && !mayBeTrue) + terminateEverything = true; + else + res = PartialValidity::MayBeTrue; + } + if (terminateEverything) { + current.pc = current.prevPC; + terminateStateEarly(current, "State missed all it's targets.", + StateTerminationType::MissedAllTargets); + return StatePair(nullptr, nullptr); + } + if (res != PartialValidity::None) + success = true; + else + success = solver->evaluate(current.constraints.cs(), condition, res, + current.queryMetaData); solver->setTimeout(time::Span()); if (!success) { current.pc = current.prevPC; @@ -1192,16 +1297,17 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, TimerStatIncrementer timer(stats::forkTime); ExecutionState *falseState, *trueState = ¤t; - ++stats::forks; + if (!current.isolated) { + ++stats::forks; + } - falseState = trueState->branch(); - addedStates.push_back(falseState); + falseState = objectManager->branchState(trueState, reason); - if (it != seedMap.end()) { + if (it != seedMap->end()) { std::vector seeds = it->second; it->second.clear(); - std::vector &trueSeeds = seedMap[trueState]; - std::vector &falseSeeds = seedMap[falseState]; + std::vector &trueSeeds = seedMap->at(trueState); + std::vector &falseSeeds = seedMap->at(falseState); for (std::vector::iterator siit = seeds.begin(), siie = seeds.end(); siit != siie; ++siit) { @@ -1222,12 +1328,12 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, if (trueSeeds.empty()) { if (¤t == trueState) swapInfo = true; - seedMap.erase(trueState); + seedMap->erase(trueState); } if (falseSeeds.empty()) { if (¤t == falseState) swapInfo = true; - seedMap.erase(falseState); + seedMap->erase(falseState); } if (swapInfo) { std::swap(trueState->coveredNew, falseState->coveredNew); @@ -1235,8 +1341,6 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, } } - processForest->attach(current.ptreeNode, falseState, trueState, reason); - if (pathWriter) { // Need to update the pathOS.id field of falseState, otherwise the same id // is used for both falseState and trueState. @@ -1270,7 +1374,15 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, } } +Executor::StatePair Executor::forkInternal(ExecutionState ¤t, + ref condition, + BranchType reason) { + return fork(current, condition, nullptr, nullptr, reason); +} + void Executor::addConstraint(ExecutionState &state, ref condition) { + condition = + Simplificator::simplifyExpr(state.constraints.cs(), condition).simplified; if (ConstantExpr *CE = dyn_cast(condition)) { if (!CE->isTrue()) llvm::report_fatal_error("attempt to add invalid constraint"); @@ -1279,8 +1391,8 @@ void Executor::addConstraint(ExecutionState &state, ref condition) { // Check to see if this constraint violates seeds. std::map>::iterator it = - seedMap.find(&state); - if (it != seedMap.end()) { + seedMap->find(&state); + if (it != seedMap->end()) { bool warn = false; for (std::vector::iterator siit = it->second.begin(), siie = it->second.end(); @@ -1302,28 +1414,14 @@ void Executor::addConstraint(ExecutionState &state, ref condition) { klee_warning("seeds patched for violating constraint"); } - std::pair symcretization = - concretizationManager->get(state.constraints.cs(), condition); + Assignment concretization = computeConcretization( + state.constraints.cs(), condition, state.queryMetaData); - if (!symcretization.second && - Query(state.constraints.cs(), condition).containsSymcretes()) { - bool mayBeInBounds; - solver->setTimeout(coreSolverTimeout); - bool success = solver->mayBeTrue(state.constraints.cs(), condition, - mayBeInBounds, state.queryMetaData); - solver->setTimeout(time::Span()); - assert(success); - assert(mayBeInBounds); - symcretization = - concretizationManager->get(state.constraints.cs(), condition); - assert(symcretization.second); - } - - if (symcretization.second) { + if (!concretization.isEmpty()) { // Update memory objects if arrays have affected them. Assignment delta = - state.constraints.cs().concretization().diffWith(symcretization.first); - updateStateWithSymcretes(state, symcretization.first); + state.constraints.cs().concretization().diffWith(concretization); + updateStateWithSymcretes(state, delta); state.addConstraint(condition, delta); } else { state.addConstraint(condition, {}); @@ -1359,7 +1457,7 @@ const Cell &Executor::eval(const KInstruction *ki, unsigned index, const Cell &Executor::eval(const KInstruction *ki, unsigned index, ExecutionState &state, bool isSymbolic) { - return eval(ki, index, state, state.stack.back(), isSymbolic); + return eval(ki, index, state, state.stack.valueStack().back(), isSymbolic); } void Executor::bindLocal(const KInstruction *target, StackFrame &frame, @@ -1425,8 +1523,8 @@ void Executor::executeGetValue(ExecutionState &state, ref e, KInstruction *target) { e = Simplificator::simplifyExpr(state.constraints.cs(), e).simplified; std::map>::iterator it = - seedMap.find(&state); - if (it == seedMap.end() || isa(e)) { + seedMap->find(&state); + if (it == seedMap->end() || isa(e)) { ref value; e = optimizer.optimizeExpr(e, true); bool success = @@ -1493,7 +1591,13 @@ void Executor::printDebugInstructions(ExecutionState &state) { if (state.pc->info->assemblyLine.hasValue()) { (*stream) << state.pc->info->assemblyLine.getValue() << ':'; } - (*stream) << state.getID(); + (*stream) << state.getID() << ":"; + (*stream) << "["; + for (auto target : state.targets()) { + (*stream) << target->toString() << ","; + } + (*stream) << "]"; + if (DebugPrintInstructions.isSet(STDERR_ALL) || DebugPrintInstructions.isSet(FILE_ALL)) (*stream) << ':' << *(state.pc->inst); @@ -1512,10 +1616,13 @@ void Executor::printDebugInstructions(ExecutionState &state) { void Executor::stepInstruction(ExecutionState &state) { printDebugInstructions(state); - if (statsTracker) - statsTracker->stepInstruction(state); + if (!state.isolated) { + if (statsTracker) { + statsTracker->stepInstruction(state); + } + ++stats::instructions; + } - ++stats::instructions; ++state.steppedInstructions; if (isa(state.pc->inst) || isa(state.pc->inst)) { ++state.steppedMemoryInstructions; @@ -1662,7 +1769,7 @@ void Executor::unwindToNextLandingpad(ExecutionState &state) { lowestStackIndex = 0; popFrames = false; } else if (auto *cui = dyn_cast(ui)) { - startIndex = state.stack.size() - 1; + startIndex = state.stack.callStack().size() - 1; lowestStackIndex = cui->catchingStackIndex; popFrames = true; } else { @@ -1670,13 +1777,13 @@ void Executor::unwindToNextLandingpad(ExecutionState &state) { } for (std::size_t i = startIndex; i > lowestStackIndex; i--) { - auto const &sf = state.stack.at(i); + auto const &sf = state.stack.callStack().at(i); Instruction *inst = sf.caller ? sf.caller->inst : nullptr; if (popFrames) { state.popFrame(); - if (statsTracker != nullptr) { + if (statsTracker != nullptr && !state.isolated) { statsTracker->framePopped(state); } } @@ -1723,9 +1830,9 @@ void Executor::unwindToNextLandingpad(ExecutionState &state) { bindArgument(kf, 1, state, clauses_mo->getSizeExpr()); bindArgument(kf, 2, state, clauses_mo->getBaseExpr()); - if (statsTracker) { - statsTracker->framePushed(state, - &state.stack[state.stack.size() - 2]); + if (statsTracker && !state.isolated) { + statsTracker->framePushed( + state, &state.stack.infoStack()[state.stack.size() - 2]); } // make sure we remember our search progress afterwards @@ -1982,7 +2089,7 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f, // va_arg is handled by caller and intrinsic lowering, see comment for // ExecutionState::varargs case Intrinsic::vastart: { - StackFrame &sf = state.stack.back(); + StackFrame &sf = state.stack.valueStack().back(); // varargs can be zero if no varargs were provided if (!sf.varargs) @@ -2067,8 +2174,9 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f, transferToBasicBlock(&*kf->function->begin(), state.getPrevPCBlock(), state); - if (statsTracker) - statsTracker->framePushed(state, &state.stack[state.stack.size() - 2]); + if (statsTracker && !state.isolated) + statsTracker->framePushed( + state, &state.stack.infoStack()[state.stack.size() - 2]); // TODO: support zeroext, signext, sret attributes @@ -2153,7 +2261,7 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f, } } - StackFrame &sf = state.stack.back(); + StackFrame &sf = state.stack.valueStack().back(); MemoryObject *mo = sf.varargs = memory->allocate(size, true, false, false, state.prevPC->inst, (requires16ByteAlignment ? 16 : 8)); @@ -2221,6 +2329,19 @@ void Executor::increaseProgressVelocity(ExecutionState &state, KBlock *block) { } } +void Executor::updateConfidenceRates() { + reportProgressTowardsTargets(); + decreaseConfidenceFromStoppedStates(objectManager->getStates(), haltExecution); + + for (auto &startBlockAndWhiteList : targets) { + startBlockAndWhiteList.second.reportFalsePositives( + hasStateWhichCanReachSomeTarget); + } + + if (searcher->empty()) + haltExecution = HaltExecution::NoMoreStates; +} + void Executor::transferToBasicBlock(KBlock *kdst, BasicBlock *src, ExecutionState &state) { // Note that in general phi nodes can reuse phi values from the same @@ -2247,7 +2368,7 @@ void Executor::transferToBasicBlock(KBlock *kdst, BasicBlock *src, void Executor::transferToBasicBlock(BasicBlock *dst, BasicBlock *src, ExecutionState &state) { - KFunction *kf = state.stack.back().kf; + KFunction *kf = state.stack.callStack().back().kf; auto kdst = kf->blockMap[dst]; transferToBasicBlock(kdst, src, state); } @@ -2281,8 +2402,7 @@ void Executor::checkNullCheckAfterDeref(ref cond, ExecutionState &state, void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { Instruction *i = ki->inst; - if (guidanceKind == GuidanceKind::ErrorGuidance && - state.prevPC->inst->isTerminator()) { + if (guidanceKind == GuidanceKind::ErrorGuidance) { for (auto kvp : state.targetForest) { auto target = kvp.first; if (target->isThatError(ReachWithError::Reachable) && @@ -2297,7 +2417,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { // Control flow case Instruction::Ret: { ReturnInst *ri = cast(i); - KInstIterator kcaller = state.stack.back().caller; + KInstIterator kcaller = state.stack.callStack().back().caller; Instruction *caller = kcaller ? kcaller->inst : nullptr; bool isVoidReturn = (ri->getNumOperands() == 0); ref result = ConstantExpr::alloc(0, Expr::Bool); @@ -2310,12 +2430,17 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { assert(!caller && "caller set on initial stack frame"); state.pc = state.prevPC; state.increaseLevel(); + if (state.isolated) { + state.popFrame(); + state.returnValue = result; + } terminateStateOnExit(state); } else { state.popFrame(); - if (statsTracker) + if (statsTracker && !state.isolated) { statsTracker->framePopped(state); + } if (InvokeInst *ii = dyn_cast(caller)) { transferToBasicBlock(ii->getNormalDest(), caller->getParent(), state); @@ -2417,15 +2542,63 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { cond = optimizer.optimizeExpr(cond, false); + KFunction *kf = state.stack.callStack().back().kf; + auto ifTrueBlock = kf->blockMap[bi->getSuccessor(0)]; + auto ifFalseBlock = kf->blockMap[bi->getSuccessor(1)]; Executor::StatePair branches = - fork(state, cond, false, BranchType::ConditionalBranch); + fork(state, cond, ifTrueBlock, ifFalseBlock, + BranchType::ConditionalBranch); + + if (ProduceUnsatCore && !state.isolated && + ExecutionMode == ExecutionKind::Bidirectional) { + if ((!branches.first && branches.second) || + (branches.first && !branches.second)) { + ExecutionState &validState = + !branches.first ? *branches.second : *branches.first; + ref condition = + !branches.first ? Expr::createIsZero(cond) : cond; + unsigned index = !branches.first ? 0 : 1; + ValidityCore core; + bool result; + auto success = + solver->getValidityCore(validState.constraints.cs(), condition, + core, result, validState.queryMetaData); + if (success && result) { + auto conflict = Conflict(); + conflict.path = validState.constraints.path(); + conflict.core = core.constraints; + // for (const auto &i: conflict.core) { + // conflict.pathIndexes.insert({i, + // state.constraints.indexes().at(i)}); + // } + conflict.core.insert(Expr::createIsZero(core.expr)); + ref targeted = new TargetedConflict( + conflict, kmodule->getKBlock(bi->getSuccessor(index))); + failedTransitionsTo[targeted->target->basicBlock] = + failedTransitionsTo[targeted->target->basicBlock] + 1; + if (!successTransitionsTo.count(targeted->target->basicBlock) && + failedTransitionsTo[targeted->target->basicBlock] > + MaxFailedBranchings) { + objectManager->addTargetedConflict(targeted); + + if (!verifingTransitionsTo.count(targeted->target->basicBlock)) { + verifingTransitionsTo.insert(targeted->target->basicBlock); + ProofObligation *pob = new ProofObligation(targeted->target); + objectManager->addPob(pob); + } + } + } + } + } // 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) + if (statsTracker && !state.isolated && + state.stack.callStack().back().kf->trackCoverage) { statsTracker->markBranchVisited(branches.first, branches.second); + } if (branches.first) transferToBasicBlock(bi->getSuccessor(0), bi->getParent(), @@ -2463,12 +2636,15 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { ref errorCase = ConstantExpr::alloc(1, Expr::Bool); SmallPtrSet destinations; + KFunction *kf = state.stack.callStack().back().kf; // collect and check destinations from label list for (unsigned k = 0; k < numDestinations; ++k) { // filter duplicates const auto d = bi->getDestination(k); if (destinations.count(d)) continue; + if (!canReachSomeTargetFromBlock(state, kf->blockMap[d])) + continue; destinations.insert(d); // create address expression @@ -2555,12 +2731,15 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { // Track default branch values ref defaultValue = ConstantExpr::alloc(1, Expr::Bool); + KFunction *kf = state.stack.callStack().back().kf; + // iterate through all non-default cases but in order of the expressions for (std::map, BasicBlock *>::iterator it = expressionOrder.begin(), itE = expressionOrder.end(); it != itE; ++it) { ref match = EqExpr::create(cond, it->first); + BasicBlock *caseSuccessor = it->second; // skip if case has same successor basic block as default case // (should work even with phi nodes as a switch is a single terminating @@ -2571,6 +2750,9 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { // Make sure that the default value does not contain this target's value defaultValue = AndExpr::create(defaultValue, Expr::createIsZero(match)); + if (!canReachSomeTargetFromBlock(state, kf->blockMap[caseSuccessor])) + continue; + // Check if control flow could take this case bool result; match = optimizer.optimizeExpr(match, false); @@ -2579,8 +2761,6 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { assert(success && "FIXME: Unhandled solver failure"); (void)success; if (result) { - BasicBlock *caseSuccessor = it->second; - // Handle the case that a basic block might be the target of multiple // switch cases. // Currently we generate an expression containing all switch-case @@ -2600,19 +2780,21 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { } } - // Check if control could take the default case - defaultValue = optimizer.optimizeExpr(defaultValue, false); - bool res; - bool success = solver->mayBeTrue(state.constraints.cs(), defaultValue, - res, state.queryMetaData); - assert(success && "FIXME: Unhandled solver failure"); - (void)success; - if (res) { - std::pair>::iterator, bool> ret = - branchTargets.insert( - std::make_pair(si->getDefaultDest(), defaultValue)); - if (ret.second) { - bbOrder.push_back(si->getDefaultDest()); + auto defaultDest = si->getDefaultDest(); + if (canReachSomeTargetFromBlock(state, kf->blockMap[defaultDest])) { + // Check if control could take the default case + defaultValue = optimizer.optimizeExpr(defaultValue, false); + bool res; + bool success = solver->mayBeTrue(state.constraints.cs(), defaultValue, + res, state.queryMetaData); + assert(success && "FIXME: Unhandled solver failure"); + (void)success; + if (res) { + std::pair>::iterator, bool> ret = + branchTargets.insert(std::make_pair(defaultDest, defaultValue)); + if (ret.second) { + bbOrder.push_back(defaultDest); + } } } @@ -2743,7 +2925,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { assert(success && "FIXME: Unhandled solver failure"); (void)success; StatePair res = - fork(*free, EqExpr::create(v, value), true, BranchType::Call); + forkInternal(*free, EqExpr::create(v, value), BranchType::Call); if (res.first) { uint64_t addr = value->getZExtValue(); auto it = legalFunctions.find(addr); @@ -3056,7 +3238,8 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { offset = AddExpr::create(offset, Expr::createPointer(kgepi->offset)); ref address = AddExpr::create(base, offset); - if (!isa(address) || base->isZero()) { + if (!isa(address) || base->isZero() || + state.isGEPExpr(base)) { if (state.isGEPExpr(base)) { state.gepExprBases[address] = state.gepExprBases[base]; } else { @@ -3794,31 +3977,6 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { } } -void Executor::updateStates(ExecutionState *current) { - if (searcher) { - searcher->update(current, addedStates, removedStates); - } - - states.insert(addedStates.begin(), addedStates.end()); - addedStates.clear(); - - for (std::vector::iterator it = removedStates.begin(), - ie = removedStates.end(); - it != ie; ++it) { - ExecutionState *es = *it; - std::set::iterator it2 = states.find(es); - assert(it2 != states.end()); - states.erase(it2); - std::map>::iterator it3 = - seedMap.find(es); - if (it3 != seedMap.end()) - seedMap.erase(it3); - processForest->remove(es->ptreeNode); - delete es; - } - removedStates.clear(); -} - template void Executor::computeOffsetsSeqTy(KGEPInstruction *kgepi, ref &constantOffset, @@ -3917,6 +4075,7 @@ bool Executor::checkMemoryUsage() { return true; // just guess at how many to kill + auto states = objectManager->getStates(); const auto numStates = states.size(); auto toKill = std::max(1UL, numStates - numStates * MaxMemory / totalUsage); klee_warning("killing %lu states (over memory cap: %luMB)", toKill, @@ -3940,10 +4099,12 @@ bool Executor::checkMemoryUsage() { return false; } -bool Executor::decreaseConfidenceFromStoppedStates( - SetOfStates &left_states, HaltExecution::Reason reason) { - bool hasStateWhichCanReachSomeTarget = false; - for (auto state : left_states) { +void Executor::decreaseConfidenceFromStoppedStates( + const SetOfStates &leftStates, HaltExecution::Reason reason) { + if (targets.size() == 0) { + return; + } + for (auto state : leftStates) { if (state->targetForest.empty()) continue; hasStateWhichCanReachSomeTarget = true; @@ -3954,24 +4115,488 @@ bool Executor::decreaseConfidenceFromStoppedStates( .subtractConfidencesFrom(state->targetForest, realReason); } } - return hasStateWhichCanReachSomeTarget; } void Executor::doDumpStates() { - if (!DumpStatesOnHalt || states.empty()) { - interpreterHandler->incPathsExplored(states.size()); + if (!DumpStatesOnHalt || objectManager->getStates().empty()) { + interpreterHandler->incPathsExplored(objectManager->getStates().size()); return; } klee_message("halting execution, dumping remaining states"); - for (const auto &state : pausedStates) - terminateStateEarly(*state, "Execution halting (paused state).", - StateTerminationType::Interrupted); - updateStates(nullptr); - for (const auto &state : states) + + for (const auto &state : objectManager->getStates()) { terminateStateEarly(*state, "Execution halting.", StateTerminationType::Interrupted); - updateStates(nullptr); + } + + objectManager->updateSubscribers(); + + for (const auto &state : objectManager->getIsolatedStates()) { + terminateState(*state, StateTerminationType::Interrupted); // Correct? + } +} + +ref Executor::fillValue(ExecutionState &state, + ref valueSource, ref size, + Expr::Width width) { + int diffLevel = -state.stack.stackBalance(); + if ((valueSource->index >= 0 && diffLevel > 0) || + (valueSource->index > 0 && valueSource->index + diffLevel > 0)) { + int reindex = valueSource->index + diffLevel; + const Array *newArray = + makeArray(size, SourceBuilder::value(valueSource->value(), reindex, + kmodule.get())); + ref result = Expr::createTempRead(newArray, width); + if (isa(valueSource)) { + const Instruction *inst = cast(&valueSource->value()); + const KInstruction *target = getKInst(const_cast(inst)); + StackFrame sf(target->parent->parent); + if (isa(target->inst)) { + lazyInitializeLocalObject(state, sf, result, target); + } + } + return result; + } + ref result; + switch (valueSource->getKind()) { + case SymbolicSource::Kind::Instruction: { + const Instruction *inst = cast(&valueSource->value()); + const KInstruction *ki = getKInst(const_cast(inst)); + if (valueSource->index == -1) { + + assert(isa(inst) || isa(inst)); + KFunction *kf = ki->parent->parent; + + if (state.stack.empty()) { +#if LLVM_VERSION_CODE >= LLVM_VERSION(8, 0) + const CallBase &cs = cast(*inst); + Value *fp = cs.getCalledOperand(); +#else + CallSite cs(inst); + Value *fp = cs.getCalledValue(); +#endif + Function *calledf = getTargetFunction(fp); + KFunction *lastkf = state.pc->parent->parent; + KBlock *pckb = lastkf->blockMap.at(state.getPCBlock()); + bool isFinalPCKB = std::find(lastkf->returnKBlocks.begin(), + lastkf->returnKBlocks.end(), + pckb) != lastkf->returnKBlocks.end(); + assert(isFinalPCKB && calledf == lastkf->function); + result = state.returnValue; + } else { + const StackFrame &frame = state.stack.valueStack().back(); + KFunction *framekf = frame.kf; + assert(kf->function == framekf->function); + result = getDestCell(frame, ki).value; + assert(result); + } + } else { + assert(valueSource->index >= 0); + assert(!state.stack.empty()); + StackFrame &frame = + state.stack.valueStack().at(state.stack.size() - valueSource->index - 1); + KFunction *framekf = frame.kf; + + if (isa(inst)) { + assert(framekf->function == inst->getParent()->getParent()); + if (inst->getParent() == state.getPCBlock()) { + result = eval(ki, state.incomingBBIndex, state, frame).value; + } else { + result = readDest(state, frame, ki); + } + } else if ((isa(inst) || isa(inst))) { + KFunction *kf = ki->parent->parent; + assert(kf->function == framekf->function); + result = readDest(state, frame, ki); + } else { + const Function *f = inst->getParent()->getParent(); + const KFunction *kf = getKFunction(f); + assert(kf->function == framekf->function); + result = readDest(state, frame, ki); + } + } + break; + } + case SymbolicSource::Kind::Argument: { + assert(valueSource->index >= 0); + assert(!state.stack.empty()); + StackFrame &frame = + state.stack.valueStack().at(state.stack.size() - valueSource->index - 1); + KFunction *framekf = frame.kf; + + const Argument *arg = cast(&valueSource->value()); + const Function *f = arg->getParent(); + const KFunction *kf = getKFunction(f); + const unsigned argN = arg->getArgNo(); + assert(kf->function == framekf->function); + result = readArgument(state, frame, kf, argN); + break; + } + + default: + assert(0 && "unreachable"); + break; + } + return ZExtExpr::create(result, width); +} + +ref +Executor::fillMakeSymbolic(ExecutionState &state, + ref makeSymbolicSource, + ref size, unsigned concreteSize) { + unsigned stateNameVersion = + state.arrayNames.count(makeSymbolicSource->name) + ? state.arrayNames.at(makeSymbolicSource->name) + : 0; + unsigned newVersion = makeSymbolicSource->version + stateNameVersion; + const Array *newArray = makeArray( + size, SourceBuilder::makeSymbolic(makeSymbolicSource->name, newVersion)); + return new ObjectState(concreteSize, newArray, + typeSystemManager->getUnknownType()); +} + +ref Executor::fillConstant(ExecutionState &state, + ref constanSource, + ref size) { + const Array *newArray = + makeArray(size, SourceBuilder::constant(constanSource->constantValues)); + return new ObjectState(constanSource->constantValues.size(), newArray, + typeSystemManager->getUnknownType()); +} + +ref Executor::fillSymbolicSizeConstant( + ExecutionState &state, + ref symbolicSizeConstantSource, ref size, + unsigned concreteSize) { + const Array *newArray = + makeArray(size, SourceBuilder::symbolicSizeConstant( + symbolicSizeConstantSource->defaultValue)); + return new ObjectState(concreteSize, newArray, + typeSystemManager->getUnknownType()); +} + +ref Executor::fillSymbolicSizeConstantAddress( + ExecutionState &state, + ref symbolicSizeConstantAddressSource, + ref size, Expr::Width width) { + unsigned stateNameVersion = state.arrayNames.count("const_arr") + ? state.arrayNames.at("const_arr") + : 0; + unsigned newVersion = + symbolicSizeConstantAddressSource->version + stateNameVersion; + const Array *newArray = makeArray( + size, SourceBuilder::symbolicSizeConstantAddress( + symbolicSizeConstantAddressSource->defaultValue, newVersion)); + return Expr::createTempRead(newArray, width); +} + +ref Executor::fillSizeAddressSymcretes(ExecutionState &state, + ref oldAddress, + ref newAddress, + ref size) { + ref uniqueSize = toUnique(state, size); + + ref arrayConstantSize = + dyn_cast(optimizer.optimizeExpr(uniqueSize, true)); + + MemoryObject *mo = nullptr; + assert(addressManager->isAllocated(oldAddress)); + addressManager->allocate(oldAddress, 0); + MemoryObject *oldMO = addressManager->allocateMemoryObject(oldAddress, 0); + + /* Constant solution exists. Just return it. */ + if (arrayConstantSize) { + mo = memory->allocate(arrayConstantSize->getZExtValue(), oldMO->isLocal, + oldMO->isGlobal, oldMO->isLazyInitialized, + oldMO->allocSite, oldMO->alignment); + } else { + ref addressSymcrete = + cast(new AllocAddressSymcrete(newAddress)); + ref sizeSymcrete = cast(new AllocSizeSymcrete( + size, cast(addressSymcrete))); + + sizeSymcrete->addDependentSymcrete(addressSymcrete); + addressSymcrete->addDependentSymcrete(sizeSymcrete); + + Expr::Width pointerWidthInBits = Context::get().getPointerWidth(); + + std::vector> symbolicSizesTerms = { + ZExtExpr::create(size, pointerWidthInBits)}; + + /* Collect all size symcretes. */ + for (ref symcrete : state.constraints.cs().symcretes()) { + if (isa(symcrete)) { + symbolicSizesTerms.push_back( + ZExtExpr::create(symcrete->symcretized, pointerWidthInBits)); + } + } + + ref symbolicSizesSum = + createNonOverflowingSumExpr(symbolicSizesTerms); + + std::vector objects; + std::vector> values; + bool success = computeSizes(state, size, symbolicSizesSum, objects, values); + assert(success); + + Assignment assignment(objects, values, true); + uint64_t sizeMemoryObject = + cast(assignment.evaluate(size))->getZExtValue(); + + if (addressManager->isAllocated(newAddress)) { + addressManager->allocate(newAddress, sizeMemoryObject); + mo = addressManager->allocateMemoryObject(newAddress, sizeMemoryObject); + } else { + mo = memory->allocate(sizeMemoryObject, oldMO->isLocal, oldMO->isGlobal, + oldMO->isLazyInitialized, oldMO->allocSite, + oldMO->alignment, newAddress, + ZExtExpr::create(size, pointerWidthInBits), + oldMO->timestamp); + + assert(mo); + addressManager->addAllocation(newAddress, mo->id); + } + assert(addressSymcrete->dependentArrays().size() == 1); + assignment.bindings[addressSymcrete->dependentArrays().back()] = + sparseBytesFromValue(mo->address); + + state.constraints.addSymcrete(sizeSymcrete, assignment); + state.constraints.addSymcrete(addressSymcrete, assignment); + state.constraints.rewriteConcretization(assignment); + } + return mo->getBaseExpr(); +} + +std::pair, ref> +Executor::getSymbolicSizeConstantSizeAddressPair( + ExecutionState &state, + ref symbolicSizeConstantAddressSource, + ref size, Expr::Width width) { + const Array *array = makeArray(size, symbolicSizeConstantAddressSource); + ref address = Expr::createTempRead(array, width); + assert(addressManager->isAllocated(address)); + addressManager->allocate(address, 0); + MemoryObject *mo = addressManager->allocateMemoryObject(address, 0); + return std::make_pair(address, mo->getSizeExpr()); +} + +Executor::ComposeResult Executor::compose(const ExecutionState &state, + const PathConstraints &pob) { + ComposeResult result; + ComposeHelper helper(this); + ComposeVisitor composer(state, helper); + ExprHashMap> rebuildMap; + unsigned offset = state.constraints.path().KBlockSize(); + for (auto &indexConstraints : pob.orderedCS()) { + Path::PathIndex index = indexConstraints.first; + index.block += offset; + for (ref constraint : indexConstraints.second) { + std::pair, ref> composeResult = + composer.compose(constraint); + if (composeResult.first->isFalse()) { + result.success = false; + return result; + } + ref condition = composeResult.second; + + condition = Simplificator::simplifyExpr(composer.state.constraints.cs(), + condition) + .simplified; + + ValidityCore core; + bool isValid; + solver->setTimeout(coreSolverTimeout); + bool success = solver->getValidityCore( + composer.state.constraints.cs(), Expr::createIsZero(condition), core, + isValid, composer.state.queryMetaData); + solver->setTimeout(time::Span()); + if (!success || haltExecution) { + result.success = false; + return result; + } + if (isValid) { + auto conflict = Conflict(); + conflict.path = Path::concat(state.constraints.path(), pob.path()); + if (debugPrints.isSet(DebugPrint::Conflict)) { + llvm::errs() << "[conflict] Conflict in backward: " + << conflict.path.toString() << "\n"; + } + constraints_ty rebuiltCore; + for (auto e : core.constraints) { + for (auto original : + composer.state.constraints.simplificationMap().at(e)) { + if (rebuildMap.count(original)) { + conflict.core.insert(Expr::createIsZero(rebuildMap.at(original))); + } + } + } + + conflict.core.insert(Expr::createIsZero(constraint)); + + result.success = false; + result.conflict = conflict; + return result; + } + + auto symcretization = + computeConcretization(composer.state.constraints.cs(), condition, + composer.state.queryMetaData); + Assignment delta; + if (!symcretization.isEmpty()) { + delta = composer.state.constraints.cs().concretization().diffWith( + symcretization); + updateStateWithSymcretes(composer.state, symcretization); + } + auto added = + composer.state.constraints.addConstraint(condition, delta, index); + for (auto expr : added) { + rebuildMap.insert({expr, constraint}); + } + } + } + + result.success = true; + composer.state.constraints.advancePath(pob.path()); + result.composed = composer.state.constraints; + return result; +} + +void Executor::executeAction(ref action) { + switch (action->getKind()) { + case BidirectionalAction::Kind::Forward: { + if (debugPrints.isSet(DebugPrint::Forward) && cast(action)->state->isolated) { + llvm::errs() << "[forward] State: " + << cast(action)->state->pathAndPCToString() + << "\n"; + if (debugConstraints.isSet(DebugPrint::Forward)) { + // TODO + } + } + goForward(cast(action)); + break; + } + case BidirectionalAction::Kind::Backward: { + if (debugPrints.isSet(DebugPrint::Backward)) { + llvm::errs() + << "[backward] State: " + << cast(action)->prop.state->pathAndPCToString() + << "\n"; + llvm::errs() << "[backward] Pob: " + << cast(action) + ->prop.pob->constraints.path() + .toString() + << "\n"; + if (debugConstraints.isSet(DebugPrint::Backward)) { + // TODO + } + } + goBackward(cast(action)); + break; + } + case BidirectionalAction::Kind::Initialize: { + initializeIsolated(cast(action)); + break; + } + } +} + +void Executor::goForward(ref action) { + ref fa = cast(action); + objectManager->setCurrentState(fa->state); + + KInstruction *prevKI = fa->state->prevPC; + + if (targetManager->isTargeted(*fa->state) && fa->state->targets().empty()) { + terminateStateEarly(*fa->state, "State missed all it's targets.", + StateTerminationType::MissedAllTargets); + } else if (prevKI->inst->isTerminator() && + fa->state->multilevel[fa->state->getPCBlock()] > MaxCycles - 1) { + terminateStateEarly(*fa->state, "max-cycles exceeded.", + StateTerminationType::MaxCycles); + } else { + KInstruction *prevKI = fa->state->prevPC; + KFunction *kf = prevKI->parent->parent; + + if (!fa->state->isolated && prevKI->inst->isTerminator() && + kmodule->inMainModule(kf->function)) { + targetCalculator->update(*fa->state); + } + + KInstruction *ki = fa->state->pc; + stepInstruction(*fa->state); + executeInstruction(*fa->state, ki); + } + + timers.invoke(); + if (::dumpStates) + dumpStates(); + if (::dumpPForest) + dumpPForest(); +} + +void Executor::goBackward(ref action) { + objectManager->removePropagation(action->prop); + + ExecutionState *state = action->prop.state; + ProofObligation *pob = action->prop.pob; + + // Conflict::core_ty conflictCore; + // ExprHashMap> rebuildMap; + + Executor::ComposeResult composeResult = compose(*state, pob->constraints); + // ProofObligation *newPob = new ProofObligation(state->initPC->parent, pob); + // bool success = Composer::tryRebuild(*pob, *state, *newPob, conflictCore, + // rebuildMap); timers.invoke(); + + if (composeResult.success) { + // Final composition states are not isolated, others are + if (!state->isolated) { + if (debugPrints.isSet(DebugPrint::ClosePob)) { + llvm::errs() << "[close pob] Pob closed due to backward reach at: " + << pob->root->location->toString() << "\n"; + } + closeProofObligation(pob); + } else { + if (state->initPC->parent->getKBlockType() == KBlockType::Call && + state->initPC->inst->getOpcode() == Instruction::Br) { + KCallBlock *b = dyn_cast(state->initPC->parent); + for (auto f : b->calledFunctions) { + KFunction *kf = kmodule->functionMap[f]; + for (auto returnBlock : kf->returnKBlocks) { + ProofObligation *callPob = + new ProofObligation(composeResult.composed, *pob, returnBlock); + callPob->propagationCount[state]++; + objectManager->addPob(callPob); + } + } + } else { + ProofObligation *newPob = + new ProofObligation(composeResult.composed, *pob); + newPob->propagationCount[state]++; + objectManager->addPob(newPob); + } + } + } else { + if (state->isolated && composeResult.conflict.core.size()) { + summary.addLemma( + new Lemma(state->constraints.path(), composeResult.conflict.core)); + if (!summarized.count(state->constraints.path().getBlocks().back())) { + summarized.insert(state->constraints.path().getBlocks().back()); + interpreterHandler->incSummarizedLocations(); + } + } + } +} + +void Executor::closeProofObligation(ProofObligation *pob) { + objectManager->removePob(pob->root); +} + +void Executor::initializeIsolated(ref action) { + auto state = objectManager->initializeState(action->location, action->targets); + assert(state->isolated); + prepareSymbolicArgs(*state, state->stack.valueStack().back()); } const KInstruction *Executor::getKInst(const llvm::Instruction *inst) const { @@ -3997,7 +4622,7 @@ const KFunction *Executor::getKFunction(const llvm::Function *f) const { } void Executor::seed(ExecutionState &initialState) { - std::vector &v = seedMap[&initialState]; + std::vector &v = seedMap->at(&initialState); for (std::vector::const_iterator it = usingSeeds->begin(), ie = usingSeeds->end(); @@ -4007,19 +4632,20 @@ void Executor::seed(ExecutionState &initialState) { int lastNumSeeds = usingSeeds->size() + 10; time::Point lastTime, startTime = lastTime = time::getWallTime(); ExecutionState *lastState = 0; - while (!seedMap.empty()) { + while (!seedMap->empty()) { if (haltExecution) { doDumpStates(); return; } std::map>::iterator it = - seedMap.upper_bound(lastState); - if (it == seedMap.end()) - it = seedMap.begin(); + seedMap->upper_bound(lastState); + if (it == seedMap->end()) + it = seedMap->begin(); lastState = it->first; ExecutionState &state = *lastState; KInstruction *ki = state.pc; + objectManager->setCurrentState(&state); stepInstruction(state); executeInstruction(state, ki); @@ -4028,13 +4654,13 @@ void Executor::seed(ExecutionState &initialState) { dumpStates(); if (::dumpPForest) dumpPForest(); - updateStates(&state); + objectManager->updateSubscribers(); if ((stats::instructions % 1000) == 0) { int numSeeds = 0, numStates = 0; for (std::map>::iterator - it = seedMap.begin(), - ie = seedMap.end(); + it = seedMap->begin(), + ie = seedMap->end(); it != ie; ++it) { numSeeds += it->second.size(); numStates++; @@ -4054,7 +4680,8 @@ void Executor::seed(ExecutionState &initialState) { } } - klee_message("seeding done (%d states remain)", (int)states.size()); + klee_message("seeding done (%d states remain)", + (int)objectManager->getStates().size()); if (OnlySeed) { doDumpStates(); @@ -4062,82 +4689,112 @@ void Executor::seed(ExecutionState &initialState) { } } +void Executor::reportProgressTowardsTargets(std::string prefix, + const SetOfStates &states) const { + klee_message("%zu %sstates remaining", states.size(), prefix.c_str()); + TargetHashMap distancesTowardsTargets; + for (auto &state : states) { + for (auto &p : *state->targetForest.getTopLayer()) { + auto target = p.first; + auto distance = distanceCalculator->getDistance(*state, target); + auto it = distancesTowardsTargets.find(target); + if (it == distancesTowardsTargets.end()) + distancesTowardsTargets.insert(it, std::make_pair(target, distance)); + else { + distance = std::min(distance, it->second); + distancesTowardsTargets[target] = distance; + } + } + } + klee_message("Distances from nearest %sstates to remaining targets:", + prefix.c_str()); + for (auto &p : distancesTowardsTargets) { + auto target = p.first; + auto distance = p.second; + klee_message("%s for %s (lines %d to %d)", distance.toString().c_str(), + target->toString().c_str(), + target->getBlock()->getFirstInstruction()->info->line, + target->getBlock()->getLastInstruction()->info->line); + } +} + +void Executor::reportProgressTowardsTargets() const { + reportProgressTowardsTargets("", objectManager->getStates()); +} + void Executor::run(std::vector initialStates) { // Delay init till now so that ticks don't accrue during optimization and // such. if (guidanceKind != GuidanceKind::ErrorGuidance) timers.reset(); - states.insert(initialStates.begin(), initialStates.end()); - if (usingSeeds) { assert(initialStates.size() == 1); ExecutionState *initialState = initialStates.back(); seed(*initialState); } - searcher = constructUserSearcher(*this); - - std::vector newStates(states.begin(), states.end()); - searcher->update(0, newStates, std::vector()); - - // main interpreter loop - while (!states.empty() && !haltExecution) { - while (!searcher->empty() && !haltExecution) { - ExecutionState &state = searcher->selectState(); - KInstruction *prevKI = state.prevPC; - KFunction *kf = prevKI->parent->parent; - - if (prevKI->inst->isTerminator() && kmodule->inMainModule(kf->function)) { - targetCalculator->update(state); - } + if (ExecutionMode == ExecutionKind::Forward) { + searcher = + std::make_unique(constructUserSearcher(*this)); + } else { + Searcher *forward = constructUserSearcher(*this); + Searcher *branch = constructUserSearcher(*this, false); + BackwardSearcher *backward = constructUserBackwardSearcher(); + Initializer *initializer = + new ConflictCoreInitializer(codeGraphDistance.get()); + searcher = std::make_unique(forward, branch, + backward, initializer); + } - executeStep(state); - } + objectManager->addSubscriber(searcher.get()); - if (searcher->empty()) - haltExecution = HaltExecution::NoMoreStates; + if (targetManager) { + objectManager->addSubscriber(targetManager.get()); + } + if (targetedExecutionManager) { + objectManager->addSubscriber(targetedExecutionManager.get()); } - if (guidanceKind == GuidanceKind::ErrorGuidance) { - bool canReachNew1 = decreaseConfidenceFromStoppedStates(pausedStates); - bool canReachNew2 = - decreaseConfidenceFromStoppedStates(states, haltExecution); + objectManager->initialUpdate(); - for (auto &startBlockAndWhiteList : targets) { - startBlockAndWhiteList.second.reportFalsePositives(canReachNew1 || - canReachNew2); - } + // main interpreter loop + while (!haltExecution && !searcher->empty()) { + auto action = searcher->selectAction(); + executeAction(action); + objectManager->updateSubscribers(); - if (searcher->empty()) - haltExecution = HaltExecution::NoMoreStates; + if (!checkMemoryUsage()) { + // update searchers when states were terminated early due to memory + // pressure + objectManager->updateSubscribers(); + } } - delete searcher; - searcher = nullptr; + updateConfidenceRates(); doDumpStates(); haltExecution = HaltExecution::NotHalt; } -void Executor::runWithTarget(ExecutionState &state, KFunction *kf, - KBlock *target) { - if (pathWriter) - state.pathOS = pathWriter->open(); - if (symPathWriter) - state.symPathOS = symPathWriter->open(); +// void Executor::runWithTarget(ExecutionState &state, KFunction *kf, +// KBlock *target) { +// if (pathWriter) +// state.pathOS = pathWriter->open(); +// if (symPathWriter) +// state.symPathOS = symPathWriter->open(); - if (statsTracker) - statsTracker->framePushed(state, 0); +// if (statsTracker) +// statsTracker->framePushed(state, 0); - processForest = std::make_unique(); - processForest->addRoot(&state); - targetedRun(state, target); - processForest = nullptr; +// processForest = std::make_unique(); +// processForest->addRoot(&state); +// targetedRun(state, target); +// processForest = nullptr; - if (statsTracker) - statsTracker->done(); -} +// if (statsTracker) +// statsTracker->done(); +// } void Executor::initializeTypeManager() { switch (TypeSystem) { @@ -4153,76 +4810,64 @@ void Executor::initializeTypeManager() { typeSystemManager->initModule(); } -void Executor::executeStep(ExecutionState &state) { - KInstruction *ki = state.pc; - stepInstruction(state); - - executeInstruction(state, ki); - timers.invoke(); - if (::dumpStates) - dumpStates(); - if (::dumpPForest) - dumpPForest(); - - updateStates(&state); - - if (!checkMemoryUsage()) { - // update searchers when states were terminated early due to memory pressure - updateStates(nullptr); - } -} - -void Executor::targetedRun(ExecutionState &initialState, KBlock *target, - ExecutionState **resultState) { - // Delay init till now so that ticks don't accrue during optimization and - // such. - if (guidanceKind != GuidanceKind::ErrorGuidance) - timers.reset(); - - states.insert(&initialState); - - TargetedSearcher *targetedSearcher = - new TargetedSearcher(Target::create(target), *codeGraphDistance); - searcher = targetedSearcher; - - std::vector newStates(states.begin(), states.end()); - searcher->update(0, newStates, std::vector()); - // main interpreter loop - KInstruction *terminator = - target != nullptr ? target->getFirstInstruction() : nullptr; - while (!searcher->empty() && !haltExecution) { - ExecutionState &state = searcher->selectState(); - - KInstruction *ki = state.pc; - - if (ki == terminator) { - *resultState = state.copy(); - terminateStateOnTerminator(state); - updateStates(&state); - haltExecution = HaltExecution::ReachedTarget; - break; - } - - executeStep(state); - } - - delete searcher; - searcher = nullptr; - - doDumpStates(); - if (*resultState) - haltExecution = HaltExecution::NotHalt; -} +// void Executor::targetedRun(ExecutionState &initialState, KBlock *target, +// ExecutionState **resultState) { +// // Delay init till now so that ticks don't accrue during optimization and +// // such. +// if (guidanceKind != GuidanceKind::ErrorGuidance) +// timers.reset(); + + +// states.insert(&initialState); + +// TargetedSearcher *targetedSearcher = +// new TargetedSearcher(Target::create(target), *distanceCalculator); +// searcher = targetedSearcher; + +// std::vector newStates(states.begin(), states.end()); +// searcher->update(0, newStates, std::vector()); +// // main interpreter loop +// KInstruction *terminator = +// target != nullptr ? target->getFirstInstruction() : nullptr; +// while (!searcher->empty() && !haltExecution) { +// ExecutionState &state = searcher->selectState(); + +// KInstruction *ki = state.pc; + +// if (ki == terminator) { +// *resultState = state.copy(); +// terminateStateOnTerminator(state); +// updateStates(&state); +// haltExecution = HaltExecution::ReachedTarget; +// break; +// } + +// executeStep(state); +// } + +// delete searcher; +// searcher = nullptr; + +// doDumpStates(); +// if (*resultState) +// haltExecution = HaltExecution::NotHalt; +// } std::string Executor::getAddressInfo(ExecutionState &state, ref address, + unsigned size, const MemoryObject *mo) const { std::string Str; llvm::raw_string_ostream info(Str); + address = + Simplificator::simplifyExpr(state.constraints.cs(), address).simplified; info << "\taddress: " << address << "\n"; if (state.isGEPExpr(address)) { ref base = state.gepExprBases[address].first; info << "\tbase: " << base << "\n"; } + if (size) { + info << "\tsize: " << size << "\n"; + } uint64_t example; if (ConstantExpr *CE = dyn_cast(address)) { @@ -4280,6 +4925,10 @@ HaltExecution::Reason fromStateTerminationType(StateTerminationType t) { return HaltExecution::MaxStackFrames; case StateTerminationType::Solver: return HaltExecution::MaxSolverTime; + case StateTerminationType::MaxCycles: + return HaltExecution::MaxCycles; + case StateTerminationType::Interrupted: + return HaltExecution::Interrupt; default: return HaltExecution::Unspecified; } @@ -4288,27 +4937,25 @@ HaltExecution::Reason fromStateTerminationType(StateTerminationType t) { void Executor::terminateState(ExecutionState &state, StateTerminationType terminationType) { state.terminationReasonType = fromStateTerminationType(terminationType); + if (terminationType >= StateTerminationType::MaxDepth && + terminationType < StateTerminationType::MissedAllTargets) { + SetOfStates states = {&state}; + decreaseConfidenceFromStoppedStates(states, state.terminationReasonType); + } if (replayKTest && replayPosition != replayKTest->numObjects) { klee_warning_once(replayKTest, "replay did not consume all objects in test input."); } - std::vector::iterator itr = - std::find(removedStates.begin(), removedStates.end(), &state); - - if (itr != removedStates.end()) { - klee_warning("remove state twice"); - return; + if (!state.isolated) { + interpreterHandler->incPathsExplored(); } - interpreterHandler->incPathsExplored(); - - state.pc = state.prevPC; - removedStates.push_back(&state); + objectManager->removeState(&state); } static bool shouldWriteTest(const ExecutionState &state) { - return !OnlyOutputStatesCoveringNew || state.coveredNew; + return !state.isolated && (!OnlyOutputStatesCoveringNew || state.coveredNew); } static std::string terminationTypeFileExtension(StateTerminationType type) { @@ -4326,12 +4973,15 @@ static std::string terminationTypeFileExtension(StateTerminationType type) { void Executor::terminateStateOnExit(ExecutionState &state) { auto terminationType = StateTerminationType::Exit; - if (shouldWriteTest(state) || (AlwaysOutputSeeds && seedMap.count(&state))) + if (shouldWriteTest(state) || (AlwaysOutputSeeds && seedMap->count(&state))) interpreterHandler->processTestCase( state, nullptr, terminationTypeFileExtension(terminationType).c_str()); - interpreterHandler->incPathsCompleted(); - targetCalculator->update(state); + if (!state.isolated) { + interpreterHandler->incPathsCompleted(); + targetCalculator->update(state); + } + terminateState(state, terminationType); } @@ -4339,16 +4989,18 @@ void Executor::terminateStateEarly(ExecutionState &state, const Twine &message, StateTerminationType terminationType) { if ((terminationType <= StateTerminationType::EXECERR && shouldWriteTest(state)) || - (AlwaysOutputSeeds && seedMap.count(&state))) { + (AlwaysOutputSeeds && seedMap->count(&state))) { interpreterHandler->processTestCase( state, (message + "\n").str().c_str(), - terminationTypeFileExtension(terminationType).c_str()); + terminationTypeFileExtension(terminationType).c_str(), + terminationType > StateTerminationType::EARLY && + terminationType <= StateTerminationType::EXECERR); } terminateState(state, terminationType); } void Executor::terminateStateOnTerminator(ExecutionState &state) { - if (shouldWriteTest(state) || (AlwaysOutputSeeds && seedMap.count(&state))) { + if (shouldWriteTest(state) || (AlwaysOutputSeeds && seedMap->count(&state))) { interpreterHandler->processTestCase(state, nullptr, nullptr); } targetCalculator->update(state); @@ -4365,8 +5017,9 @@ Executor::getLastNonKleeInternalInstruction(const ExecutionState &state, Instruction **lastInstruction) { // unroll the stack of the applications state and find // the last instruction which is not inside a KLEE internal function - ExecutionState::stack_ty::const_reverse_iterator it = state.stack.rbegin(), - itE = state.stack.rend(); + ExecutionStack::call_stack_ty::const_reverse_iterator + it = state.stack.callStack().rbegin(), + itE = state.stack.callStack().rend(); // don't check beyond the outermost function (i.e. main()) itE--; @@ -4414,9 +5067,9 @@ void Executor::reportStateOnTargetError(ExecutionState &state, ReachWithError error) { if (guidanceKind == GuidanceKind::ErrorGuidance) { bool reportedTruePositive = - targetedExecutionManager.reportTruePositive(state, error); + targetedExecutionManager->reportTruePositive(state, error); if (!reportedTruePositive) { - targetedExecutionManager.reportFalseNegative(state, error); + targetedExecutionManager->reportFalseNegative(state, error); } } } @@ -4472,7 +5125,8 @@ void Executor::terminateStateOnError(ExecutionState &state, const InstructionInfo &ii = getLastNonKleeInternalInstruction(state, &lastInst); - if (EmitAllErrors || + if (!state.isolated && + EmitAllErrors || emittedErrors.insert(std::make_pair(lastInst, message)).second) { if (!ii.file.empty()) { klee_message("ERROR: %s:%d: %s", ii.file.c_str(), ii.line, @@ -4493,7 +5147,7 @@ void Executor::terminateStateOnError(ExecutionState &state, } msg << "State: " << state.getID() << '\n'; } - msg << "Stack: \n"; + msg << "ExecutionStack: \n"; state.dumpStack(msg); std::string info_str = info.str(); @@ -4503,10 +5157,13 @@ void Executor::terminateStateOnError(ExecutionState &state, const std::string ext = terminationTypeFileExtension(terminationType); // use user provided suffix from klee_report_error() const char *file_suffix = suffix ? suffix : ext.c_str(); - interpreterHandler->processTestCase(state, msg.str().c_str(), file_suffix); + interpreterHandler->processTestCase(state, msg.str().c_str(), file_suffix, + true); } - targetCalculator->update(state); + if (!state.isolated) { + targetCalculator->update(state); + } terminateState(state, terminationType); if (shouldExitOn(terminationType)) @@ -4787,7 +5444,7 @@ ObjectState *Executor::bindObjectInState(ExecutionState &state, // matter because all we use this list for is to unbind the object // on function return. if (isLocal && state.stack.size() > 0) { - state.stack.back().allocas.push_back(mo->id); + state.stack.valueStack().back().allocas.push_back(mo->id); } return os; } @@ -4800,10 +5457,17 @@ void Executor::executeAlloc(ExecutionState &state, ref size, bool isLocal, if (allocationAlignment == 0) { allocationAlignment = getAllocationAlignment(allocSite); } - ref upperBoundSizeConstraint = UleExpr::create( - ZExtExpr::create(size, Context::get().getPointerWidth()), - Expr::createPointer(isa(size) ? MaxConstantAllocationSize - : MaxSymbolicAllocationSize)); + + if (!isa(size) && !UseSymbolicSizeAllocation && !state.isolated) { + size = toConstant(state, size, "symbolic size allocation"); + } + + ref upperBoundSizeConstraint = Expr::createTrue(); + if (!isa(size)) { + upperBoundSizeConstraint = UleExpr::create( + ZExtExpr::create(size, Context::get().getPointerWidth()), + Expr::createPointer(MaxSymbolicAllocationSize)); + } /* If size greater then upper bound for size, then we will follow the malloc semantic and return NULL. Otherwise continue execution. */ @@ -4819,6 +5483,10 @@ void Executor::executeAlloc(ExecutionState &state, ref size, bool isLocal, } if (inBounds != PValidity::MustBeFalse && inBounds != PValidity::MayBeFalse) { + if (inBounds != PValidity::MustBeTrue) { + addConstraint(state, upperBoundSizeConstraint); + } + MemoryObject *mo = allocate(state, size, isLocal, /*isGlobal=*/false, allocSite, allocationAlignment); if (!mo) { @@ -4839,21 +5507,8 @@ void Executor::executeAlloc(ExecutionState &state, ref size, bool isLocal, Expr::createPointer(0), address); } - if (inBounds == PValidity::MustBeTrue) { - bindLocal(target, state, address); - } else { - if (OutOfMemAllocs) { - ref isNull = EqExpr::create(address, Expr::createPointer(0)); - addConstraint(state, EqExpr::create(upperBoundSizeConstraint, - Expr::createIsZero(isNull))); - ref result = SelectExpr::create( - upperBoundSizeConstraint, address, Expr::createPointer(0)); - bindLocal(target, state, result); - } else { - addConstraint(state, upperBoundSizeConstraint); - bindLocal(target, state, address); - } - } + // state.addPointerResolution(address, mo); + bindLocal(target, state, address); if (reallocFrom) { unsigned count = std::min(reallocFrom->size, os->size); @@ -4865,7 +5520,7 @@ void Executor::executeAlloc(ExecutionState &state, ref size, bool isLocal, } } } else { - bindLocal(target, state, Expr::createPointer(0)); + terminateStateEarly(state, "", StateTerminationType::SilentExit); } } @@ -4873,7 +5528,7 @@ void Executor::executeFree(ExecutionState &state, ref address, KInstruction *target) { address = optimizer.optimizeExpr(address, true); StatePair zeroPointer = - fork(state, Expr::createIsZero(address), true, BranchType::Free); + forkInternal(state, Expr::createIsZero(address), BranchType::Free); if (zeroPointer.first) { if (target) bindLocal(target, *zeroPointer.first, Expr::createPointer(0)); @@ -4911,31 +5566,74 @@ void Executor::executeFree(ExecutionState &state, ref address, } } -bool Executor::resolveExact(ExecutionState &state, ref p, KType *type, - ExactResolutionList &results, +bool Executor::resolveExact(ExecutionState &estate, ref address, + KType *type, ExactResolutionList &results, const std::string &name) { + ref base = address; + + if (estate.isGEPExpr(address)) { + base = estate.gepExprBases[address].first; + } + + if (SimplifySymIndices) { + if (!isa(address)) + address = Simplificator::simplifyExpr(estate.constraints.cs(), address) + .simplified; + if (!isa(base)) + base = + Simplificator::simplifyExpr(estate.constraints.cs(), base).simplified; + } + + address = optimizer.optimizeExpr(address, true); + base = optimizer.optimizeExpr(base, true); + + ref uniqueBase = + Simplificator::simplifyExpr(estate.constraints.cs(), base).simplified; + uniqueBase = toUnique(estate, uniqueBase); + + StatePair branches = + forkInternal(estate, Expr::createIsZero(base), BranchType::MemOp); + ExecutionState *bound = branches.first; + if (bound) { + if (!isReadFromSymbolicArray(uniqueBase) || + guidanceKind != GuidanceKind::ErrorGuidance) { + terminateStateOnTargetError(*bound, ReachWithError::NullPointerException); + } else { + terminateStateEarly(*bound, "", StateTerminationType::SilentExit); + } + } + if (!branches.second) { + address = Expr::createPointer(0); + } + + ExecutionState &state = *branches.second; + ResolutionList rl; bool mayBeOutOfBound = true; bool hasLazyInitialized = false; bool incomplete = false; /* We do not need this variable here, just a placeholder for resolve */ - bool success = - resolveMemoryObjects(state, p, type, state.prevPC, 0, rl, mayBeOutOfBound, - hasLazyInitialized, incomplete); + bool success = resolveMemoryObjects( + state, address, type, state.prevPC, 0, rl, mayBeOutOfBound, + hasLazyInitialized, incomplete, + LazyInitialization == LazyInitializationPolicy::Only); assert(success); ExecutionState *unbound = &state; - for (ResolutionList::iterator it = rl.begin(), ie = rl.end(); it != ie; - ++it) { - const MemoryObject *mo = unbound->addressSpace.findObject(*it).first; - ref inBounds = EqExpr::create(p, mo->getBaseExpr()); - + for (unsigned i = 0; i < rl.size(); ++i) { + const MemoryObject *mo = unbound->addressSpace.findObject(rl.at(i)).first; + ref inBounds; + if (i + 1 == rl.size() && hasLazyInitialized) { + inBounds = Expr::createTrue(); + } else { + inBounds = EqExpr::create(address, mo->getBaseExpr()); + } StatePair branches = - fork(*unbound, inBounds, true, BranchType::ResolvePointer); + forkInternal(*unbound, inBounds, BranchType::ResolvePointer); if (branches.first) - results.push_back(std::make_pair(*it, branches.first)); + results.push_back(std::make_pair(rl.at(i), branches.first)); unbound = branches.second; if (!unbound) // Fork failure @@ -4952,7 +5650,7 @@ bool Executor::resolveExact(ExecutionState &state, ref p, KType *type, } else { terminateStateOnError(*unbound, "memory error: invalid pointer: " + name, StateTerminationType::Ptr, - getAddressInfo(*unbound, p)); + getAddressInfo(*unbound, address)); } } return true; @@ -4973,7 +5671,7 @@ bool Executor::computeSizes(ExecutionState &state, ref size, bool success = solver->getResponse( state.constraints.cs(), UgtExpr::create(symbolicSizesSum, - ConstantExpr::create(SymbolicAllocationThreshhold, + ConstantExpr::create(SymbolicAllocationThreshold, symbolicSizesSum->getWidth())), response, state.queryMetaData); solver->setTimeout(time::Span()); @@ -5039,7 +5737,8 @@ MemoryObject *Executor::allocate(ExecutionState &state, ref size, lazyInitializationSource ? cast( new LazyInitializedAddressSymcrete(addressExpr)) - : cast(new AllocAddressSymcrete(addressExpr)); + : cast( + new AllocAddressSymcrete(addressExpr)); ref sizeSymcrete = lazyInitializationSource ? cast(new LazyInitializedSizeSymcrete( @@ -5149,15 +5848,34 @@ bool Executor::resolveMemoryObjects( for (auto resolution : state.resolvedPointers.at(address)) { mayBeResolvedMemoryObjects.push_back(resolution); } + } else if (state.resolvedPointers.count(base)) { + for (auto resolution : state.resolvedPointers.at(base)) { + mayBeResolvedMemoryObjects.push_back(resolution); + } } else { // we are on an error path (no resolution, multiple resolution, one // resolution with out of bounds) - bool baseWasResolved = state.resolvedPointers.count(base) == 0; address = optimizer.optimizeExpr(address, true); ref checkOutOfBounds = Expr::createTrue(); - if (!onlyLazyInitialize) { + bool checkAddress = + isa(address) || isa(address) || base != address; + if (!checkAddress && isa(address)) { + checkAddress = true; + std::vector> alternatives; + ArrayExprHelper::collectAlternatives(*cast(address), + alternatives); + for (auto alt : alternatives) { + checkAddress &= isa(alt) || isa(alt) || + isa(alt) || state.isGEPExpr(alt); + } + } + + mayLazyInitialize = LazyInitialization != LazyInitializationPolicy::None && + !isa(base); + + if (!onlyLazyInitialize || !mayLazyInitialize) { ResolutionList rl; ResolutionList rlSkipped; @@ -5173,31 +5891,10 @@ bool Executor::resolveMemoryObjects( ref inBounds = mo->getBoundsCheckPointer(address, bytes); ref notInBounds = Expr::createIsZero(inBounds); mayBeResolvedMemoryObjects.push_back(mo->id); - state.addPointerResolution(address, mo); - state.addPointerResolution(base, mo); checkOutOfBounds = AndExpr::create(checkOutOfBounds, notInBounds); } } - ref baseUniqueExpr = toUnique(state, base); - - bool checkAddress = isa(address) || isa(address) || - state.isGEPExpr(address); - if (!checkAddress && isa(address)) { - checkAddress = true; - std::vector> alternatives; - ArrayExprHelper::collectAlternatives(*cast(address), - alternatives); - for (auto alt : alternatives) { - checkAddress &= isa(alt) || isa(alt) || - isa(alt) || state.isGEPExpr(alt); - } - } - - mayLazyInitialize = LazyInitialization != LazyInitializationPolicy::None && - !isa(baseUniqueExpr) && baseWasResolved && - checkAddress; - if (mayLazyInitialize) { solver->setTimeout(coreSolverTimeout); bool success = solver->mayBeTrue(state.constraints.cs(), checkOutOfBounds, @@ -5207,8 +5904,10 @@ bool Executor::resolveMemoryObjects( return false; } else if (mayLazyInitialize) { IDType idLazyInitialization; - if (!lazyInitializeObject(state, base, target, baseTargetType, size, - idLazyInitialization)) { + uint64_t minObjectSize = MinNumberElementsLazyInit * size; + if (!lazyInitializeObject(state, base, target, baseTargetType, + minObjectSize, false, idLazyInitialization, + state.isolated || UseSymbolicSizeLazyInit)) { return false; } // Lazy initialization might fail if we've got unappropriate address @@ -5216,8 +5915,6 @@ bool Executor::resolveMemoryObjects( ObjectPair pa = state.addressSpace.findObject(idLazyInitialization); const MemoryObject *mo = pa.first; mayBeResolvedMemoryObjects.push_back(mo->id); - state.addPointerResolution(address, mo); - state.addPointerResolution(base, mo); } else { mayLazyInitialize = false; } @@ -5230,7 +5927,7 @@ bool Executor::resolveMemoryObjects( bool Executor::checkResolvedMemoryObjects( ExecutionState &state, ref address, KInstruction *target, unsigned bytes, const std::vector &mayBeResolvedMemoryObjects, - std::vector &resolvedMemoryObjects, + bool hasLazyInitialized, std::vector &resolvedMemoryObjects, std::vector> &resolveConditions, std::vector> &unboundConditions, ref &checkOutOfBounds, bool &mayBeOutOfBound) { @@ -5245,45 +5942,164 @@ bool Executor::checkResolvedMemoryObjects( } checkOutOfBounds = Expr::createTrue(); - for (unsigned int i = 0; i < mayBeResolvedMemoryObjects.size(); ++i) { + if (mayBeResolvedMemoryObjects.size() == 1) { const MemoryObject *mo = - state.addressSpace.findObject(mayBeResolvedMemoryObjects.at(i)).first; + state.addressSpace.findObject(*mayBeResolvedMemoryObjects.begin()) + .first; + + state.addPointerResolution(address, mo); + state.addPointerResolution(base, mo); ref inBounds = mo->getBoundsCheckPointer(address, bytes); ref baseInBounds = Expr::createTrue(); + ref notInBounds = Expr::createIsZero(inBounds); - if (state.isGEPExpr(address)) { + if (base != address || size != bytes) { baseInBounds = AndExpr::create(baseInBounds, mo->getBoundsCheckPointer(base, size)); + } + + if (hasLazyInitialized && !state.isolated) { baseInBounds = AndExpr::create( baseInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); } - ref notInBounds = Expr::createIsZero(inBounds); + inBounds = AndExpr::create(inBounds, baseInBounds); + inBounds = optimizer.optimizeExpr(inBounds, true); + inBounds = Simplificator::simplifyExpr(state.constraints.cs(), inBounds) + .simplified; + notInBounds = + Simplificator::simplifyExpr(state.constraints.cs(), notInBounds) + .simplified; - bool mayBeInBounds; + PartialValidity result; solver->setTimeout(coreSolverTimeout); - bool success = solver->mayBeTrue(state.constraints.cs(), - AndExpr::create(inBounds, baseInBounds), - mayBeInBounds, state.queryMetaData); + bool success = solver->evaluate(state.constraints.cs(), inBounds, result, + state.queryMetaData); solver->setTimeout(time::Span()); if (!success) { return false; } - if (!mayBeInBounds) { - continue; + + mayBeOutOfBound = PValidity::MustBeFalse == result || + PValidity::MayBeFalse == result || + PValidity::TrueOrFalse == result; + bool mayBeInBound = PValidity::MustBeTrue == result || + PValidity::MayBeTrue == result || + PValidity::TrueOrFalse == result; + bool mustBeInBounds = PValidity::MustBeTrue == result; + bool mustBeOutOfBound = PValidity::MustBeFalse == result; + + if (mayBeInBound) { + state.addPointerResolution(address, mo, bytes); + state.addPointerResolution(base, mo, size); + resolvedMemoryObjects.push_back(mo->id); + if (mustBeInBounds) { + resolveConditions.push_back(Expr::createTrue()); + unboundConditions.push_back(Expr::createFalse()); + checkOutOfBounds = Expr::createFalse(); + } else { + resolveConditions.push_back(inBounds); + unboundConditions.push_back(notInBounds); + if (hasLazyInitialized) { + if (state.isolated) { + baseInBounds = AndExpr::create( + baseInBounds, + Expr::createIsZero(URemExpr::create( + mo->getOffsetExpr(base), Expr::createPointer(size)))); + + } else { + notInBounds = AndExpr::create( + notInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); + } + } + checkOutOfBounds = notInBounds; + } + } else if (mayBeOutOfBound) { + if (mustBeOutOfBound) { + checkOutOfBounds = Expr::createTrue(); + } else { + if (hasLazyInitialized) { + if (state.isolated) { + baseInBounds = AndExpr::create( + baseInBounds, + Expr::createIsZero(URemExpr::create( + mo->getOffsetExpr(base), Expr::createPointer(size)))); + + } else { + notInBounds = AndExpr::create( + notInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); + } + } + checkOutOfBounds = notInBounds; + } } + } else { + for (unsigned int i = 0; i < mayBeResolvedMemoryObjects.size(); ++i) { + const MemoryObject *mo = + state.addressSpace.findObject(mayBeResolvedMemoryObjects.at(i)).first; - state.addPointerResolution(address, mo, bytes); - state.addPointerResolution(base, mo, size); + state.addPointerResolution(address, mo); + state.addPointerResolution(base, mo); - inBounds = AndExpr::create(inBounds, baseInBounds); + ref inBounds = mo->getBoundsCheckPointer(address, bytes); + ref baseInBounds = Expr::createTrue(); + ref notInBounds = Expr::createIsZero(inBounds); - resolveConditions.push_back(inBounds); - resolvedMemoryObjects.push_back(mo->id); - unboundConditions.push_back(notInBounds); - if (mayBeOutOfBound) { - checkOutOfBounds = AndExpr::create(checkOutOfBounds, notInBounds); + if (base != address || size != bytes) { + baseInBounds = AndExpr::create(baseInBounds, + mo->getBoundsCheckPointer(base, size)); + baseInBounds = AndExpr::create( + baseInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); + } + + if (hasLazyInitialized && i == mayBeResolvedMemoryObjects.size() - 1 && !state.isolated) { + baseInBounds = AndExpr::create( + baseInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); + } + + inBounds = AndExpr::create(inBounds, baseInBounds); + inBounds = Simplificator::simplifyExpr(state.constraints.cs(), inBounds) + .simplified; + notInBounds = + Simplificator::simplifyExpr(state.constraints.cs(), notInBounds) + .simplified; + + bool mayBeInBounds; + solver->setTimeout(coreSolverTimeout); + bool success = solver->mayBeTrue(state.constraints.cs(), inBounds, + mayBeInBounds, state.queryMetaData); + solver->setTimeout(time::Span()); + if (!success) { + return false; + } + if (!mayBeInBounds) { + continue; + } + + state.addPointerResolution(address, mo, bytes); + state.addPointerResolution(base, mo, size); + + resolveConditions.push_back(inBounds); + resolvedMemoryObjects.push_back(mo->id); + unboundConditions.push_back(notInBounds); + + if (hasLazyInitialized && i == mayBeResolvedMemoryObjects.size() - 1) { + if (state.isolated) { + baseInBounds = AndExpr::create( + baseInBounds, + Expr::createIsZero(URemExpr::create(mo->getOffsetExpr(base), + Expr::createPointer(size)))); + + } else { + notInBounds = AndExpr::create( + notInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); + } + } + + if (mayBeOutOfBound) { + checkOutOfBounds = AndExpr::create(checkOutOfBounds, notInBounds); + } } } @@ -5296,9 +6112,11 @@ bool Executor::checkResolvedMemoryObjects( return false; } } + if (!mayBeOutOfBound) { checkOutOfBounds = Expr::createFalse(); } + return true; } @@ -5317,7 +6135,7 @@ bool Executor::makeGuard(ExecutionState &state, selectGuard = OrExpr::create(selectGuard, resolveConditions.at(i)); } if (hasLazyInitialized) { - ref head = resolveConditions.back(); + ref head = Expr::createIsZero(unboundConditions.back()); ref body = Expr::createTrue(); for (unsigned int j = 0; j < resolveConditions.size(); ++j) { if (resolveConditions.size() - 1 != j) { @@ -5355,15 +6173,11 @@ bool Executor::collectConcretizations( } for (unsigned int i = 0; i < resolvedMemoryObjects.size(); ++i) { - auto symcretization = concretizationManager->get(state.constraints.cs(), - resolveConditions.at(i)); - if (symcretization.second) { - Assignment assigment = symcretization.first; - resolveConcretizations.push_back(assigment); - } else { - resolveConcretizations.push_back(state.constraints.cs().concretization()); - } + Assignment concretization = computeConcretization( + state.constraints.cs(), resolveConditions.at(i), state.queryMetaData); + resolveConcretizations.push_back(concretization); } + return true; } @@ -5389,18 +6203,13 @@ void Executor::collectReads( const MemoryObject *mo = op.first; const ObjectState *os = op.second; - ObjectState *wos = state.addressSpace.getWriteable(mo, os); - - wos->getDynamicType()->handleMemoryAccess( - targetType, mo->getOffsetExpr(address), - ConstantExpr::alloc(size, Context::get().getPointerWidth()), true); - ref result = wos->read(mo->getOffsetExpr(address), type); + ref result = os->read(mo->getOffsetExpr(address), type); - if (MockMutableGlobals != MockMutableGlobalsPolicy::None && mo->isGlobal && - !wos->readOnly && isa(result) && - (MockMutableGlobals != MockMutableGlobalsPolicy::PrimitiveFields || - !targetType->getRawType()->isPointerTy())) { + if (MockMutableGlobals == MockMutableGlobalsPolicy::PrimitiveFields && + mo->isGlobal && !os->readOnly && isa(result) && + !targetType->getRawType()->isPointerTy()) { result = makeMockValue(state, "mockGlobalValue", result->getWidth()); + ObjectState *wos = state.addressSpace.getWriteable(mo, os); wos->write(mo->getOffsetExpr(address), result); } @@ -5408,6 +6217,30 @@ void Executor::collectReads( } } +void Executor::collectObjectStates( + ExecutionState &state, ref address, Expr::Width type, unsigned bytes, + const std::vector &resolvedMemoryObjects, + const std::vector &resolveConcretizations, + std::vector> &results) { + ref base = address; + unsigned size = bytes; + if (state.isGEPExpr(address)) { + base = state.gepExprBases[address].first; + size = kmodule->targetData->getTypeStoreSize( + state.gepExprBases[address].second); + } + + for (unsigned int i = 0; i < resolvedMemoryObjects.size(); ++i) { + updateStateWithSymcretes(state, resolveConcretizations[i]); + state.constraints.rewriteConcretization(resolveConcretizations[i]); + + ObjectPair op = state.addressSpace.findObject(resolvedMemoryObjects[i]); + const ObjectState *os = op.second; + + results.push_back(new ObjectState(*os)); + } +} + void Executor::executeMemoryOperation( ExecutionState &estate, bool isWrite, KType *targetType, ref address, ref value /* undef if read */, @@ -5430,12 +6263,24 @@ void Executor::executeMemoryOperation( } if (SimplifySymIndices) { - if (!isa(address)) + ref oldAddress = address; + ref oldbase = base; + if (!isa(address)) { address = Simplificator::simplifyExpr(estate.constraints.cs(), address) .simplified; - if (!isa(base)) + } + if (!isa(base)) { base = Simplificator::simplifyExpr(estate.constraints.cs(), base).simplified; + } + if (!isa(address) || base->isZero()) { + if (estate.isGEPExpr(oldAddress)) { + estate.gepExprBases[address] = { + base, + estate.gepExprBases[oldAddress].second, + }; + } + } if (isWrite && !isa(value)) value = Simplificator::simplifyExpr(estate.constraints.cs(), value) .simplified; @@ -5444,12 +6289,10 @@ void Executor::executeMemoryOperation( address = optimizer.optimizeExpr(address, true); base = optimizer.optimizeExpr(base, true); - ref uniqueBase = - Simplificator::simplifyExpr(estate.constraints.cs(), base).simplified; - uniqueBase = toUnique(estate, uniqueBase); + ref uniqueBase = toUnique(estate, base); StatePair branches = - fork(estate, Expr::createIsZero(base), true, BranchType::MemOp); + forkInternal(estate, Expr::createIsZero(base), BranchType::MemOp); ExecutionState *bound = branches.first; if (bound) { if (!isReadFromSymbolicArray(uniqueBase) || @@ -5465,12 +6308,12 @@ void Executor::executeMemoryOperation( // fast path: single in-bounds resolution IDType idFastResult; - bool success; + bool success = false; - if (state->resolvedPointers.count(address) && - state->resolvedPointers.at(address).size() == 1) { + if (state->resolvedPointers.count(base) && + state->resolvedPointers.at(base).size() == 1) { success = true; - idFastResult = *state->resolvedPointers[address].begin(); + idFastResult = *state->resolvedPointers[base].begin(); } else { solver->setTimeout(coreSolverTimeout); @@ -5493,14 +6336,15 @@ void Executor::executeMemoryOperation( } ref inBounds = mo->getBoundsCheckPointer(address, bytes); + ref baseInBounds = Expr::createTrue(); - if (state->isGEPExpr(address)) { - inBounds = - AndExpr::create(inBounds, mo->getBoundsCheckPointer(base, size)); - inBounds = AndExpr::create(inBounds, - Expr::createIsZero(mo->getOffsetExpr(base))); + if (base != address || size != bytes) { + baseInBounds = + AndExpr::create(baseInBounds, mo->getBoundsCheckPointer(base, size)); } + inBounds = AndExpr::create(inBounds, baseInBounds); + inBounds = optimizer.optimizeExpr(inBounds, true); inBounds = Simplificator::simplifyExpr(state->constraints.cs(), inBounds) .simplified; @@ -5510,13 +6354,13 @@ void Executor::executeMemoryOperation( bool success = solver->getResponse(state->constraints.cs(), inBounds, response, state->queryMetaData); solver->setTimeout(time::Span()); - bool mustBeInBounds = !isa(response); if (!success) { state->pc = state->prevPC; terminateStateOnSolverError(*state, "Query timed out (bounds check)."); return; } + bool mustBeInBounds = !isa(response); if (mustBeInBounds) { if (isa(response)) { addConstraint(*state, inBounds); @@ -5526,8 +6370,8 @@ void Executor::executeMemoryOperation( const ObjectState *os = op.second; state->addPointerResolution(base, mo); state->addPointerResolution(address, mo); - ObjectState *wos = state->addressSpace.getWriteable(mo, os); if (isWrite) { + ObjectState *wos = state->addressSpace.getWriteable(mo, os); wos->getDynamicType()->handleMemoryAccess( targetType, mo->getOffsetExpr(address), ConstantExpr::alloc(size, Context::get().getPointerWidth()), true); @@ -5538,19 +6382,16 @@ void Executor::executeMemoryOperation( wos->write(mo->getOffsetExpr(address), value); } } else { - wos->getDynamicType()->handleMemoryAccess( - targetType, mo->getOffsetExpr(address), - ConstantExpr::alloc(size, Context::get().getPointerWidth()), false); - result = wos->read(mo->getOffsetExpr(address), type); + result = os->read(mo->getOffsetExpr(address), type); if (interpreterOpts.MakeConcreteSymbolic) result = replaceReadWithSymbolic(*state, result); - if (MockMutableGlobals != MockMutableGlobalsPolicy::None && - mo->isGlobal && !wos->readOnly && isa(result) && - (MockMutableGlobals != MockMutableGlobalsPolicy::PrimitiveFields || - !targetType->getRawType()->isPointerTy())) { + if (MockMutableGlobals == MockMutableGlobalsPolicy::PrimitiveFields && + mo->isGlobal && !os->readOnly && isa(result) && + !targetType->getRawType()->isPointerTy()) { result = makeMockValue(*state, "mockGlobalValue", result->getWidth()); + ObjectState *wos = state->addressSpace.getWriteable(mo, os); wos->write(mo->getOffsetExpr(address), result); } @@ -5594,8 +6435,8 @@ void Executor::executeMemoryOperation( if (!checkResolvedMemoryObjects( *state, address, target, bytes, mayBeResolvedMemoryObjects, - resolvedMemoryObjects, resolveConditions, unboundConditions, - checkOutOfBounds, mayBeOutOfBound)) { + hasLazyInitialized, resolvedMemoryObjects, resolveConditions, + unboundConditions, checkOutOfBounds, mayBeOutOfBound)) { terminateStateOnSolverError(*state, "Query timed out (executeMemoryOperation)"); return; @@ -5648,24 +6489,26 @@ void Executor::executeMemoryOperation( ObjectState *wos = state->addressSpace.getWriteable(mo, os); if (wos->readOnly) { branches = - fork(*state, resolveConditions[i], true, BranchType::MemOp); + forkInternal(*state, Expr::createIsZero(unboundConditions[i]), + BranchType::MemOp); assert(branches.first); terminateStateOnError(*branches.first, "memory error: object read only", StateTerminationType::ReadOnly); state = branches.second; } else { - ref result = - SelectExpr::create(resolveConditions[i], value, results[i]); + ref result = SelectExpr::create( + Expr::createIsZero(unboundConditions[i]), value, results[i]); wos->write(mo->getOffsetExpr(address), result); } } } else { - ref result = results[resolveConditions.size() - 1]; - for (unsigned int i = 0; i < resolveConditions.size(); ++i) { - unsigned int index = resolveConditions.size() - 1 - i; - result = SelectExpr::create(resolveConditions[index], results[index], - result); + ref result = results[unboundConditions.size() - 1]; + for (unsigned int i = 0; i < unboundConditions.size(); ++i) { + unsigned int index = unboundConditions.size() - 1 - i; + result = + SelectExpr::create(Expr::createIsZero(unboundConditions[index]), + results[index], result); } bindLocal(target, *state, result); } @@ -5692,17 +6535,27 @@ void Executor::executeMemoryOperation( const MemoryObject *mo = op.first; const ObjectState *os = op.second; - bound->addPointerResolution(address, mo); - bound->addPointerResolution(base, mo); + if (hasLazyInitialized && i + 1 != resolvedMemoryObjects.size()) { + const MemoryObject *liMO = + bound->addressSpace + .findObject( + resolvedMemoryObjects[resolvedMemoryObjects.size() - 1]) + .first; + bound->removePointerResolutions(liMO); + bound->addressSpace.unbindObject(liMO); + } + + bound->addUniquePointerResolution(address, mo); + bound->addUniquePointerResolution(base, mo); /* FIXME: Notice, that here we are creating a new instance of object for every memory operation in order to handle type changes. This might waste too much memory as we do now always modify something. To fix this we can ask memory if we will make anything, and create a copy if required. */ - ObjectState *wos = bound->addressSpace.getWriteable(mo, os); if (isWrite) { + ObjectState *wos = bound->addressSpace.getWriteable(mo, os); wos->getDynamicType()->handleMemoryAccess( targetType, mo->getOffsetExpr(address), ConstantExpr::alloc(size, Context::get().getPointerWidth()), true); @@ -5713,16 +6566,13 @@ void Executor::executeMemoryOperation( wos->write(mo->getOffsetExpr(address), value); } } else { - wos->getDynamicType()->handleMemoryAccess( - targetType, mo->getOffsetExpr(address), - ConstantExpr::alloc(size, Context::get().getPointerWidth()), false); - ref result = wos->read(mo->getOffsetExpr(address), type); + ref result = os->read(mo->getOffsetExpr(address), type); - if (MockMutableGlobals != MockMutableGlobalsPolicy::None && - mo->isGlobal && !wos->readOnly && isa(result) && - (MockMutableGlobals != MockMutableGlobalsPolicy::PrimitiveFields || - !targetType->getRawType()->isPointerTy())) { + if (MockMutableGlobals == MockMutableGlobalsPolicy::PrimitiveFields && + mo->isGlobal && !os->readOnly && isa(result) && + !targetType->getRawType()->isPointerTy()) { result = makeMockValue(*bound, "mockGlobalValue", result->getWidth()); + ObjectState *wos = bound->addressSpace.getWriteable(mo, os); wos->write(mo->getOffsetExpr(address), result); } @@ -5744,15 +6594,14 @@ void Executor::executeMemoryOperation( assert(mayBeOutOfBound && "must be true since unbound is not null"); terminateStateOnError(*unbound, "memory error: out of bound pointer", StateTerminationType::Ptr, - getAddressInfo(*unbound, address)); + getAddressInfo(*unbound, address, bytes)); } } bool Executor::lazyInitializeObject(ExecutionState &state, ref address, const KInstruction *target, KType *targetType, uint64_t size, - IDType &id, bool isConstantSize, - bool isLocal) { + bool isLocal, IDType &id, bool isSymbolic) { assert(!isa(address)); const llvm::Value *allocSite = target ? target->inst : nullptr; std::pair, ref> moBasePair; @@ -5762,7 +6611,7 @@ bool Executor::lazyInitializeObject(ExecutionState &state, ref address, } ref sizeExpr; - if (size <= MaxSymbolicAllocationSize && !isConstantSize) { + if (size < MaxSymbolicAllocationSize && !isLocal && isSymbolic) { const Array *lazyInstantiationSize = makeArray( Expr::createPointer(Context::get().getPointerWidth() / CHAR_BIT), SourceBuilder::lazyInitializationSize(address)); @@ -5788,9 +6637,11 @@ bool Executor::lazyInitializeObject(ExecutionState &state, ref address, sizeExpr = Expr::createPointer(size); } - MemoryObject *mo = allocate(state, sizeExpr, isLocal, - /*isGlobal=*/false, allocSite, - /*allocationAlignment=*/8, address, timestamp); + ref addressExpr = isSymbolic ? address : nullptr; + MemoryObject *mo = + allocate(state, sizeExpr, isLocal, + /*isGlobal=*/false, allocSite, + /*allocationAlignment=*/8, addressExpr, timestamp); if (!mo) { return false; } @@ -5813,6 +6664,8 @@ bool Executor::lazyInitializeObject(ExecutionState &state, ref address, if (!mayBeLazyInitialized) { id = 0; } else { + address = + Simplificator::simplifyExpr(state.constraints.cs(), address).simplified; executeMakeSymbolic(state, mo, targetType, SourceBuilder::lazyInitializationContent(address), isLocal); @@ -5840,7 +6693,8 @@ IDType Executor::lazyInitializeLocalObject(ExecutionState &state, IDType id; bool success = lazyInitializeObject( state, address, target, typeSystemManager->getWrappedType(ai->getType()), - elementSize, id, isa(size), true); + elementSize, true, id, + state.isolated || UseSymbolicSizeLazyInit); assert(success); assert(id); auto op = state.addressSpace.findObject(id); @@ -5849,13 +6703,17 @@ IDType Executor::lazyInitializeLocalObject(ExecutionState &state, state.addConstraint(EqExpr::create(address, op.first->getBaseExpr()), {}); state.addConstraint( Expr::createIsZero(EqExpr::create(address, Expr::createPointer(0))), {}); + if (isa(size)) { + addConstraint(state, EqExpr::create(size, op.first->getSizeExpr())); + } return id; } IDType Executor::lazyInitializeLocalObject(ExecutionState &state, ref address, const KInstruction *target) { - return lazyInitializeLocalObject(state, state.stack.back(), address, target); + return lazyInitializeLocalObject(state, state.stack.valueStack().back(), + address, target); } void Executor::updateStateWithSymcretes(ExecutionState &state, @@ -5939,8 +6797,8 @@ void Executor::executeMakeSymbolic(ExecutionState &state, state.addSymbolic(mo, array, type); std::map>::iterator it = - seedMap.find(&state); - if (it != seedMap.end()) { // In seed mode we need to add this as a + seedMap->find(&state); + if (it != seedMap->end()) { // In seed mode we need to add this as a // binding. for (std::vector::iterator siit = it->second.begin(), siie = it->second.end(); @@ -6120,20 +6978,24 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv, return; } + processForest = std::make_unique(); + objectManager->addProcessForest(processForest.get()); + ExecutionState *state = formState(f, argc, argv, envp); + auto emptyState = state->copy(); + emptyState->popFrame(); + emptyState->isolated = true; + objectManager->setEmptyState(emptyState); bindModuleConstants(llvm::APFloat::rmNearestTiesToEven); + std::vector states; if (guidanceKind == GuidanceKind::ErrorGuidance) { KInstIterator caller; - if (kmodule->WithPOSIXRuntime()) { - state = prepareStateForPOSIX(caller, state->copy()); - } else { - state->popFrame(); - } + state->popFrame(); auto &paths = interpreterOpts.Paths.value(); - auto prepTargets = targetedExecutionManager.prepareTargets( + auto prepTargets = targetedExecutionManager->prepareTargets( kmodule.get(), std::move(paths)); if (prepTargets.empty()) { klee_warning( @@ -6153,14 +7015,14 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv, targets.emplace(kf, TargetedHaltsOnTraces(whitelist)); ExecutionState *initialState = state->withStackFrame(caller, kf); prepareSymbolicArgs(*initialState); - prepareTargetedExecution(initialState, whitelist); + prepareTargetedExecution(*initialState, whitelist); states.push_back(initialState); } - } else { - ExecutionState *initialState = state->copy(); - states.push_back(initialState); delete state; + } else { + states.push_back(state); } + state = nullptr; TreeOStream pathOS; TreeOStream symPathOS; @@ -6172,8 +7034,8 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv, symPathOS = symPathWriter->open(); } - processForest = std::make_unique(); - for (auto &state : states) { + for (auto state : states) { + objectManager->addInitialState(state); if (statsTracker) statsTracker->framePushed(*state, 0); @@ -6181,15 +7043,14 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv, state->pathOS = pathOS; if (symPathWriter) state->symPathOS = symPathOS; - - processForest->addRoot(state); } + summary.readFromFile(kmodule.get(), &arrayCache); + run(states); - processForest = nullptr; - if (statsTracker) - statsTracker->done(); + summary.dumpToFile(kmodule.get()); + objectManager->clear(); clearMemory(); clearGlobal(); @@ -6198,49 +7059,58 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv, statsTracker->done(); } -ExecutionState *Executor::prepareStateForPOSIX(KInstIterator &caller, - ExecutionState *state) { - Function *mainFn = kmodule->module->getFunction("__klee_posix_wrapped_main"); - - assert(mainFn && "__klee_posix_wrapped_main not found"); - KBlock *target = kmodule->functionMap[mainFn]->entryKBlock; - - if (pathWriter) - state->pathOS = pathWriter->open(); - if (symPathWriter) - state->symPathOS = symPathWriter->open(); - - if (statsTracker) - statsTracker->framePushed(*state, 0); - - processForest = std::make_unique(); - processForest->addRoot(state); - ExecutionState *original = state->copy(); - ExecutionState *initialState = nullptr; - state->targetForest.add(Target::create(target)); - targetedRun(*state, target, &initialState); - state = initialState; - if (state) { - auto frame = state->stack.back(); - caller = frame.caller; - state->popFrame(); - delete original; - } else { - state = original; - state->popFrame(); - } - - processForest = nullptr; - - if (statsTracker) - statsTracker->done(); - - return state; -} - -void Executor::prepareTargetedExecution(ExecutionState *initialState, +// ExecutionState *Executor::prepareStateForPOSIX(KInstIterator &caller, +// ExecutionState *state) { +// Function *mainFn = kmodule->module->getFunction("__klee_posix_wrapped_main"); + +// assert(mainFn && "__klee_posix_wrapped_main not found"); +// KBlock *target = kmodule->functionMap[mainFn]->entryKBlock; + +// if (pathWriter) +// state->pathOS = pathWriter->open(); +// if (symPathWriter) +// state->symPathOS = symPathWriter->open(); + +// if (statsTracker) +// statsTracker->framePushed(*state, 0); + +// processForest = std::make_unique(); +// processForest->addRoot(state); +// ExecutionState *original = state->copy(); +// ExecutionState *initialState = nullptr; +// state->targetForest.add(Target::create(target)); +// state->isTargeted = true; +// state->history = state->targetForest.getHistory(); +// state->targets = state->targetForest.getTargets(); +// state->prevHistory = state->history; +// state->prevTargets = state->targets; +// targetedRun(*state, target, &initialState); +// state = initialState; +// if (state) { +// auto frame = state->stack.back(); +// caller = frame.caller; +// state->popFrame(); +// delete original; +// } else { +// state = original; +// state->popFrame(); +// } + +// processForest = nullptr; + +// if (statsTracker) +// statsTracker->done(); + +// return state; +// } + +void Executor::prepareTargetedExecution(ExecutionState &initialState, ref whitelist) { - initialState->targetForest = *whitelist->deepCopy(); + initialState.targetForest = *whitelist->deepCopy(); + initialState.setTargeted(true); + initialState.setHistory(initialState.targetForest.getHistory()); + initialState.setTargets(initialState.targetForest.getTargets()); + initialState.stepTargetsAndHistory(); } bool isReturnValueFromInitBlock(const ExecutionState &state, @@ -6290,12 +7160,12 @@ void Executor::prepareMockValue(ExecutionState &state, const std::string &name, KInstruction *target) { Expr::Width width = kmodule->targetData->getTypeSizeInBits(target->inst->getType()); - prepareMockValue(state, state.stack.back(), name, width, target); + prepareMockValue(state, state.stack.valueStack().back(), name, width, target); } void Executor::prepareSymbolicValue(ExecutionState &state, KInstruction *target) { - prepareSymbolicValue(state, state.stack.back(), target); + prepareSymbolicValue(state, state.stack.valueStack().back(), target); } void Executor::prepareSymbolicRegister(ExecutionState &state, StackFrame &sf, @@ -6325,7 +7195,7 @@ void Executor::prepareSymbolicArgs(ExecutionState &state, StackFrame &frame) { } void Executor::prepareSymbolicArgs(ExecutionState &state) { - prepareSymbolicArgs(state, state.stack.back()); + prepareSymbolicArgs(state, state.stack.valueStack().back()); } unsigned Executor::getPathStreamID(const ExecutionState &state) { @@ -6375,18 +7245,24 @@ void Executor::getConstraintLog(const ExecutionState &state, std::string &res, void Executor::logState(const ExecutionState &state, int id, std::unique_ptr &f) { *f << "State number " << state.id << ". Test number: " << id << "\n\n"; + for (auto &object : state.addressSpace.objects) { + *f << "ID memory object: " << object.first->id << "\n"; + *f << "Address memory object: " << object.first->address << "\n"; + *f << "Memory object size: " << object.first->size << "\n"; + } *f << state.symbolics.size() << " symbolics total. " << "Symbolics:\n"; size_t sc = 0; - for (auto &i : state.symbolics) { + for (auto &symbolic : state.symbolics) { *f << "Symbolic number " << sc++ << "\n"; - *f << "Associated memory object: " << i.memoryObject.get()->id << "\n"; - *f << "Memory object size: " << i.memoryObject.get()->size << "\n"; + *f << "Associated memory object: " << symbolic.memoryObject.get()->id + << "\n"; + *f << "Memory object size: " << symbolic.memoryObject.get()->size << "\n"; } *f << "\n"; *f << "State constraints:\n"; - for (auto i : state.constraints.cs().cs()) { - i->print(*f); + for (auto constraint : state.constraints.cs().cs()) { + constraint->print(*f); *f << "\n"; } } @@ -6446,14 +7322,14 @@ void Executor::setInitializationGraph(const ExecutionState &state, continue; } - ref update_check; + ref updateCheck; if (auto e = dyn_cast(pointer.first)) { - update_check = e->getLeft(); + updateCheck = e->getLeft(); } else { - update_check = e; + updateCheck = e; } - if (auto e = dyn_cast(update_check)) { + if (auto e = dyn_cast(updateCheck)) { if (e->updates.getSize() != 0) { continue; } @@ -6486,7 +7362,7 @@ void Executor::setInitializationGraph(const ExecutionState &state, if (s[pointerIndex].count(o.offset) && s[pointerIndex][o.offset] != std::make_pair(o.index, o.indexOffset)) { - assert(0 && "wft"); + assert(0 && "unreachable"); } if (!s[pointerIndex].count(o.offset)) { pointers[pointerIndex].push_back(o); @@ -6505,6 +7381,24 @@ void Executor::setInitializationGraph(const ExecutionState &state, return; } +Assignment Executor::computeConcretization(const ConstraintSet &constraints, + ref condition, + SolverQueryMetaData &queryMetaData) { + Assignment concretization; + if (Query(constraints, condition).containsSymcretes()) { + ref response; + solver->setTimeout(coreSolverTimeout); + bool success = solver->getResponse( + constraints, Expr::createIsZero(condition), response, queryMetaData); + solver->setTimeout(time::Span()); + assert(success); + assert(isa(response)); + concretization = cast(response)->initialValuesFor( + constraints.gatherSymcretizedArrays()); + } + return concretization; +} + bool Executor::getSymbolicSolution(const ExecutionState &state, KTest &res) { solver->setTimeout(coreSolverTimeout); @@ -6530,10 +7424,11 @@ bool Executor::getSymbolicSolution(const ExecutionState &state, KTest &res) { // If the particular constraint operated on in this iteration through // the loop isn't implied then add it to the list of constraints. if (!mustBeTrue) { - auto symcretization = - concretizationManager->get(extendedConstraints.cs(), pi); - if (symcretization.second) { - extendedConstraints.addConstraint(pi, symcretization.first); + Assignment concretization = computeConcretization( + extendedConstraints.cs(), pi, state.queryMetaData); + + if (!concretization.isEmpty()) { + extendedConstraints.addConstraint(pi, concretization); } else { extendedConstraints.addConstraint(pi, {}); } @@ -6738,15 +7633,16 @@ void Executor::dumpStates() { auto os = interpreterHandler->openOutputFile("states.txt"); if (os) { - for (ExecutionState *es : states) { + for (ExecutionState *es : objectManager->getStates()) { *os << "(" << es << ","; *os << "["; - auto next = es->stack.begin(); + auto next = es->stack.callStack().begin(); ++next; - for (auto sfIt = es->stack.begin(), sf_ie = es->stack.end(); + for (auto sfIt = es->stack.callStack().begin(), + sf_ie = es->stack.callStack().end(); sfIt != sf_ie; ++sfIt) { *os << "('" << sfIt->kf->function->getName().str() << "',"; - if (next == es->stack.end()) { + if (next == es->stack.callStack().end()) { *os << es->prevPC->info->line << "), "; } else { *os << next->caller->info->line << "), "; @@ -6755,7 +7651,7 @@ void Executor::dumpStates() { } *os << "], "; - StackFrame &sf = es->stack.back(); + InfoStackFrame &sf = es->stack.infoStack().back(); uint64_t md2u = computeMinDistToUncovered(es->pc, sf.minDistToUncoveredOnReturn); uint64_t icnt = theStatisticManager->getIndexedValue(stats::instructions, diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index add4ff1b68..19bc67c6f9 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -15,7 +15,9 @@ #ifndef KLEE_EXECUTOR_H #define KLEE_EXECUTOR_H +#include "BidirectionalSearcher.h" #include "ExecutionState.h" +#include "SeedMap.h" #include "TargetedExecutionManager.h" #include "UserSearcher.h" @@ -26,12 +28,12 @@ #include "klee/Expr/ArrayCache.h" #include "klee/Expr/ArrayExprOptimizer.h" #include "klee/Expr/Constraints.h" +#include "klee/Expr/Lemma.h" #include "klee/Expr/SourceBuilder.h" #include "klee/Expr/SymbolicSource.h" #include "klee/Module/Cell.h" #include "klee/Module/KInstruction.h" #include "klee/Module/KModule.h" -#include "klee/Solver/ConcretizationManager.h" #include "klee/System/Time.h" #include "llvm/ADT/Twine.h" @@ -70,6 +72,7 @@ class AddressManager; class Array; struct Cell; class CodeGraphDistance; +class DistanceCalculator; class ExecutionState; class ExternalDispatcher; class Expr; @@ -90,12 +93,11 @@ class SpecialFunctionHandler; struct StackFrame; class SymbolicSource; class TargetCalculator; +class TargetManager; class StatsTracker; class TimingSolver; class TreeStreamWriter; class TypeManager; -class MergeHandler; -class MergingSearcher; template class ref; /// \todo Add a context object to keep track of data only live @@ -103,6 +105,7 @@ template class ref; /// removedStates, and haltExecution, among others. class Executor : public Interpreter { + friend struct ComposeHelper; friend class OwningSearcher; friend class WeightedRandomSearcher; friend class SpecialFunctionHandler; @@ -110,6 +113,12 @@ class Executor : public Interpreter { friend klee::Searcher * klee::constructUserSearcher(Executor &executor, bool stopAfterReachingTarget); + struct ComposeResult { + bool success; + PathConstraints composed; + Conflict conflict; + }; + public: typedef std::pair StatePair; @@ -125,7 +134,7 @@ class Executor : public Interpreter { std::unique_ptr kmodule; InterpreterHandler *interpreterHandler; - Searcher *searcher; + std::unique_ptr searcher; ExternalDispatcher *externalDispatcher; TimingSolver *solver; @@ -133,27 +142,17 @@ class Executor : public Interpreter { MemoryManager *memory; TypeManager *typeSystemManager; - SetOfStates states; - SetOfStates pausedStates; + std::unique_ptr objectManager; + Summary summary; StatsTracker *statsTracker; TreeStreamWriter *pathWriter, *symPathWriter; SpecialFunctionHandler *specialFunctionHandler; TimerGroup timers; - std::unique_ptr concretizationManager; std::unique_ptr processForest; std::unique_ptr codeGraphDistance; + std::unique_ptr distanceCalculator; std::unique_ptr targetCalculator; - - /// 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::vector 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::vector removedStates; + std::unique_ptr targetManager; /// When non-empty the Executor is running in "seed" mode. The /// states in this map will be executed in an arbitrary order @@ -162,7 +161,7 @@ class Executor : public Interpreter { /// 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> seedMap; + std::unique_ptr seedMap; /// Map of globals to their representative memory object. std::map globalObjects; @@ -176,7 +175,7 @@ class Executor : public Interpreter { std::unordered_map legalFunctions; /// Manager for everything related to targeted execution mode - TargetedExecutionManager targetedExecutionManager; + std::unique_ptr targetedExecutionManager; /// When non-null the bindings that will be used for calls to /// klee_make_symbolic in order replay. @@ -235,8 +234,17 @@ class Executor : public Interpreter { /// Typeids used during exception handling std::vector> eh_typeids; + // For statistics + std::set summarized; + + std::unordered_set verifingTransitionsTo; + std::unordered_set successTransitionsTo; + std::unordered_map failedTransitionsTo; + GuidanceKind guidanceKind; + bool hasStateWhichCanReachSomeTarget = false; + /// Return the typeid corresponding to a certain `type_info` ref getEhTypeidFor(ref type_info); @@ -244,12 +252,12 @@ class Executor : public Interpreter { void executeInstruction(ExecutionState &state, KInstruction *ki); - void targetedRun(ExecutionState &initialState, KBlock *target, - ExecutionState **resultState = nullptr); + // void targetedRun(ExecutionState &initialState, KBlock *target, + // ExecutionState **resultState = nullptr); void seed(ExecutionState &initialState); void run(std::vector initialStates); - void runWithTarget(ExecutionState &state, KFunction *kf, KBlock *target); + // void runWithTarget(ExecutionState &state, KFunction *kf, KBlock *target); void initializeTypeManager(); @@ -267,7 +275,6 @@ class Executor : public Interpreter { void initializeGlobalObjects(ExecutionState &state); void stepInstruction(ExecutionState &state); - void updateStates(ExecutionState *current); void transferToBasicBlock(llvm::BasicBlock *dst, llvm::BasicBlock *src, ExecutionState &state); void transferToBasicBlock(KBlock *dst, llvm::BasicBlock *src, @@ -360,7 +367,7 @@ class Executor : public Interpreter { bool checkResolvedMemoryObjects( ExecutionState &state, ref address, KInstruction *target, unsigned bytes, const std::vector &mayBeResolvedMemoryObjects, - std::vector &resolvedMemoryObjects, + bool hasLazyInitialized, std::vector &resolvedMemoryObjects, std::vector> &resolveConditions, std::vector> &unboundConditions, ref &checkOutOfBounds, bool &mayBeOutOfBound); @@ -380,6 +387,13 @@ class Executor : public Interpreter { std::vector &resolveConcretizations, bool &mayBeInBounds); + void + collectObjectStates(ExecutionState &state, ref address, + Expr::Width type, unsigned bytes, + const std::vector &resolvedMemoryObjects, + const std::vector &resolveConcretizations, + std::vector> &results); + void collectReads(ExecutionState &state, ref address, KType *targetType, Expr::Width type, unsigned bytes, const std::vector &resolvedMemoryObjects, @@ -395,8 +409,8 @@ class Executor : public Interpreter { bool lazyInitializeObject(ExecutionState &state, ref address, const KInstruction *target, KType *targetType, - uint64_t size, IDType &id, - bool isConstantSize = false, bool isLocal = false); + uint64_t size, bool isLocal, IDType &id, + bool isConstant = true); IDType lazyInitializeLocalObject(ExecutionState &state, StackFrame &sf, ref address, @@ -412,6 +426,10 @@ class Executor : public Interpreter { void updateStateWithSymcretes(ExecutionState &state, const Assignment &assignment); + /// Assume that `current` state stepped to `block`. + /// Can we reach at least one target of `current` from there? + bool canReachSomeTargetFromBlock(ExecutionState ¤t, KBlock *block); + /// 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 include @@ -422,13 +440,20 @@ class Executor : public Interpreter { /// 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 condition, bool isInternal, - BranchType reason); + /// if ifTrueBlock == ifFalseBlock, then fork is internal + StatePair fork(ExecutionState ¤t, ref condition, + KBlock *ifTrueBlock, KBlock *ifFalseBlock, BranchType reason); + StatePair forkInternal(ExecutionState ¤t, ref condition, + BranchType reason); // If the MaxStatic*Pct limits have been reached, concretize the condition and // return it. Otherwise, return the unmodified condition. ref maxStaticPctChecks(ExecutionState ¤t, ref condition); + Assignment computeConcretization(const ConstraintSet &constraints, + ref condition, + SolverQueryMetaData &queryMetaData); + /// Add the given (boolean) condition as a constraint on state. This /// function is a wrapper around the state's addConstraint function /// which also manages propagation of implied values, @@ -479,11 +504,11 @@ class Executor : public Interpreter { Cell &getArgumentCell(const ExecutionState &state, const KFunction *kf, unsigned index) { - return getArgumentCell(state.stack.back(), kf, index); + return getArgumentCell(state.stack.valueStack().back(), kf, index); } Cell &getDestCell(const ExecutionState &state, const KInstruction *target) { - return getDestCell(state.stack.back(), target); + return getDestCell(state.stack.valueStack().back(), target); } void bindLocal(const KInstruction *target, StackFrame &frame, @@ -538,6 +563,7 @@ class Executor : public Interpreter { /// Get textual information regarding a memory address. std::string getAddressInfo(ExecutionState &state, ref address, + unsigned size = 0, const MemoryObject *mo = nullptr) const; // Determines the \param lastInstruction of the \param state which is not KLEE @@ -591,6 +617,10 @@ class Executor : public Interpreter { void terminateStateOnUserError(ExecutionState &state, const llvm::Twine &message); + void reportProgressTowardsTargets(std::string prefix, + const SetOfStates &states) const; + void reportProgressTowardsTargets() const; + /// bindModuleConstants - Initialize the module constant table. void bindModuleConstants(const llvm::APFloat::roundingMode &rm); @@ -598,16 +628,18 @@ class Executor : public Interpreter { const Array *makeArray(ref size, const ref source); - ExecutionState *prepareStateForPOSIX(KInstIterator &caller, - ExecutionState *state); + // ExecutionState *prepareStateForPOSIX(KInstIterator &caller, + // ExecutionState *state); - void prepareTargetedExecution(ExecutionState *initialState, + void prepareTargetedExecution(ExecutionState &initialState, ref whitelist); void increaseProgressVelocity(ExecutionState &state, KBlock *block); - bool decreaseConfidenceFromStoppedStates( - SetOfStates &left_states, + void updateConfidenceRates(); + + void decreaseConfidenceFromStoppedStates( + const SetOfStates &leftStates, HaltExecution::Reason reason = HaltExecution::NotHalt); void checkNullCheckAfterDeref(ref cond, ExecutionState &state, @@ -643,6 +675,40 @@ class Executor : public Interpreter { void dumpStates(); void dumpPForest(); + ref fillValue(ExecutionState &state, ref valueSource, + ref size, Expr::Width width); + ref fillMakeSymbolic(ExecutionState &state, + ref makeSymbolicSource, + ref size, unsigned concreteSize); + ref fillConstant(ExecutionState &state, + ref constanSource, + ref size); + ref fillSymbolicSizeConstant( + ExecutionState &state, + ref symbolicSizeConstantSource, + ref size, unsigned concreteSize); + ref fillSymbolicSizeConstantAddress( + ExecutionState &state, + ref symbolicSizeConstantAddressSource, + ref size, Expr::Width width); + std::pair, ref> getSymbolicSizeConstantSizeAddressPair( + ExecutionState &state, + ref symbolicSizeConstantAddressSource, + ref size, Expr::Width width); + ref fillSizeAddressSymcretes(ExecutionState &state, + ref oldAddress, ref newAddress, + ref size); + ComposeResult compose(const ExecutionState &state, + const PathConstraints &pob); + + void executeAction(ref action); + + void goForward(ref action); + void goBackward(ref action); + void initializeIsolated(ref action); + + void closeProofObligation(ProofObligation *pob); + const KInstruction *getKInst(const llvm::Instruction *ints) const; const KBlock *getKBlock(const llvm::BasicBlock *bb) const; const KFunction *getKFunction(const llvm::Function *f) const; @@ -760,8 +826,6 @@ class Executor : public Interpreter { /// Returns the errno location in memory of the state int *getErrnoLocation(const ExecutionState &state) const; - - void executeStep(ExecutionState &state); }; } // namespace klee diff --git a/lib/Core/Initializer.cpp b/lib/Core/Initializer.cpp new file mode 100644 index 0000000000..eaf22daba4 --- /dev/null +++ b/lib/Core/Initializer.cpp @@ -0,0 +1,173 @@ +#include "Initializer.h" +#include "ProofObligation.h" +#include "klee/Module/KInstruction.h" +#include "klee/Module/KModule.h" +#include "klee/Support/DebugFlags.h" + +#include "llvm/IR/Instructions.h" + +#include +#include +#include +#include +#include + +namespace klee { + +std::pair>> +ConflictCoreInitializer::selectAction() { + auto KI = queued.front(); + queued.pop_front(); + auto targets = targetMap[KI]; + assert(!targets.empty()); + targetMap.erase(KI); + return {KI, targets}; +} + +bool ConflictCoreInitializer::empty() { return queued.empty(); } + +void ConflictCoreInitializer::update(const pobs_ty &added, + const pobs_ty &removed) { + for (auto i : added) { + addPob(i); + } + for (auto i : removed) { + removePob(i); + } +} + +void ConflictCoreInitializer::addPob(ProofObligation *pob) { + auto target = pob->location; + knownTargets[target]++; + if (knownTargets[target] > 1) { + return; // There has been such a target already + } + + std::list enqueue; + for (auto KI : awaiting) { + if (targetMap[KI].count(target)) { + enqueue.push_back(KI); + } + } + + for (auto KI : enqueue) { + awaiting.remove(KI); + queued.push_back(KI); + } +} + +void ConflictCoreInitializer::removePob(ProofObligation *pob) { + auto target = pob->location; + assert(knownTargets[target] != 0); + knownTargets[target]--; + + if (knownTargets[target] > 0) { + return; + } + + std::list dequeue; + for (auto KI : queued) { + bool noKnown = true; + for (auto target : knownTargets) { + if (target.second != 0 && targetMap[KI].count(target.first)) { + noKnown = false; + break; + } + } + if (noKnown) { + dequeue.push_back(KI); + } + } + + for (auto KI : dequeue) { + awaiting.push_back(KI); + queued.remove(KI); + } +} + +void ConflictCoreInitializer::addConflictInit(const Conflict &conflict, + KBlock *target) { + auto &blocks = conflict.path.getBlocks(); + std::vector functions; + + for (auto block : blocks) { + if (std::find(functions.begin(), functions.end(), block->parent) == + functions.end()) { + functions.push_back(block->parent); + } + } + + // Dismantle all functions present in the path + for (auto function : functions) { + auto dismantled = + cgd->dismantle(function->entryKBlock, function->returnKBlocks); + for (auto i : dismantled) { + KInstruction *from = + (isa(i.first) && + isa( + cast(i.first)->kcallInstruction->inst) + ? i.first->instructions[1] + : i.first->instructions[0]); + addInit(from, Target::create(i.second)); + } + } + + // Go through all the present functions and bridge calls if they lead to + // a present function (itself included) + for (auto function : functions) { + for (auto &block : function->blocks) { + if (auto call = dyn_cast(block.get())) { + auto called = call->getKFunction(); + if (std::find(functions.begin(), functions.end(), called) != + functions.end()) { + addInit(call->getFirstInstruction(), Target::create(called->entryKBlock)); + } + } + } + } + + // Dismantle to target + auto dismantled = cgd->dismantle(target->parent->entryKBlock, {target}); + for (auto i : dismantled) { + KInstruction *from = + (isa(i.first) && + isa( + cast(i.first)->kcallInstruction->inst) + ? i.first->instructions[1] + : i.first->instructions[0]); + addInit(from, Target::create(i.second)); + } +} + +void ConflictCoreInitializer::addInit(KInstruction *from, ref to) { + if (initialized[from].count(to)) { + return; + } + initialized[from].insert(to); + + if (debugPrints.isSet(DebugPrint::Init)) { + llvm::errs() << "[initializer] From " << from->toString() << " to " + << to->toString() << "\n"; + } + + targetMap[from].insert(to); + bool awaits = + (std::find(awaiting.begin(), awaiting.end(), from) != awaiting.end()); + bool enqueued = + (std::find(queued.begin(), queued.end(), from) != queued.end()); + + if (!awaits && !enqueued) { + if (knownTargets.count(to)) { + queued.push_back(from); + } else { + awaiting.push_back(from); + } + } else if (awaits) { + if (knownTargets.count(to)) { + awaiting.remove(from); + queued.push_back(from); + } + } +} + +}; // namespace klee diff --git a/lib/Core/Initializer.h b/lib/Core/Initializer.h new file mode 100644 index 0000000000..a8b67dc2cf --- /dev/null +++ b/lib/Core/Initializer.h @@ -0,0 +1,63 @@ +// -*- C++ -*- +#ifndef KLEE_INITIALIZER_H +#define KLEE_INITIALIZER_H + +#include "ProofObligation.h" +#include "klee/Module/CodeGraphDistance.h" +#include "klee/Module/KInstruction.h" +#include "klee/Module/KModule.h" +#include +#include +#include + +namespace klee { +struct Conflict; + +class Initializer { +public: + virtual ~Initializer() {} + virtual std::pair>> selectAction() = 0; + virtual bool empty() = 0; + virtual void update(const pobs_ty &added, const pobs_ty &removed) = 0; + virtual void addConflictInit(const Conflict &, KBlock *) = 0; +}; + +class ConflictCoreInitializer : public Initializer { +public: + std::pair>> selectAction() override; + bool empty() override; + + void addConflictInit(const Conflict &, KBlock *) override; + void update(const pobs_ty &added, const pobs_ty &removed) override; + + explicit ConflictCoreInitializer(CodeGraphDistance *cgd) : cgd(cgd){}; + ~ConflictCoreInitializer() override {} + +private: + CodeGraphDistance *cgd; + + // There are proof obligation in these targets + std::map, unsigned> knownTargets; + + // Targets collected for each initial instruction + std::map>> targetMap; + + // awaiting until the are proof obligations in one of their targets + std::list awaiting; + + // There are currently proof obligations in their targets so they are + // queued for dispatch + std::list queued; + + // For every (KI, Target) pair in this map, there is a state that starts + // at KI and has Target as one of its targets. + std::map>> initialized; + + void addInit(KInstruction *from, ref to); + void addPob(ProofObligation *pob); + void removePob(ProofObligation *pob); +}; + +}; // namespace klee + +#endif diff --git a/lib/Core/Memory.cpp b/lib/Core/Memory.cpp index eafecdb710..79a552a073 100644 --- a/lib/Core/Memory.cpp +++ b/lib/Core/Memory.cpp @@ -91,6 +91,15 @@ ObjectState::ObjectState(const MemoryObject *mo, KType *dt) memset(concreteStore, 0, size); } +ObjectState::ObjectState(unsigned size, const Array *array, KType *dt) + : copyOnWriteOwner(0), object(nullptr), concreteStore(new uint8_t[size]), + concreteMask(nullptr), knownSymbolics(nullptr), unflushedMask(nullptr), + updates(array, nullptr), lastUpdate(nullptr), dynamicType(dt), size(size), + readOnly(false) { + makeSymbolic(); + memset(concreteStore, 0, size); +} + ObjectState::ObjectState(const MemoryObject *mo, const Array *array, KType *dt) : copyOnWriteOwner(0), object(mo), concreteStore(new uint8_t[mo->size]), concreteMask(nullptr), knownSymbolics(nullptr), unflushedMask(nullptr), diff --git a/lib/Core/Memory.h b/lib/Core/Memory.h index bbc314e982..fac197b113 100644 --- a/lib/Core/Memory.h +++ b/lib/Core/Memory.h @@ -155,17 +155,18 @@ class MemoryObject { ref isZeroSizeExpr = EqExpr::create(Expr::createPointer(0), getSizeExpr()); ref isZeroOffsetExpr = EqExpr::create(Expr::createPointer(0), offset); - /* Check for zero size with zero offset. Useful for free of malloc(0) */ - ref andZeroExpr = AndExpr::create(isZeroSizeExpr, isZeroOffsetExpr); - return OrExpr::create(UltExpr::create(offset, getSizeExpr()), andZeroExpr); + return SelectExpr::create(isZeroSizeExpr, isZeroOffsetExpr, + UltExpr::create(offset, getSizeExpr())); } ref getBoundsCheckOffset(ref offset, unsigned bytes) const { ref offsetSizeCheck = UleExpr::create(Expr::createPointer(bytes), getSizeExpr()); - ref writeInSizeCheck = UleExpr::create( - offset, SubExpr::create(getSizeExpr(), Expr::createPointer(bytes))); - return AndExpr::create(offsetSizeCheck, writeInSizeCheck); + ref trueExpr = UltExpr::create( + offset, AddExpr::create( + SubExpr::create(getSizeExpr(), Expr::createPointer(bytes)), + Expr::createPointer(1))); + return SelectExpr::create(offsetSizeCheck, trueExpr, Expr::createFalse()); } /// Compare this object with memory object b. @@ -241,6 +242,7 @@ class ObjectState { /// Create a new object state for the given memory object with symbolic /// contents. ObjectState(const MemoryObject *mo, const Array *array, KType *dt); + ObjectState(unsigned size, const Array *array, KType *dt); ObjectState(const MemoryObject *mo, const ObjectState &os); ObjectState(const ObjectState &os); diff --git a/lib/Core/MemoryManager.cpp b/lib/Core/MemoryManager.cpp index 309dff8601..f0dc22287c 100644 --- a/lib/Core/MemoryManager.cpp +++ b/lib/Core/MemoryManager.cpp @@ -72,6 +72,11 @@ llvm::cl::opt MaxSymbolicAllocationSize( "Maximum available size for single allocation (default 10Mb)"), llvm::cl::init(10ll << 20), llvm::cl::cat(MemoryCat)); +llvm::cl::opt UseSymbolicSizeAllocation( + "use-sym-size-alloc", + llvm::cl::desc("Allows symbolic size allocation (default false)"), + llvm::cl::init(false), llvm::cl::cat(MemoryCat)); + /***/ MemoryManager::MemoryManager(ArrayCache *_arrayCache) : arrayCache(_arrayCache), deterministicSpace(0), nextFreeSlot(0), @@ -117,6 +122,8 @@ MemoryObject *MemoryManager::allocate(uint64_t size, bool isLocal, ref sizeExpr, unsigned timestamp, IDType id) { if (size > MaxConstantAllocationSize) { + klee_warning_once( + 0, "Large alloc: %" PRIu64 " bytes. KLEE models out of memory.", size); return 0; } diff --git a/lib/Core/ObjectManager.cpp b/lib/Core/ObjectManager.cpp new file mode 100644 index 0000000000..6ffe3efdd1 --- /dev/null +++ b/lib/Core/ObjectManager.cpp @@ -0,0 +1,311 @@ +#include "ObjectManager.h" + +#include "PForest.h" +#include "SearcherUtil.h" + +#include "klee/Module/KModule.h" +#include "klee/Support/Debug.h" +#include "klee/Support/DebugFlags.h" + +#include "llvm/Support/CommandLine.h" +#include + +using namespace llvm; +using namespace klee; + +ObjectManager::ObjectManager() : emptyState(nullptr) {} + +ObjectManager::~ObjectManager() {} + +void ObjectManager::addSubscriber(Subscriber *s) { subscribers.push_back(s); } + +void ObjectManager::addProcessForest(PForest *pf) { processForest = pf; } + +void ObjectManager::setEmptyState(ExecutionState *state) { + emptyState = state; +} + +void ObjectManager::addInitialState(ExecutionState *state) { + reachedStates[Target::create(state->pc->parent)].insert(state->copy()); + states.insert(state); + processForest->addRoot(state); +} + +void ObjectManager::clear() { + delete emptyState; + for (auto target : reachedStates) { + for (auto state : target.second) { + delete state; + } + } +} + +void ObjectManager::setCurrentState(ExecutionState *_current) { + assert(current == nullptr); + current = _current; + statesUpdated = true; + stateUpdateKind = + (current->isolated ? StateKind::Isolated : StateKind::Regular); +} + +ExecutionState *ObjectManager::branchState(ExecutionState *state, + BranchType reason) { + if (statesUpdated) { + auto kind = (state->isolated ? StateKind::Isolated : StateKind::Regular); + assert(kind == stateUpdateKind); + } else { + assert(0); // Is this possible? + } + ExecutionState *newState = state->branch(); + addedStates.push_back(newState); + processForest->attach(state->ptreeNode, newState, state, reason); + return newState; +} + +void ObjectManager::removeState(ExecutionState *state) { + std::vector::iterator itr = + std::find(removedStates.begin(), removedStates.end(), state); + assert(itr == removedStates.end()); + + if (!statesUpdated) { + statesUpdated = true; + stateUpdateKind = + (state->isolated ? StateKind::Isolated : StateKind::Regular); + } else { + auto kind = (state->isolated ? StateKind::Isolated : StateKind::Regular); + assert(kind == stateUpdateKind); + } + + state->pc = state->prevPC; + removedStates.push_back(state); +} + +ExecutionState *ObjectManager::initializeState(KInstruction *location, + std::set> targets) { + ExecutionState *state = nullptr; + state = emptyState->withKInstruction(location); + processForest->addRoot(state); + state->setTargeted(true); + for (auto target : targets) { + state->targetForest.add(target); + } + + state->setHistory(state->targetForest.getHistory()); + state->setTargets(state->targetForest.getTargets()); + state->stepTargetsAndHistory(); + + statesUpdated = true; + stateUpdateKind = StateKind::Isolated; + addedStates.push_back(state); + return state; +} + +void ObjectManager::updateSubscribers() { + if (statesUpdated) { + assert(stateUpdateKind != StateKind::None); + bool isolated = stateUpdateKind == StateKind::Isolated; + + if (isolated) { + checkReachedStates(); + } + + ref e = new States(current, addedStates, removedStates, isolated); + for (auto s : subscribers) { + s->update(e); + } + for (auto state : addedStates) { + isolated ? isolatedStates.insert(state) : states.insert(state); + } + for (auto state : removedStates) { + processForest->remove(state->ptreeNode); + isolated ? isolatedStates.erase(state) : states.erase(state); + delete state; + } + + current = nullptr; + addedStates.clear(); + removedStates.clear(); + statesUpdated = false; + stateUpdateKind = StateKind::None; + } + + { + ref e = new Propagations(addedPropagations, removedPropagations); + for (auto s : subscribers) { + s->update(e); + } + for (auto prop : addedPropagations) { + propagations[prop.pob->location].insert(prop); + } + for (auto prop : removedPropagations) { + propagations[prop.pob->location].erase(prop); + } + addedPropagations.clear(); + removedPropagations.clear(); + } + + { + ref e = new ProofObligations(addedPobs, removedPobs); + for (auto s : subscribers) { + s->update(e); + } + for (auto pob : addedPobs) { + pobs[pob->location].insert(pob); + } + for (auto pob : removedPobs) { + pobs[pob->location].erase(pob); + delete pob; + } + addedPobs.clear(); + removedPobs.clear(); + } + + { + ref e = new Conflicts(addedTargetedConflicts); + for (auto s : subscribers) { + s->update(e); + } + addedTargetedConflicts.clear(); + } +} + +void ObjectManager::initialUpdate() { + addedStates.insert(addedStates.begin(), states.begin(), states.end()); + statesUpdated = true; + stateUpdateKind = StateKind::Regular; + updateSubscribers(); +} + +const states_ty &ObjectManager::getStates() { return states; } + +const states_ty &ObjectManager::getIsolatedStates() { return isolatedStates; } + +void ObjectManager::checkReachedStates() { + assert(statesUpdated && stateUpdateKind == StateKind::Isolated); + std::set states(addedStates.begin(), addedStates.end()); + if (current) { + states.insert(current); + } + + std::vector toRemove; + for (auto state : states) { + if (!isOKIsolatedState(state)) { + if (state->isolated) { + // llvm::errs() << "DELETING: " << state->pathAndPCToString() << "\n"; + } + toRemove.push_back(state); + continue; + } + for (auto target : state->targets()) { + if (state->reachedTarget(target)) { + if (debugPrints.isSet(DebugPrint::Reached)) { + llvm::errs() << "[reached] Isolated state: " + << state->pathAndPCToString() << "\n"; + } + auto copy = state->copy(); + reachedStates[target].insert(copy); + for (auto pob : pobs[target]) { + if (checkStack(copy, pob)) { + addedPropagations.insert({copy, pob}); + } + } + toRemove.push_back(state); + if (state->isolated) { + // llvm::errs() << "DELETING: " << state->pathAndPCToString() << "\n"; + } + break; + } + } + } + + for (auto state : toRemove) { + if (std::find(removedStates.begin(), removedStates.end(), state) == + removedStates.end()) { + removeState(state); + } + } +} + + +bool ObjectManager::isOKIsolatedState(ExecutionState *state) { + assert(state->isolated); + + if (state->multilevelCount > state->level.size()) { + return false; + } + + if (state->stack.size() < 2) { + return true; + } + if (state->stack.size() == 2) { + auto initBlock = state->initPC->parent; + if (isa(initBlock) && + state->initPC == initBlock->getFirstInstruction()) { + return true; + } + } + return false; +} + +void ObjectManager::addTargetedConflict(ref conflict) { + addedTargetedConflicts.push_back(conflict); +} + +void ObjectManager::addPob(ProofObligation *pob) { + assert(!pobExists(pob)); + + if (!pob->parent && debugPrints.isSet(DebugPrint::RootPob)) { + llvm::errs() << "[pob] New root proof obligation at: " + << pob->location->toString() << "\n"; + } + + // if (pob->parent) { + // llvm::errs() << "[pob] NEW POB WITH:\n"; + // pob->constraints.cs().dump(); + // } + + addedPobs.insert(pob); + pathedPobs.insert({{pob->constraints.path(), pob->location}, pob}); + for (auto state : reachedStates[pob->location]) { + if (checkStack(state, pob)) { + addedPropagations.insert({state, pob}); + } + } +} + +void ObjectManager::removePob(ProofObligation *pob) { + auto subtree = pob->getSubtree(); + for (auto pob : subtree) { + removedPobs.insert(pob); + pathedPobs.erase({pob->constraints.path(), pob->location}); + for (auto prop : propagations[pob->location]) { + if (prop.pob == pob) { + removedPropagations.insert(prop); + } + } + } +} + +void ObjectManager::removePropagation(Propagation prop) { + removedPropagations.insert(prop); +} + +bool ObjectManager::checkStack(ExecutionState *state, ProofObligation *pob) { + if (state->stack.size() == 0) { + return true; + } + + size_t range = std::min(state->stack.callStack().size() - 1, pob->stack.size()); + auto stateIt = state->stack.callStack().rbegin(); + auto pobIt = pob->stack.rbegin(); + + for (size_t i = 0; i < range; ++i) { + if (stateIt->kf != pobIt->second || + (pobIt->first && &*(stateIt->caller) != pobIt->first)) { + return false; + } + stateIt++; + pobIt++; + } + return true; +} diff --git a/lib/Core/ObjectManager.h b/lib/Core/ObjectManager.h new file mode 100644 index 0000000000..74e74bdd57 --- /dev/null +++ b/lib/Core/ObjectManager.h @@ -0,0 +1,183 @@ +#ifndef KLEE_OBJECTMANAGER_H +#define KLEE_OBJECTMANAGER_H + +#include "ExecutionState.h" +#include "PForest.h" +#include "ProofObligation.h" +#include "SearcherUtil.h" +#include "klee/ADT/Ref.h" +#include "klee/Core/BranchTypes.h" +#include "klee/Module/KModule.h" + +#include +#include + +namespace klee { + +class Subscriber; + +class ObjectManager { +public: + struct Event { + friend class ref; + + protected: + class ReferenceCounter _refCount; + + public: + enum class Kind { States, Propagations, ProofObligations, Conflicts }; + + Event() = default; + virtual ~Event() = default; + + virtual Kind getKind() const = 0; + static bool classof(const Event *) { return true; } + }; + + struct States : public Event { + friend class ref; + ExecutionState *modified; + const std::vector &added; + const std::vector &removed; + bool isolated; + + States(ExecutionState *modified, const std::vector &added, + const std::vector &removed, bool isolated) + : modified(modified), added(added), removed(removed), + isolated(isolated) {} + + Kind getKind() const { return Kind::States; } + static bool classof(const Event *A) { return A->getKind() == Kind::States; } + static bool classof(const States *) { return true; } + }; + + struct Propagations : public Event { + friend class ref; + const propagations_ty &added; + const propagations_ty &removed; + + Propagations(const propagations_ty &added, const propagations_ty &removed) + : added(added), removed(removed) {} + + Kind getKind() const { return Kind::Propagations; } + static bool classof(const Event *A) { + return A->getKind() == Kind::Propagations; + } + static bool classof(const Propagations *) { return true; } + }; + + struct Conflicts : public Event { + friend class ref; + const std::vector> &conflicts; + + Conflicts(const std::vector> &conflicts) + : conflicts(conflicts) {} + + Kind getKind() const { return Kind::Conflicts; } + static bool classof(const Event *A) { + return A->getKind() == Kind::Conflicts; + } + static bool classof(const Conflicts *) { return true; } + }; + + struct ProofObligations : public Event { + friend class ref; + const pobs_ty &added; + const pobs_ty &removed; + + ProofObligations(const pobs_ty &added, const pobs_ty &removed) + : added(added), removed(removed) {} + + Kind getKind() const { return Kind::ProofObligations; } + static bool classof(const Event *A) { + return A->getKind() == Kind::ProofObligations; + } + static bool classof(const ProofObligations *) { return true; } + }; + + ObjectManager(); + ~ObjectManager(); + + void addSubscriber(Subscriber *); + void addProcessForest(PForest *); + + void setEmptyState(ExecutionState *state); + void addInitialState(ExecutionState *state); + void clear(); + + void setCurrentState(ExecutionState *_current); + ExecutionState *branchState(ExecutionState *state, BranchType reason); + void removeState(ExecutionState *state); + ExecutionState *initializeState(KInstruction *location, + std::set> targets); + + const states_ty &getStates(); + const states_ty &getIsolatedStates(); + + void addTargetedConflict(ref conflict); + + void addPob(ProofObligation *pob); + void removePob(ProofObligation *pob); + + void removePropagation(Propagation prop); + + void updateSubscribers(); + void initialUpdate(); + +private: + std::vector subscribers; + PForest *processForest; + +public: + ExecutionState *emptyState; + + std::set entrypoints; + + states_ty states; + states_ty isolatedStates; + std::map, states_ty> reachedStates; + std::map, pobs_ty> pobs; + std::map>, ProofObligation *> pathedPobs; + std::map, propagations_ty> propagations; + + // These are used to buffer execution results and pass the updates to + // subscribers + + bool statesUpdated = false; + enum class StateKind { + Regular, + Isolated, + None + } stateUpdateKind = StateKind::None; + + ExecutionState *current = nullptr; + std::vector addedStates; + std::vector removedStates; + + pobs_ty addedPobs; + pobs_ty removedPobs; + + propagations_ty addedPropagations; + propagations_ty removedPropagations; + + std::vector> addedTargetedConflicts; + + bool checkStack(ExecutionState *state, ProofObligation *pob); + void checkReachedStates(); + + // Check that the state has not entered a different function + // unless it is a 'bridge' state + bool isOKIsolatedState(ExecutionState *state); + bool pobExists(ProofObligation *pob) { + return pathedPobs.count({pob->constraints.path(), pob->location}); + } +}; + +class Subscriber { +public: + virtual void update(ref e) = 0; +}; + +} // namespace klee + +#endif /*KLEE_OBJECTMANAGER_H*/ diff --git a/lib/Core/ProofObligation.cpp b/lib/Core/ProofObligation.cpp new file mode 100644 index 0000000000..d4ea2c5b73 --- /dev/null +++ b/lib/Core/ProofObligation.cpp @@ -0,0 +1,7 @@ +#include "ProofObligation.h" + +namespace klee { + +unsigned ProofObligation::nextID = 0; + +} // namespace klee diff --git a/lib/Core/ProofObligation.h b/lib/Core/ProofObligation.h new file mode 100644 index 0000000000..05f8087759 --- /dev/null +++ b/lib/Core/ProofObligation.h @@ -0,0 +1,104 @@ +#ifndef KLEE_PROOFOBLIGATION_H +#define KLEE_PROOFOBLIGATION_H + +#include "ExecutionState.h" + +#include "klee/Expr/Constraints.h" +#include "klee/Expr/Path.h" +#include "klee/Module/KInstruction.h" +#include "klee/Module/KModule.h" +#include "klee/Module/Target.h" + +#include +#include +#include +#include +#include + +namespace klee { + +class MemoryObject; + +class ProofObligation { + +private: + static unsigned nextID; + +public: + const std::uint32_t id; + ProofObligation *parent; + ProofObligation *root; + std::set children; + std::vector stack; + std::map + propagationCount; + + ref location; + PathConstraints constraints; + + std::vector, const Array *>> + sourcedSymbolics; + + ProofObligation(KBlock *_location) + : id(nextID++), parent(nullptr), root(this), + location(Target::create(_location)) { + if (!location->atReturn()) { + constraints.advancePath(_location->getFirstInstruction()); + } + } + + ProofObligation(PathConstraints &pconstraint, ProofObligation &_parent, + KBlock *_location = nullptr) + : id(nextID++), parent(&_parent), root(parent->root), + stack(pconstraint.path().getStack(true)), + propagationCount(parent->propagationCount), + location(_location + ? Target::create(_location) + : Target::create(pconstraint.path().getBlocks().front())), + constraints(pconstraint) { + parent->children.insert(this); + } + + ~ProofObligation() { + for (auto pob : children) { + pob->parent = nullptr; + } + if (parent) { + parent->children.erase(this); + } + } + + std::set getSubtree() { + std::set subtree; + std::queue queue; + queue.push(this); + while (!queue.empty()) { + auto current = queue.front(); + queue.pop(); + subtree.insert(current); + for (auto pob : current->children) { + queue.push(pob); + } + } + return subtree; + } + + bool atReturn() const { return isa(location->getBlock()); } + std::uint32_t getID() const { return id; }; + std::string print() const; +}; + +struct ProofObligationIDCompare { + bool operator()(const ProofObligation *a, const ProofObligation *b) const { + return a->getID() < b->getID(); + } +}; + +using pobs_ty = std::set; + +ProofObligation *propagateToReturn(ProofObligation *pob, KInstruction *callSite, + KBlock *returnBlock); + +} // namespace klee + +#endif diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp index ab6ef2e1a9..db056a2013 100644 --- a/lib/Core/Searcher.cpp +++ b/lib/Core/Searcher.cpp @@ -19,7 +19,6 @@ #include "klee/ADT/DiscretePDF.h" #include "klee/ADT/RNG.h" #include "klee/ADT/WeightedQueue.h" -#include "klee/Module/CodeGraphDistance.h" #include "klee/Module/InstructionInfoTable.h" #include "klee/Module/KInstruction.h" #include "klee/Module/KModule.h" @@ -149,716 +148,234 @@ static unsigned int ulog2(unsigned int val) { /// -TargetedSearcher::TargetedSearcher(ref target, - CodeGraphDistance &_distance) - : states(std::make_unique< - WeightedQueue>()), - target(target), codeGraphDistance(_distance), - distanceToTargetFunction( - codeGraphDistance.getBackwardDistance(target->getBlock()->parent)) {} - -ExecutionState &TargetedSearcher::selectState() { return *states->choose(0); } - -bool TargetedSearcher::distanceInCallGraph(KFunction *kf, KBlock *kb, - unsigned int &distance) { - distance = UINT_MAX; - const std::unordered_map &dist = - codeGraphDistance.getDistance(kb); - KBlock *targetBB = target->getBlock(); - KFunction *targetF = targetBB->parent; - - if (kf == targetF && dist.count(targetBB) != 0) { - distance = 0; - return true; - } - - for (auto &kCallBlock : kf->kCallBlocks) { - if (dist.count(kCallBlock) != 0) { - for (auto &calledFunction : kCallBlock->calledFunctions) { - KFunction *calledKFunction = kf->parent->functionMap[calledFunction]; - if (distanceToTargetFunction.count(calledKFunction) != 0 && - distance > distanceToTargetFunction.at(calledKFunction) + 1) { - distance = distanceToTargetFunction.at(calledKFunction) + 1; - } - } - } - } - return distance != UINT_MAX; -} - -TargetedSearcher::WeightResult -TargetedSearcher::tryGetLocalWeight(ExecutionState *es, weight_type &weight, - const std::vector &localTargets) { - unsigned int intWeight = es->steppedMemoryInstructions; - KFunction *currentKF = es->pc->parent->parent; - KBlock *initKB = es->initPC->parent; - KBlock *currentKB = currentKF->blockMap[es->getPCBlock()]; - KBlock *prevKB = currentKF->blockMap[es->getPrevPCBlock()]; - const std::unordered_map &dist = - codeGraphDistance.getDistance(currentKB); - unsigned int localWeight = UINT_MAX; - for (auto &end : localTargets) { - if (dist.count(end) > 0) { - unsigned int w = dist.at(end); - localWeight = std::min(w, localWeight); - } - } - - if (localWeight == UINT_MAX) - return Miss; - if (localWeight == 0 && (initKB == currentKB || prevKB != currentKB || - target->shouldFailOnThisTarget())) { - return Done; - } - - intWeight += localWeight; - weight = ulog2(intWeight); // number on [0,32)-discrete-interval - return Continue; -} - -TargetedSearcher::WeightResult -TargetedSearcher::tryGetPreTargetWeight(ExecutionState *es, - weight_type &weight) { - KFunction *currentKF = es->pc->parent->parent; - std::vector localTargets; - for (auto &kCallBlock : currentKF->kCallBlocks) { - for (auto &calledFunction : kCallBlock->calledFunctions) { - KFunction *calledKFunction = - currentKF->parent->functionMap[calledFunction]; - if (distanceToTargetFunction.count(calledKFunction) > 0) { - localTargets.push_back(kCallBlock); - } - } - } - - if (localTargets.empty()) - return Miss; +TargetedSearcher::~TargetedSearcher() {} - WeightResult res = tryGetLocalWeight(es, weight, localTargets); - weight = weight + 32; // number on [32,64)-discrete-interval - return res == Done ? Continue : res; -} - -TargetedSearcher::WeightResult -TargetedSearcher::tryGetPostTargetWeight(ExecutionState *es, - weight_type &weight) { - KFunction *currentKF = es->pc->parent->parent; - std::vector &localTargets = currentKF->returnKBlocks; - - if (localTargets.empty()) - return Miss; - - WeightResult res = tryGetLocalWeight(es, weight, localTargets); - weight = weight + 32; // number on [32,64)-discrete-interval - return res == Done ? Continue : res; -} +bool TargetedSearcher::empty() { return states->empty(); } -TargetedSearcher::WeightResult -TargetedSearcher::tryGetTargetWeight(ExecutionState *es, weight_type &weight) { - std::vector localTargets = {target->getBlock()}; - WeightResult res = tryGetLocalWeight(es, weight, localTargets); - return res; +void TargetedSearcher::printName(llvm::raw_ostream &os) { + os << "TargetedSearcher"; } -TargetedSearcher::WeightResult -TargetedSearcher::tryGetWeight(ExecutionState *es, weight_type &weight) { - if (target->atReturn() && !target->shouldFailOnThisTarget()) { - if (es->prevPC->parent == target->getBlock() && - es->prevPC == target->getBlock()->getLastInstruction()) { - return Done; - } else if (es->pc->parent == target->getBlock()) { - weight = 0; - return Continue; - } - } - - if (target->shouldFailOnThisTarget() && target->isTheSameAsIn(es->prevPC) && - target->isThatError(es->error)) { - return Done; - } - - BasicBlock *bb = es->getPCBlock(); - KBlock *kb = es->pc->parent->parent->blockMap[bb]; - KInstruction *ki = es->pc; - if (!target->shouldFailOnThisTarget() && kb->numInstructions && - !isa(kb) && kb->getFirstInstruction() != ki && - states->tryGetWeight(es, weight)) { - return Continue; - } - unsigned int minCallWeight = UINT_MAX, minSfNum = UINT_MAX, sfNum = 0; - for (auto sfi = es->stack.rbegin(), sfe = es->stack.rend(); sfi != sfe; - sfi++) { - unsigned callWeight; - if (distanceInCallGraph(sfi->kf, kb, callWeight)) { - callWeight *= 2; - if (callWeight == 0 && target->shouldFailOnThisTarget()) { - weight = 0; - return target->isTheSameAsIn(kb->getFirstInstruction()) && - target->isThatError(es->error) - ? Done - : Continue; - } else { - callWeight += sfNum; - } - - if (callWeight < minCallWeight) { - minCallWeight = callWeight; - minSfNum = sfNum; - } - } - - if (sfi->caller) { - kb = sfi->caller->parent; - bb = kb->basicBlock; - } - sfNum++; - - if (minCallWeight < sfNum) - break; - } +TargetedSearcher::TargetedSearcher(ref target, + DistanceCalculator &_distanceCalculator) + : states(std::make_unique< + WeightedQueue>()), + target(target), distanceCalculator(_distanceCalculator) {} - WeightResult res = Miss; - if (minCallWeight == 0) - res = tryGetTargetWeight(es, weight); - else if (minSfNum == 0) - res = tryGetPreTargetWeight(es, weight); - else if (minSfNum != UINT_MAX) - res = tryGetPostTargetWeight(es, weight); - if (Done == res && target->shouldFailOnThisTarget()) { - if (!target->isThatError(es->error)) { - res = Continue; - } - } - return res; -} +ExecutionState &TargetedSearcher::selectState() { return *states->choose(0); } void TargetedSearcher::update( ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { - updateCheckCanReach(current, addedStates, removedStates); -} - -bool TargetedSearcher::updateCheckCanReach( - ExecutionState *current, const std::vector &addedStates, - const std::vector &removedStates) { - weight_type weight = 0; - bool canReach = false; // update current if (current && std::find(removedStates.begin(), removedStates.end(), - current) == removedStates.end()) { - switch (tryGetWeight(current, weight)) { - case Continue: - states->update(current, weight); - canReach = true; - break; - case Done: - reachedOnLastUpdate.insert(current); - canReach = true; - break; - case Miss: - current->targetForest.remove(target); - states->remove(current); - break; - } - } + current) == removedStates.end()) + states->update(current, getWeight(current)); // insert states - for (const auto state : addedStates) { - switch (tryGetWeight(state, weight)) { - case Continue: - states->insert(state, weight); - canReach = true; - break; - case Done: - states->insert(state, weight); - reachedOnLastUpdate.insert(state); - canReach = true; - break; - case Miss: - state->targetForest.remove(target); - break; - } - } + for (const auto state : addedStates) + states->insert(state, getWeight(state)); // remove states - for (const auto state : removedStates) { - if (target->atReturn() && !target->shouldFailOnThisTarget() && - state->prevPC == target->getBlock()->getLastInstruction()) { - canReach = true; - reachedOnLastUpdate.insert(state); - } else { - switch (tryGetWeight(state, weight)) { - case Done: - reachedOnLastUpdate.insert(state); - canReach = true; - break; - case Miss: - state->targetForest.remove(target); - states->remove(state); - break; - case Continue: - states->remove(state); - canReach = true; - break; - } - } - } - return canReach; -} - -bool TargetedSearcher::empty() { return states->empty(); } - -void TargetedSearcher::printName(llvm::raw_ostream &os) { - os << "TargetedSearcher"; + for (const auto state : removedStates) + states->remove(state); } -TargetedSearcher::~TargetedSearcher() { - while (!states->empty()) { - auto &state = selectState(); - state.targetForest.remove(target); - states->remove(&state); +weight_type TargetedSearcher::getWeight(ExecutionState *es) { + KBlock *kb = es->pc->parent; + KInstruction *ki = es->pc; + weight_type weight; + if (!target->shouldFailOnThisTarget() && kb->numInstructions && + !isa(kb) && kb->getFirstInstruction() != ki && + states->tryGetWeight(es, weight)) { + return weight; } -} - -std::set TargetedSearcher::reached() { - return reachedOnLastUpdate; -} - -void TargetedSearcher::removeReached() { - for (auto state : reachedOnLastUpdate) { - states->remove(state); - state->targetForest.stepTo(target); + auto distRes = distanceCalculator.getDistance(*es, target); + weight = ulog2(distRes.weight + es->steppedMemoryInstructions + 1); // [0, 32) + if (!distRes.isInsideFunction) { + weight += 32; // [32, 64) } - reachedOnLastUpdate.clear(); + return weight; } /// -GuidedSearcher::GuidedSearcher( - CodeGraphDistance &codeGraphDistance, TargetCalculator &stateHistory, - std::set &pausedStates, - std::size_t bound, RNG &rng, Searcher *baseSearcher) - : baseSearcher(baseSearcher), codeGraphDistance(codeGraphDistance), - stateHistory(stateHistory), pausedStates(pausedStates), bound(bound), - theRNG(rng) { - guidance = baseSearcher ? CoverageGuidance : ErrorGuidance; -} - ExecutionState &GuidedSearcher::selectState() { unsigned size = historiesAndTargets.size(); index = theRNG.getInt32() % (size + 1); ExecutionState *state = nullptr; - if (CoverageGuidance == guidance && index == size) { + if (index == size) { state = &baseSearcher->selectState(); } else { index = index % size; auto &historyTargetPair = historiesAndTargets[index]; - ref history = historyTargetPair.first; + ref history = historyTargetPair.first; ref target = historyTargetPair.second; - assert(targetedSearchers.find(history) != targetedSearchers.end() && - targetedSearchers.at(history).find(target) != - targetedSearchers.at(history).end() && - !targetedSearchers.at(history)[target]->empty()); - state = &targetedSearchers.at(history).at(target)->selectState(); + assert(targetedSearchers.find({history, target}) != + targetedSearchers.end() && + targetedSearchers.at({history, target}) && + !targetedSearchers.at({history, target})->empty()); + state = &targetedSearchers.at({history, target})->selectState(); } return *state; } -bool GuidedSearcher::isStuck(ExecutionState &state) { - KInstruction *prevKI = state.prevPC; - return (prevKI->inst->isTerminator() && - state.multilevel.count(state.getPCBlock()) > bound); -} - -bool GuidedSearcher::updateTargetedSearcher( - ref history, ref target, - ExecutionState *current, std::vector &addedStates, - std::vector &removedStates) { - bool canReach = false; - auto &historiedTargetedSearchers = targetedSearchers[history]; - - if (historiedTargetedSearchers.count(target) != 0 || - tryAddTarget(history, target)) { - - if (current) { - assert(current->targetForest.contains(target)); - for (auto &reached : reachedTargets) { - current->targetForest.blockIn(target, reached); - } - if (!current->targetForest.contains(target)) { - removedStates.push_back(current); - } - } - for (std::vector::iterator is = addedStates.begin(), - ie = addedStates.end(); - is != ie;) { - ExecutionState *state = *is; - assert(state->targetForest.contains(target)); - for (auto &reached : reachedTargets) { - state->targetForest.blockIn(target, reached); - } - if (!state->targetForest.contains(target)) { - is = addedStates.erase(is); - ie = addedStates.end(); - } else { - ++is; - } - } - - canReach = historiedTargetedSearchers[target]->updateCheckCanReach( - current, addedStates, removedStates); - if (historiedTargetedSearchers[target]->empty()) { - removeTarget(history, target); - } - } else if (isReached(history, target)) { - canReach = true; - if (current) { - current->targetForest.remove(target); - } - assert(removedStates.empty()); - for (auto &state : addedStates) { - state->targetForest.remove(target); - } - } - if (targetedSearchers[history].empty()) - targetedSearchers.erase(history); - return canReach; -} - -static void updateConfidences(ExecutionState *current, - const GuidedSearcher::TargetToStateUnorderedSetMap - &reachableStatesOfTarget) { - if (current) - current->targetForest.divideConfidenceBy(reachableStatesOfTarget); -} - -void GuidedSearcher::updateTargetedSearcherForStates( - std::vector &states, - std::vector &tmpAddedStates, - std::vector &tmpRemovedStates, - TargetToStateUnorderedSetMap &reachableStatesOfTarget, bool areStatesStuck, - bool areStatesRemoved) { - for (const auto state : states) { - auto history = state->targetForest.getHistory(); - auto targets = state->targetForest.getTargets(); - TargetForest::TargetsSet stateTargets; - for (auto &targetF : *targets) { - stateTargets.insert(targetF.first); - } - - for (auto &target : stateTargets) { - if (areStatesRemoved || state->targetForest.contains(target)) { - if (areStatesRemoved) - tmpRemovedStates.push_back(state); - else - tmpAddedStates.push_back(state); - assert(!state->targetForest.empty()); - - bool canReach = areStatesStuck; // overapproximation: assume that stuck - // state can reach any target - if (!areStatesStuck) - canReach = updateTargetedSearcher(history, target, nullptr, - tmpAddedStates, tmpRemovedStates); - if (canReach) - reachableStatesOfTarget[target].insert(state); - } - tmpAddedStates.clear(); - tmpRemovedStates.clear(); - } - } -} - -void GuidedSearcher::innerUpdate( +void GuidedSearcher::update( ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { - baseAddedStates.insert(baseAddedStates.end(), addedStates.begin(), - addedStates.end()); - baseRemovedStates.insert(baseRemovedStates.end(), removedStates.begin(), - removedStates.end()); - - std::vector addedStuckStates; - if (ErrorGuidance == guidance) { - if (current && - (std::find(baseRemovedStates.begin(), baseRemovedStates.end(), - current) == baseRemovedStates.end()) && - isStuck(*current)) { - pausedStates.insert(current); - baseRemovedStates.push_back(current); - } - for (const auto state : addedStates) { - if (isStuck(*state)) { - pausedStates.insert(state); - addedStuckStates.push_back(state); - auto is = - std::find(baseAddedStates.begin(), baseAddedStates.end(), state); - assert(is != baseAddedStates.end()); - baseAddedStates.erase(is); - } + + if (current) { + updateTargets(current); + ref history = current->history(); + const TargetHashSet &targets = current->targets(); + for (auto target : targets) { + localHistoryTargets.insert({history, target}); + currTargets.insert({history, target}); } } - for (const auto state : baseAddedStates) { - if (!state->targetForest.empty()) { - targetedAddedStates.push_back(state); - } else { - targetlessStates.push_back(state); + for (const auto state : addedStates) { + ref history = state->history(); + const TargetHashSet &targets = state->targets(); + for (auto target : targets) { + localHistoryTargets.insert({history, target}); + addedTStates[{history, target}].push_back(state); } } - TargetForest::TargetsSet currTargets; - if (current) { - auto targets = current->targetForest.getTargets(); - for (auto &targetF : *targets) { - auto target = targetF.first; - assert(target && "Target should be not null!"); - currTargets.insert(target); + for (const auto state : removedStates) { + ref history = state->history(); + const TargetHashSet &targets = state->targets(); + for (auto target : targets) { + localHistoryTargets.insert({history, target}); + removedTStates[{history, target}].push_back(state); } } - if (current && currTargets.empty() && - std::find(baseRemovedStates.begin(), baseRemovedStates.end(), current) == - baseRemovedStates.end()) { - targetlessStates.push_back(current); - } + for (auto historyTarget : localHistoryTargets) { + ref history = historyTarget.first; + ref target = historyTarget.second; - if (!baseRemovedStates.empty()) { - std::vector alt = baseRemovedStates; - for (const auto state : alt) { - auto it = pausedStates.find(state); - if (it != pausedStates.end()) { - pausedStates.erase(it); - baseRemovedStates.erase(std::remove(baseRemovedStates.begin(), - baseRemovedStates.end(), state), - baseRemovedStates.end()); - } - } - } + ExecutionState *currTState = + currTargets.count({history, target}) != 0 ? current : nullptr; - std::vector tmpAddedStates; - std::vector tmpRemovedStates; - - if (CoverageGuidance == guidance) { - for (const auto state : targetlessStates) { - if (isStuck(*state)) { - ref target(stateHistory.calculate(*state)); - if (target) { - state->targetForest.add(target); - auto history = state->targetForest.getHistory(); - tmpAddedStates.push_back(state); - updateTargetedSearcher(history, target, nullptr, tmpAddedStates, - tmpRemovedStates); - auto is = std::find(targetedAddedStates.begin(), - targetedAddedStates.end(), state); - if (is != targetedAddedStates.end()) { - targetedAddedStates.erase(is); - } - tmpAddedStates.clear(); - tmpRemovedStates.clear(); - } else { - pausedStates.insert(state); - if (std::find(baseAddedStates.begin(), baseAddedStates.end(), - state) != baseAddedStates.end()) { - auto is = std::find(baseAddedStates.begin(), baseAddedStates.end(), - state); - baseAddedStates.erase(is); - } else { - baseRemovedStates.push_back(state); - } - } - } + if (!isThereTarget(history, target)) { + addTarget(history, target); } - } - targetlessStates.clear(); - - TargetToStateUnorderedSetMap reachableStatesOfTarget; - - if (current && !currTargets.empty()) { - auto history = current->targetForest.getHistory(); - for (auto &target : currTargets) { - bool canReach = false; - if (current->targetForest.contains(target)) { - canReach = updateTargetedSearcher(history, target, current, - tmpAddedStates, tmpRemovedStates); - } else { - tmpRemovedStates.push_back(current); - canReach = updateTargetedSearcher(history, target, nullptr, - tmpAddedStates, tmpRemovedStates); - } - if (canReach) - reachableStatesOfTarget[target].insert(current); - tmpAddedStates.clear(); - tmpRemovedStates.clear(); - } - } - updateTargetedSearcherForStates(targetedAddedStates, tmpAddedStates, - tmpRemovedStates, reachableStatesOfTarget, - false, false); - targetedAddedStates.clear(); - updateTargetedSearcherForStates(addedStuckStates, tmpAddedStates, - tmpRemovedStates, reachableStatesOfTarget, - true, false); - updateTargetedSearcherForStates(baseRemovedStates, tmpAddedStates, - tmpRemovedStates, reachableStatesOfTarget, - false, true); - - if (CoverageGuidance == guidance) { - assert(baseSearcher); - baseSearcher->update(current, baseAddedStates, baseRemovedStates); + targetedSearchers.at({history, target}) + ->update(currTState, addedTStates[{history, target}], + removedTStates[{history, target}]); + addedTStates.at({history, target}).clear(); + removedTStates.at({history, target}).clear(); + if (targetedSearchers.at({history, target})->empty()) { + removeTarget(history, target); + } } + localHistoryTargets.clear(); + currTargets.clear(); - if (ErrorGuidance == guidance) { - updateConfidences(current, reachableStatesOfTarget); - for (auto state : baseAddedStates) - updateConfidences(state, reachableStatesOfTarget); - for (auto state : addedStuckStates) - updateConfidences(state, reachableStatesOfTarget); + if (baseSearcher) { + baseSearcher->update(current, addedStates, removedStates); } - baseAddedStates.clear(); - baseRemovedStates.clear(); } -void GuidedSearcher::update( - ExecutionState *current, const std::vector &addedStates, - const std::vector &removedStates) { - innerUpdate(current, addedStates, removedStates); - clearReached(removedStates); -} - -void GuidedSearcher::collectReached(TargetToStateSetMap &reachedStates) { - for (auto &historyTarget : historiesAndTargets) { - auto &history = historyTarget.first; - auto &target = historyTarget.second; - auto reached = targetedSearchers.at(history).at(target)->reached(); - if (!reached.empty()) { - for (auto state : reached) - reachedStates[target].insert(state); - } +void GuidedSearcher::updateTargets(ExecutionState *state) { + if (!state->areTargetsChanged()) { + return; } -} -void GuidedSearcher::clearReached( - const std::vector &removedStates) { - std::vector addedStates; - std::vector tmpAddedStates; - std::vector tmpRemovedStates; - TargetToStateSetMap reachedStates; - collectReached(reachedStates); - - for (auto &targetState : reachedStates) { - auto target = targetState.first; - auto states = targetState.second; - for (auto &state : states) { - auto history = state->targetForest.getHistory(); - auto targets = state->targetForest.getTargets(); - if (std::find(removedStates.begin(), removedStates.end(), state) == - removedStates.end()) { - TargetForest::TargetsSet stateTargets; - for (auto &targetF : *targets) { - stateTargets.insert(targetF.first); - } - tmpRemovedStates.push_back(state); - for (auto &anotherTarget : stateTargets) { - if (target != anotherTarget) { - updateTargetedSearcher(history, anotherTarget, nullptr, - tmpAddedStates, tmpRemovedStates); - } - } - tmpRemovedStates.clear(); - addedStates.push_back(state); - } + ref prevHistory = state->prevHistory(); + ref history = state->history(); + const TargetHashSet &prevTargets = state->prevTargets(); + const TargetHashSet &targets = state->targets(); + if (prevHistory != history) { + for (auto target : prevTargets) { + localHistoryTargets.insert({prevHistory, target}); + removedTStates[{prevHistory, target}].push_back(state); } - } - - if (!reachedStates.empty()) { - for (auto &targetState : reachedStates) { - auto target = targetState.first; - auto states = targetState.second; - for (auto &state : states) { - auto history = state->targetForest.getHistory(); - if (target->shouldFailOnThisTarget()) { - reachedTargets.insert(target); - } - targetedSearchers.at(history).at(target)->removeReached(); - if (CoverageGuidance == guidance || - (ErrorGuidance == guidance && target->shouldFailOnThisTarget()) || - targetedSearchers.at(history).at(target)->empty()) { - removeTarget(history, target); - } - if (targetedSearchers.at(history).empty()) - targetedSearchers.erase(history); + for (auto target : targets) { + localHistoryTargets.insert({history, target}); + addedTStates[{history, target}].push_back(state); + } + } else { + addedTargets = targets; + for (auto target : prevTargets) { + if (addedTargets.erase(target) == 0) { + removedTargets.insert(target); } } + for (auto target : removedTargets) { + localHistoryTargets.insert({history, target}); + removedTStates[{history, target}].push_back(state); + } + for (auto target : addedTargets) { + localHistoryTargets.insert({history, target}); + addedTStates[{history, target}].push_back(state); + } + removedTargets.clear(); + addedTargets.clear(); } - for (const auto state : addedStates) { - auto history = state->targetForest.getHistory(); - auto targets = state->targetForest.getTargets(); - TargetForest::TargetsSet stateTargets; - for (auto &targetF : *targets) { - stateTargets.insert(targetF.first); + for (auto historyTarget : localHistoryTargets) { + ref history = historyTarget.first; + ref target = historyTarget.second; + + if (!isThereTarget(history, target)) { + addTarget(history, target); } - for (auto &target : stateTargets) { - if (state->targetForest.contains(target)) { - tmpAddedStates.push_back(state); - updateTargetedSearcher(history, target, nullptr, tmpAddedStates, - tmpRemovedStates); - } - tmpAddedStates.clear(); - tmpRemovedStates.clear(); + + targetedSearchers.at({history, target}) + ->update(nullptr, addedTStates[{history, target}], + removedTStates[{history, target}]); + addedTStates.at({history, target}).clear(); + removedTStates.at({history, target}).clear(); + if (targetedSearchers.at({history, target})->empty()) { + removeTarget(history, target); } } + localHistoryTargets.clear(); } -bool GuidedSearcher::empty() { - return CoverageGuidance == guidance ? baseSearcher->empty() - : targetedSearchers.empty(); -} - -void GuidedSearcher::printName(llvm::raw_ostream &os) { - os << "GuidedSearcher\n"; +bool GuidedSearcher::isThereTarget(ref history, + ref target) { + return targetedSearchers.count({history, target}) != 0; } -bool GuidedSearcher::isReached(ref history, +void GuidedSearcher::addTarget(ref history, ref target) { - return reachedTargets.count(target) != 0; + assert(targetedSearchers.count({history, target}) == 0); + targetedSearchers[{history, target}] = + std::make_unique(target, distanceCalculator); + assert(std::find_if( + historiesAndTargets.begin(), historiesAndTargets.end(), + [&history, &target](const std::pair, + ref> &element) { + return element.first.get() == history.get() && + element.second.get() == target.get(); + }) == historiesAndTargets.end()); + historiesAndTargets.push_back({history, target}); } -bool GuidedSearcher::tryAddTarget(ref history, +void GuidedSearcher::removeTarget(ref history, ref target) { - if (isReached(history, target)) { - return false; - } - assert(targetedSearchers.count(history) == 0 || - targetedSearchers.at(history).count(target) == 0); - targetedSearchers[history][target] = - std::make_unique(target, codeGraphDistance); + targetedSearchers.erase({history, target}); auto it = std::find_if( historiesAndTargets.begin(), historiesAndTargets.end(), [&history, &target]( - const std::pair, ref> &element) { + const std::pair, ref> &element) { return element.first.get() == history.get() && element.second.get() == target.get(); }); - assert(it == historiesAndTargets.end()); - historiesAndTargets.push_back({history, target}); - return true; + assert(it != historiesAndTargets.end()); + historiesAndTargets.erase(it); } -GuidedSearcher::TargetForestHisoryTargetVector::iterator -GuidedSearcher::removeTarget(ref history, - ref target) { - targetedSearchers.at(history).erase(target); - auto it = std::find_if( - historiesAndTargets.begin(), historiesAndTargets.end(), - [&history, &target]( - const std::pair, ref> &element) { - return element.first.get() == history.get() && - element.second.get() == target.get(); - }); - assert(it != historiesAndTargets.end()); - return historiesAndTargets.erase(it); +bool GuidedSearcher::empty() { return baseSearcher->empty(); } + +void GuidedSearcher::printName(llvm::raw_ostream &os) { + os << "GuidedSearcher\n"; } /// @@ -903,7 +420,7 @@ double WeightedRandomSearcher::getWeight(ExecutionState *es) { return inv * inv; } case CPInstCount: { - StackFrame &sf = es->stack.back(); + const InfoStackFrame &sf = es->stack.infoStack().back(); uint64_t count = sf.callPathNode->statistics.getValue(stats::instructions); double inv = 1. / std::max((uint64_t)1, count); return inv; @@ -915,7 +432,7 @@ double WeightedRandomSearcher::getWeight(ExecutionState *es) { case CoveringNew: case MinDistToUncovered: { uint64_t md2u = computeMinDistToUncovered( - es->pc, es->stack.back().minDistToUncoveredOnReturn); + es->pc, es->stack.infoStack().back().minDistToUncoveredOnReturn); double invMD2U = 1. / (md2u ? md2u : 10000); if (type == CoveringNew) { diff --git a/lib/Core/Searcher.h b/lib/Core/Searcher.h index f307f46a77..09eeb86210 100644 --- a/lib/Core/Searcher.h +++ b/lib/Core/Searcher.h @@ -10,9 +10,12 @@ #ifndef KLEE_SEARCHER_H #define KLEE_SEARCHER_H +#include "DistanceCalculator.h" #include "ExecutionState.h" #include "PForest.h" #include "PTree.h" +#include "TargetManager.h" + #include "klee/ADT/RNG.h" #include "klee/Module/KModule.h" #include "klee/System/Time.h" @@ -34,11 +37,10 @@ class raw_ostream; } // namespace llvm namespace klee { -class CodeGraphDistance; +class DistanceCalculator; template class DiscretePDF; template class WeightedQueue; class ExecutionState; -class Executor; class TargetCalculator; class TargetForest; @@ -127,142 +129,80 @@ class RandomSearcher final : public Searcher { void printName(llvm::raw_ostream &os) override; }; -/// TargetedSearcher picks a state /*COMMENT*/. class TargetedSearcher final : public Searcher { -public: - enum WeightResult : std::uint8_t { - Continue, - Done, - Miss, - }; - private: - typedef unsigned weight_type; - std::unique_ptr> states; ref target; - CodeGraphDistance &codeGraphDistance; - const std::unordered_map &distanceToTargetFunction; - std::set reachedOnLastUpdate; - - bool distanceInCallGraph(KFunction *kf, KBlock *kb, unsigned int &distance); - WeightResult tryGetLocalWeight(ExecutionState *es, weight_type &weight, - const std::vector &localTargets); - WeightResult tryGetPreTargetWeight(ExecutionState *es, weight_type &weight); - WeightResult tryGetTargetWeight(ExecutionState *es, weight_type &weight); - WeightResult tryGetPostTargetWeight(ExecutionState *es, weight_type &weight); - WeightResult tryGetWeight(ExecutionState *es, weight_type &weight); + DistanceCalculator &distanceCalculator; + + weight_type getWeight(ExecutionState *es); public: - TargetedSearcher(ref target, CodeGraphDistance &distance); + TargetedSearcher(ref target, DistanceCalculator &distanceCalculator); ~TargetedSearcher() override; ExecutionState &selectState() override; void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) override; - bool updateCheckCanReach(ExecutionState *current, - const std::vector &addedStates, - const std::vector &removedStates); bool empty() override; void printName(llvm::raw_ostream &os) override; - std::set reached(); - void removeReached(); }; class GuidedSearcher final : public Searcher { -private: - template - class TargetHashMap - : public std::unordered_map, T, RefTargetHash, RefTargetCmp> { - }; - class TargetHashSet - : public std::unordered_set, RefTargetHash, RefTargetCmp> {}; - public: - using TargetToStateUnorderedSetMap = - TargetHashMap>; + template + using TargetHistoryTargetPairHashMap = + std::unordered_map; + + using TargetHistoryTargetPair = + std::pair, ref>; + using TargetHistoryTargetPairToSearcherMap = + std::unordered_map, + TargetHistoryTargetHash, TargetHistoryTargetCmp>; + using StatesVector = std::vector; + using TargetHistoryTargetPairToStatesMap = + std::unordered_map; + using TargetForestHisoryTargetVector = std::vector; + using TargetForestHistoryTargetSet = std::set; private: - using TargetToSearcherMap = TargetHashMap>; - using TargetToStateSetMap = - TargetHashMap>; - using TargetToStateVectorMap = TargetHashMap>; - - template - class TargetForestHistoryHashMap - : public std::unordered_map, T, - RefTargetsHistoryHash, RefTargetsHistoryCmp> { - }; - template - class TargetForestHistoryTargetsHashMap - : public TargetForestHistoryHashMap> {}; - - using TargetForestHistoryToSearcherMap = - TargetForestHistoryTargetsHashMap>; - using TargetForestHistoryToStateSetMap = - TargetForestHistoryTargetsHashMap>; - using TargetForestHisoryToStateVectorMap = - TargetForestHistoryTargetsHashMap>; - using TargetForestHisoryToTargetSet = TargetForestHistoryHashMap< - std::unordered_set, RefTargetHash, RefTargetCmp>>; - using TargetForestHisoryToTargetVector = - TargetForestHistoryHashMap>>; - using TargetForestHisoryTargetVector = - std::vector, ref>>; - - enum Guidance { CoverageGuidance, ErrorGuidance }; - - Guidance guidance; std::unique_ptr baseSearcher; - TargetForestHistoryToSearcherMap targetedSearchers; - CodeGraphDistance &codeGraphDistance; - TargetCalculator &stateHistory; - TargetHashSet reachedTargets; - std::set &pausedStates; - std::size_t bound; + TargetHistoryTargetPairToSearcherMap targetedSearchers; + DistanceCalculator &distanceCalculator; RNG &theRNG; unsigned index{1}; + TargetForestHistoryTargetSet localHistoryTargets; std::vector baseAddedStates; std::vector baseRemovedStates; - std::vector targetedAddedStates; - std::vector targetlessStates; - TargetForestHisoryTargetVector historiesAndTargets; + TargetHistoryTargetPairToStatesMap addedTStates; + TargetHistoryTargetPairToStatesMap removedTStates; - bool tryAddTarget(ref history, ref target); - TargetForestHisoryTargetVector::iterator - removeTarget(ref history, ref target); - bool isStuck(ExecutionState &state); - void innerUpdate(ExecutionState *current, - const std::vector &addedStates, - const std::vector &removedStates); - bool updateTargetedSearcher(ref history, - ref target, ExecutionState *current, - std::vector &addedStates, - std::vector &removedStates); - void updateTargetedSearcherForStates( - std::vector &states, - std::vector &tmpAddedStates, - std::vector &tmpRemovedStates, - TargetToStateUnorderedSetMap &reachableStatesOfTarget, - bool areStatesStuck, bool areStatesRemoved); - - void clearReached(const std::vector &removedStates); - void collectReached(TargetToStateSetMap &reachedStates); - bool isReached(ref history, ref target); + TargetHashSet removedTargets; + TargetHashSet addedTargets; + TargetForestHistoryTargetSet currTargets; + + TargetForestHisoryTargetVector historiesAndTargets; + bool isThereTarget(ref history, ref target); + void addTarget(ref history, ref target); + void removeTarget(ref history, ref target); public: - GuidedSearcher( - CodeGraphDistance &codeGraphDistance, TargetCalculator &stateHistory, - std::set &pausedStates, - std::size_t bound, RNG &rng, Searcher *baseSearcher = nullptr); + GuidedSearcher(Searcher *baseSearcher, DistanceCalculator &distanceCalculator, + RNG &rng) + : baseSearcher(baseSearcher), distanceCalculator(distanceCalculator), + theRNG(rng) {} ~GuidedSearcher() override = default; ExecutionState &selectState() override; void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) override; + void updateTargets(ExecutionState *current); bool empty() override; void printName(llvm::raw_ostream &os) override; diff --git a/lib/Core/SearcherUtil.h b/lib/Core/SearcherUtil.h new file mode 100644 index 0000000000..9ed1ae8a2f --- /dev/null +++ b/lib/Core/SearcherUtil.h @@ -0,0 +1,103 @@ +// -*- C++ -*- +#ifndef KLEE_SEARCHERUTIL_H +#define KLEE_SEARCHERUTIL_H + +#include "ExecutionState.h" +#include "ProofObligation.h" +#include "klee/Expr/Path.h" +#include "klee/Module/KInstruction.h" + +namespace klee { + +struct Propagation { + ExecutionState *state; + ProofObligation *pob; + + Propagation(ExecutionState *_state, ProofObligation *_pob) + : state(_state), pob(_pob) {} + + bool operator==(const Propagation &rhs) const { + return state == rhs.state && pob == rhs.pob; + } + + bool operator<(const Propagation &rhs) const { + return state->id < rhs.state->id || + (state->id == rhs.state->id && pob->id < rhs.pob->id); + } +}; + +struct PropagationIDCompare { + bool operator()(const Propagation &a, const Propagation &b) const { + return a.state->getID() < b.state->getID() || + (a.state->getID() == b.state->getID() && + a.pob->getID() < b.pob->getID()); + } +}; + +using propagations_ty = std::set; + +struct BidirectionalAction { + friend class ref; + +protected: + /// @brief Required by klee::ref-managed objects + class ReferenceCounter _refCount; + +public: + enum class Kind { Initialize, Forward, Backward }; + + BidirectionalAction() = default; + virtual ~BidirectionalAction() = default; + + virtual Kind getKind() const = 0; + + static bool classof(const BidirectionalAction *) { return true; } +}; + +struct ForwardAction : public BidirectionalAction { + friend class ref; + + ExecutionState *state; + + ForwardAction(ExecutionState *_state) : state(_state) {} + + Kind getKind() const { return Kind::Forward; } + static bool classof(const BidirectionalAction *A) { + return A->getKind() == Kind::Forward; + } + static bool classof(const ForwardAction *) { return true; } +}; + +struct BackwardAction : public BidirectionalAction { + friend class ref; + + Propagation prop; + + BackwardAction(Propagation prop) : prop(prop) {} + + Kind getKind() const { return Kind::Backward; } + static bool classof(const BidirectionalAction *A) { + return A->getKind() == Kind::Backward; + } + static bool classof(const BackwardAction *) { return true; } +}; + +struct InitializeAction : public BidirectionalAction { + friend class ref; + + KInstruction *location; + std::set> targets; + + InitializeAction(KInstruction *_location, std::set> _targets) + : location(_location), targets(_targets) {} + + Kind getKind() const { return Kind::Initialize; } + static bool classof(const BidirectionalAction *A) { + return A->getKind() == Kind::Initialize; + } + static bool classof(const InitializeAction *) { return true; } +}; + +} // namespace klee + +#endif diff --git a/lib/Core/SeedMap.cpp b/lib/Core/SeedMap.cpp new file mode 100644 index 0000000000..922f7a2790 --- /dev/null +++ b/lib/Core/SeedMap.cpp @@ -0,0 +1,62 @@ +#include "SeedMap.h" +#include "ObjectManager.h" + +using namespace klee; + +SeedMap::SeedMap() {} + +void SeedMap::update(ref e) { + if (auto statesEvent = dyn_cast(e)) { + if (!statesEvent->isolated) { + for (const auto state : statesEvent->removed) { + std::map>::iterator it = + seedMap.find(state); + if (it != seedMap.end()) { + seedMap.erase(it); + } + } + } + } +} + +std::map>::iterator +SeedMap::upper_bound(ExecutionState *state) { + return seedMap.upper_bound(state); +} + +std::map>::iterator +SeedMap::find(ExecutionState *state) { + return seedMap.find(state); +} + +std::map>::iterator SeedMap::begin() { + return seedMap.begin(); +} + +std::map>::iterator SeedMap::end() { + return seedMap.end(); +} + +void SeedMap::erase( + std::map>::iterator it) { + seedMap.erase(it); +} + +void SeedMap::erase(ExecutionState *state) { seedMap.erase(state); } + +void SeedMap::push_back(ExecutionState *state, + std::vector::iterator siit) { + seedMap[state].push_back(*siit); +} + +std::size_t SeedMap::count(ExecutionState *state) { + return seedMap.count(state); +} + +std::vector &SeedMap::at(ExecutionState *state) { + return seedMap[state]; +} + +bool SeedMap::empty() { return seedMap.empty(); } + +SeedMap::~SeedMap() {} diff --git a/lib/Core/SeedMap.h b/lib/Core/SeedMap.h new file mode 100644 index 0000000000..5c966675ba --- /dev/null +++ b/lib/Core/SeedMap.h @@ -0,0 +1,38 @@ +#ifndef KLEE_SEEDMAP_H +#define KLEE_SEEDMAP_H + +#include "ExecutionState.h" +#include "ObjectManager.h" +#include "SearcherUtil.h" +#include "SeedInfo.h" + +#include + +namespace klee { +class SeedMap : public Subscriber { +private: + std::map> seedMap; + +public: + SeedMap(); + + void update(ref e) override; + + std::map>::iterator + upper_bound(ExecutionState *state); + std::map>::iterator + find(ExecutionState *state); + std::map>::iterator end(); + std::map>::iterator begin(); + void erase(std::map>::iterator it); + void erase(ExecutionState *state); + void push_back(ExecutionState *state, std::vector::iterator siit); + std::size_t count(ExecutionState *state); + std::vector &at(ExecutionState *state); + bool empty(); + + virtual ~SeedMap(); +}; +} // namespace klee + +#endif /*KLEE_SEEDMAP_H*/ diff --git a/lib/Core/SpecialFunctionHandler.cpp b/lib/Core/SpecialFunctionHandler.cpp index 1a77e09687..f6c7c635f5 100644 --- a/lib/Core/SpecialFunctionHandler.cpp +++ b/lib/Core/SpecialFunctionHandler.cpp @@ -675,7 +675,8 @@ void SpecialFunctionHandler::handleWarning(ExecutionState &state, "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().data(), + klee_warning("%s: %s", + state.stack.callStack().back().kf->function->getName().data(), msg_str.c_str()); } @@ -686,9 +687,10 @@ void SpecialFunctionHandler::handleWarningOnce( "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().data(), - msg_str.c_str()); + klee_warning_once( + 0, "%s: %s", + state.stack.callStack().back().kf->function->getName().data(), + msg_str.c_str()); } void SpecialFunctionHandler::handlePrintRange( @@ -813,16 +815,15 @@ void SpecialFunctionHandler::handleRealloc(ExecutionState &state, ref address = arguments[0]; ref size = arguments[1]; - Executor::StatePair zeroSize = - executor.fork(state, Expr::createIsZero(size), true, BranchType::Realloc); + Executor::StatePair zeroSize = executor.forkInternal( + state, Expr::createIsZero(size), BranchType::Realloc); 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, - BranchType::Realloc); + Executor::StatePair zeroPointer = executor.forkInternal( + *zeroSize.second, Expr::createIsZero(address), BranchType::Realloc); if (zeroPointer.first) { // address == 0 executor.executeAlloc(*zeroPointer.first, size, false, target, diff --git a/lib/Core/StatsTracker.cpp b/lib/Core/StatsTracker.cpp index d69b85a028..5099207302 100644 --- a/lib/Core/StatsTracker.cpp +++ b/lib/Core/StatsTracker.cpp @@ -397,7 +397,7 @@ void StatsTracker::stepInstruction(ExecutionState &es) { Instruction *inst = es.pc->inst; const InstructionInfo &ii = *es.pc->info; - StackFrame &sf = es.stack.back(); + InfoStackFrame &sf = es.stack.infoStack().back(); theStatisticManager->setIndex(ii.id); if (UseCallPaths) theStatisticManager->setContext(&sf.callPathNode->statistics); @@ -416,8 +416,11 @@ void StatsTracker::stepInstruction(ExecutionState &es) { es.coveredLines[&ii.file].insert(ii.line); es.coveredNew = true; es.instsSinceCovNew = 1; - ++stats::coveredInstructions; - stats::uncoveredInstructions += (uint64_t)-1; + + if (!es.isolated) { + ++stats::coveredInstructions; + stats::uncoveredInstructions += (uint64_t)-1; + } } } } @@ -441,28 +444,31 @@ void StatsTracker::stepInstruction(ExecutionState &es) { /// /* Should be called _after_ the es->pushFrame() */ -void StatsTracker::framePushed(ExecutionState &es, StackFrame *parentFrame) { +void StatsTracker::framePushed(ExecutionState &es, + InfoStackFrame *parentFrame) { if (OutputIStats) { - StackFrame &sf = es.stack.back(); + const CallStackFrame &csf = es.stack.callStack().back(); + InfoStackFrame &isf = es.stack.infoStack().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; + parent, csf.caller ? csf.caller->inst : 0, csf.kf->function); + isf.callPathNode = cp; cp->count++; } } if (updateMinDistToUncovered) { - StackFrame &sf = es.stack.back(); + const CallStackFrame &csf = es.stack.callStack().back(); + InfoStackFrame &isf = es.stack.infoStack().back(); uint64_t minDistAtRA = 0; if (parentFrame) minDistAtRA = parentFrame->minDistToUncoveredOnReturn; - sf.minDistToUncoveredOnReturn = - sf.caller ? computeMinDistToUncovered(sf.caller, minDistAtRA) : 0; + isf.minDistToUncoveredOnReturn = + csf.caller ? computeMinDistToUncovered(csf.caller, minDistAtRA) : 0; } } @@ -603,7 +609,7 @@ void StatsTracker::writeStatsLine() { sqlite3_bind_int64(insertStmt, 3, partialBranches); sqlite3_bind_int64(insertStmt, 4, numBranches); sqlite3_bind_int64(insertStmt, 5, time::getUserTime().toMicroseconds()); - sqlite3_bind_int64(insertStmt, 6, executor.states.size()); + sqlite3_bind_int64(insertStmt, 6, executor.objectManager->getStates().size()); sqlite3_bind_int64(insertStmt, 7, util::GetTotalMallocUsage() + executor.memory->getUsedDeterministicSize()); @@ -645,15 +651,17 @@ void StatsTracker::writeStatsLine() { } void StatsTracker::updateStateStatistics(uint64_t addend) { - for (std::set::iterator it = executor.states.begin(), - ie = executor.states.end(); + std::set states = + executor.objectManager->getStates(); + for (std::set::iterator it = states.begin(), + ie = 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); + state.stack.infoStack().back().callPathNode->statistics.incrementValue( + stats::states, addend); } } @@ -817,11 +825,13 @@ void StatsTracker::writeIStats() { /// -typedef std::map> calltargets_ty; +typedef std::unordered_map> + calltargets_ty; static calltargets_ty callTargets; -static std::map> functionCallers; -static std::map functionShortestPath; +static std::unordered_map> + functionCallers; +static std::unordered_map functionShortestPath; static std::vector getSuccs(Instruction *i) { BasicBlock *bb = i->getParent(); @@ -1084,25 +1094,32 @@ void StatsTracker::computeReachableUncovered() { } } while (changed); - for (std::set::iterator it = executor.states.begin(), - ie = executor.states.end(); + std::set states = + executor.objectManager->getStates(); + for (std::set::iterator it = states.begin(), + ie = 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; + ExecutionStack::call_stack_ty::const_iterator + sfIt = es->stack.callStack().begin(), + sf_ie = es->stack.callStack().end(); + ExecutionStack::info_stack_ty::iterator isfIt = + es->stack.infoStack().begin(), + isf_ie = + es->stack.infoStack().end(); + for (; sfIt != sf_ie && isfIt != isf_ie; ++sfIt, ++isfIt) { + ExecutionStack::call_stack_ty::const_iterator next = sfIt + 1; KInstIterator kii; - if (next == es->stack.end()) { + if (next == sf_ie) { kii = es->pc; } else { kii = next->caller; ++kii; } - sfIt->minDistToUncoveredOnReturn = currentFrameMinDist; + isfIt->minDistToUncoveredOnReturn = currentFrameMinDist; currentFrameMinDist = computeMinDistToUncovered(kii, currentFrameMinDist); } diff --git a/lib/Core/StatsTracker.h b/lib/Core/StatsTracker.h index cd479dc529..be0f39ae5b 100644 --- a/lib/Core/StatsTracker.h +++ b/lib/Core/StatsTracker.h @@ -30,7 +30,7 @@ class Executor; class InstructionInfoTable; class InterpreterHandler; struct KInstruction; -struct StackFrame; +struct InfoStackFrame; class StatsTracker { friend class WriteStatsTimer; @@ -79,7 +79,7 @@ class StatsTracker { StatsTracker &operator=(StatsTracker &&other) noexcept = delete; // called after a new StackFrame has been pushed (for callpath tracing) - void framePushed(ExecutionState &es, StackFrame *parentFrame); + void framePushed(ExecutionState &es, InfoStackFrame *parentFrame); // called after a StackFrame has been popped void framePopped(ExecutionState &es); diff --git a/lib/Core/TargetCalculator.cpp b/lib/Core/TargetCalculator.cpp index 8975b32e21..c56417d5a2 100644 --- a/lib/Core/TargetCalculator.cpp +++ b/lib/Core/TargetCalculator.cpp @@ -98,8 +98,9 @@ ref TargetCalculator::calculate(ExecutionState &state) { unsigned int minDistance = UINT_MAX; unsigned int sfNum = 0; bool newCov = false; - for (auto sfi = state.stack.rbegin(), sfe = state.stack.rend(); sfi != sfe; - sfi++, sfNum++) { + for (auto sfi = state.stack.callStack().rbegin(), + sfe = state.stack.callStack().rend(); + sfi != sfe; sfi++, sfNum++) { kf = sfi->kf; for (const auto &kbd : codeGraphDistance.getSortedDistance(kb)) { diff --git a/lib/Core/TargetManager.cpp b/lib/Core/TargetManager.cpp new file mode 100644 index 0000000000..8d5e123573 --- /dev/null +++ b/lib/Core/TargetManager.cpp @@ -0,0 +1,151 @@ +//===-- TargetedManager.cpp -----------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TargetManager.h" + +#include "TargetCalculator.h" + +#include "klee/Module/KInstruction.h" + +#include + +using namespace llvm; +using namespace klee; + +namespace klee {} // namespace klee + +void TargetManager::updateMiss(ExecutionState &state, ref target) { + auto &stateTargetForest = targetForest(state); + stateTargetForest.remove(target); + setTargets(state, stateTargetForest.getTargets()); + if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { + state.setTargeted(false); + } +} + +void TargetManager::updateContinue(ExecutionState &state, ref target) {} + +void TargetManager::updateDone(ExecutionState &state, ref target) { + auto &stateTargetForest = targetForest(state); + + stateTargetForest.stepTo(target); + setTargets(state, stateTargetForest.getTargets()); + setHistory(state, stateTargetForest.getHistory()); + if (guidance == Interpreter::GuidanceKind::CoverageGuidance || + target->shouldFailOnThisTarget()) { + reachedTargets.insert(target); + for (auto es : states) { + if (isTargeted(*es)) { + auto &esTargetForest = targetForest(*es); + esTargetForest.block(target); + if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { + if (targets(*es).size() == 0) { + es->setTargeted(false); + } + } + setTargets(*es, esTargetForest.getTargets()); + } + } + } + if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { + state.setTargeted(false); + } +} + +void TargetManager::updateTargets(ExecutionState &state) { + if (!state.isolated && guidance == Interpreter::GuidanceKind::CoverageGuidance) { + if (targets(state).empty() && state.isStuck(MaxCyclesBeforeStuck)) { + state.setTargeted(true); + } + if (isTargeted(state) && targets(state).empty()) { + ref target(targetCalculator.calculate(state)); + if (target) { + state.targetForest.add(target); + setTargets(state, state.targetForest.getTargets()); + } + } + } + + if (!isTargeted(state)) { + return; + } + + auto stateTargets = targets(state); + auto &stateTargetForest = targetForest(state); + + for (auto target : stateTargets) { + if (!stateTargetForest.contains(target)) { + continue; + } + + DistanceResult stateDistance = distance(state, target); + switch (stateDistance.result) { + case WeightResult::Continue: + updateContinue(state, target); + break; + case WeightResult::Miss: + updateMiss(state, target); + break; + case WeightResult::Done: + updateDone(state, target); + break; + default: + assert(0 && "unreachable"); + } + } +} + +void TargetManager::update(ExecutionState *current, + const std::vector &addedStates, + const std::vector &removedStates) { + + states.insert(addedStates.begin(), addedStates.end()); + + for (const auto state : removedStates) { + states.erase(state); + } + + if (current && (std::find(removedStates.begin(), removedStates.end(), + current) == removedStates.end())) { + localStates.insert(current); + } + for (const auto state : addedStates) { + localStates.insert(state); + } + for (const auto state : removedStates) { + localStates.insert(state); + } + + for (auto state : localStates) { + state->stepTargetsAndHistory(); + auto prevKI = state->prevPC; + auto kf = prevKI->parent->parent; + auto kmodule = kf->parent; + + if (prevKI->inst->isTerminator() && kmodule->inMainModule(kf->function)) { + auto target = Target::create(state->prevPC->parent); + if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { + if (!target->atReturn() || + state->prevPC == target->getBlock()->getLastInstruction()) { + setReached(target); + } + } + } + + updateTargets(*state); + } + + localStates.clear(); +} + +void TargetManager::update(ref e) { + if (auto states = dyn_cast(e)) { + update(states->modified, states->added, states->removed); + } +} diff --git a/lib/Core/TargetManager.h b/lib/Core/TargetManager.h new file mode 100644 index 0000000000..ae2a0051ce --- /dev/null +++ b/lib/Core/TargetManager.h @@ -0,0 +1,100 @@ +//===-- TargetedManager.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 manage everything about targets +// +//===----------------------------------------------------------------------===// + +#include "DistanceCalculator.h" + +#include "ObjectManager.h" +#include "klee/Core/Interpreter.h" +#include "klee/Module/TargetHash.h" + +#include +#include + +#ifndef KLEE_TARGETMANAGER_H +#define KLEE_TARGETMANAGER_H + +namespace klee { +class TargetCalculator; + +class TargetManager final : public Subscriber { +private: + using StatesSet = std::unordered_set; + + Interpreter::GuidanceKind guidance; + DistanceCalculator &distanceCalculator; + TargetCalculator &targetCalculator; + TargetHashSet reachedTargets; + StatesSet states; + StatesSet localStates; + + void setTargets(ExecutionState &state, const TargetHashSet &targets) { + state.setTargets(targets); + } + + void setHistory(ExecutionState &state, ref history) { + state.setHistory(history); + } + + void updateMiss(ExecutionState &state, ref target); + + void updateContinue(ExecutionState &state, ref target); + + void updateDone(ExecutionState &state, ref target); + + void updateTargets(ExecutionState &state); + +public: + TargetManager(Interpreter::GuidanceKind _guidance, + DistanceCalculator &_distanceCalculator, + TargetCalculator &_targetCalculator) + : guidance(_guidance), distanceCalculator(_distanceCalculator), + targetCalculator(_targetCalculator){}; + + void update(ExecutionState *current, + const std::vector &addedStates, + const std::vector &removedStates); + + void update(ref e) override; + + DistanceResult distance(const ExecutionState &state, ref target) { + return distanceCalculator.getDistance(state, target); + } + + const TargetHashSet &targets(const ExecutionState &state) { + return state.targets(); + } + + ref history(const ExecutionState &state) { + return state.history(); + } + + ref prevHistory(const ExecutionState &state) { + return state.prevHistory(); + } + + const TargetHashSet &prevTargets(const ExecutionState &state) { + return state.prevTargets(); + } + + TargetForest &targetForest(ExecutionState &state) { + return state.targetForest; + } + + bool isTargeted(const ExecutionState &state) { return state.isTargeted(); } + + void setReached(ref target) { reachedTargets.insert(target); } +}; + +} // namespace klee + +#endif /* KLEE_TARGETMANAGER_H */ diff --git a/lib/Core/TargetedExecutionManager.cpp b/lib/Core/TargetedExecutionManager.cpp index 3cf9cd1810..5498aa1907 100644 --- a/lib/Core/TargetedExecutionManager.cpp +++ b/lib/Core/TargetedExecutionManager.cpp @@ -9,10 +9,14 @@ #include "TargetedExecutionManager.h" +#include "DistanceCalculator.h" +#include "TargetManager.h" + #include "ExecutionState.h" #include "klee/Core/TerminationTypes.h" #include "klee/Module/CodeGraphDistance.h" #include "klee/Module/KInstruction.h" +#include "klee/Module/KModule.h" #include "klee/Support/ErrorHandling.h" #include @@ -154,6 +158,12 @@ llvm::cl::opt SmartResolveEntryFunction( "smart-resolve-entry-function", cl::init(false), cl::desc("Resolve entry function using code flow graph instead of taking " "function of first location (default=false)")); + +cl::opt + MaxCycles("max-cycles", + cl::desc("stop execution after visiting some basic block this " + "amount of times (default=0)."), + cl::init(0), cl::cat(TerminationCat)); } // namespace klee void LocatedEventManager::prefetchFindFilename(const std::string &filename) { @@ -248,6 +258,9 @@ getAdviseWhatToIncreaseConfidenceRate(HaltExecution::Reason reason) { case HaltExecution::MaxSteppedInstructions: what = MaxSteppedInstructions.ArgStr.str(); break; + case HaltExecution::MaxCycles: + what = "max-cycles"; // TODO: taken from UserSearcher.cpp + break; case HaltExecution::CovCheck: what = "cov-check"; // TODO: taken from StatsTracker.cpp break; @@ -457,23 +470,24 @@ KFunction *TargetedExecutionManager::tryResolveEntryFunction( return resKf; } -std::unordered_map> +std::map, + TargetedExecutionManager::KFunctionLess> TargetedExecutionManager::prepareTargets(KModule *kmodule, SarifReport paths) { Locations locations = collectAllLocations(paths); LocationToBlocks locToBlocks = prepareAllLocations(kmodule, locations); - std::unordered_map> whitelists; + std::map, KFunctionLess> whitelists; for (auto &result : paths.results) { bool isFullyResolved = tryResolveLocations(result, locToBlocks); if (!isFullyResolved) { - broken_traces.insert(result.id); + brokenTraces.insert(result.id); continue; } auto kf = tryResolveEntryFunction(result, locToBlocks); if (!kf) { - broken_traces.insert(result.id); + brokenTraces.insert(result.id); continue; } @@ -496,10 +510,9 @@ void TargetedExecutionManager::reportFalseNegative(ExecutionState &state, bool TargetedExecutionManager::reportTruePositive(ExecutionState &state, ReachWithError error) { bool atLeastOneReported = false; - for (auto kvp : state.targetForest) { - auto target = kvp.first; - if (!target->isThatError(error) || broken_traces.count(target->getId()) || - reported_traces.count(target->getId())) + for (auto target : state.targetForest.getTargets()) { + if (!target->isThatError(error) || brokenTraces.count(target->getId()) || + reportedTraces.count(target->getId())) continue; /// The following code checks if target is a `call ...` instruction and we @@ -516,7 +529,7 @@ bool TargetedExecutionManager::reportTruePositive(ExecutionState &state, found = false; break; } - possibleInstruction = state.stack[i].caller; + possibleInstruction = state.stack.callStack().at(i).caller; i--; } if (!found) @@ -533,7 +546,56 @@ bool TargetedExecutionManager::reportTruePositive(ExecutionState &state, getErrorString(error), target->getId()); } target->isReported = true; - reported_traces.insert(target->getId()); + reportedTraces.insert(target->getId()); } return atLeastOneReported; } + +void TargetedExecutionManager::update( + ExecutionState *current, const std::vector &addedStates, + const std::vector &removedStates) { + if (current && (std::find(removedStates.begin(), removedStates.end(), + current) == removedStates.end())) { + localStates.insert(current); + } + for (const auto state : addedStates) { + localStates.insert(state); + } + for (const auto state : removedStates) { + localStates.insert(state); + } + + TargetToStateUnorderedSetMap reachableStatesOfTarget; + + for (auto state : localStates) { + auto &stateTargets = state->targets(); + + for (auto target : stateTargets) { + DistanceResult stateDistance = targetManager.distance(*state, target); + switch (stateDistance.result) { + case WeightResult::Miss: + break; + case WeightResult::Continue: + case WeightResult::Done: + reachableStatesOfTarget[target].insert(state); + break; + default: + assert(0 && "unreachable"); + } + } + } + + for (auto state : localStates) { + state->targetForest.divideConfidenceBy(reachableStatesOfTarget); + } + + localStates.clear(); +} + +void TargetedExecutionManager::update(ref e) { + if (auto states = dyn_cast(e)) { + if (!states->isolated) { + update(states->modified, states->added, states->removed); + } + } +} diff --git a/lib/Core/TargetedExecutionManager.h b/lib/Core/TargetedExecutionManager.h index 7488f5cc55..dc20e5ae99 100644 --- a/lib/Core/TargetedExecutionManager.h +++ b/lib/Core/TargetedExecutionManager.h @@ -14,6 +14,7 @@ #ifndef KLEE_TARGETEDEXECUTIONMANAGER_H #define KLEE_TARGETEDEXECUTIONMANAGER_H +#include "ObjectManager.h" #include "klee/Core/TargetedExecutionReporter.h" #include "klee/Module/KModule.h" #include "klee/Module/Target.h" @@ -22,6 +23,9 @@ #include namespace klee { +class DistanceCalculator; +class TargetManager; + extern llvm::cl::OptionCategory TerminationCat; /*** Termination criteria options ***/ @@ -56,6 +60,8 @@ extern llvm::cl::opt MaxStaticPctCheckDelay; extern llvm::cl::opt TimerInterval; +extern llvm::cl::opt MaxCycles; + class CodeGraphDistance; class LocatedEventManager { @@ -78,8 +84,8 @@ class TargetedHaltsOnTraces { using TraceToHaltTypeToConfidence = std::unordered_map, HaltTypeToConfidence, - TargetForest::RefUnorderedTargetsSetHash, - TargetForest::RefUnorderedTargetsSetCmp>; + TargetForest::UnorderedTargetsSetHash, + TargetForest::UnorderedTargetsSetCmp>; TraceToHaltTypeToConfidence traceToHaltTypeToConfidence; static void totalConfidenceAndTopContributor( @@ -96,21 +102,24 @@ class TargetedHaltsOnTraces { void reportFalsePositives(bool canReachSomeTarget); }; -class TargetedExecutionManager { +class TargetedExecutionManager final : public Subscriber { private: using Blocks = std::unordered_set; using LocationToBlocks = std::unordered_map, Blocks, RefLocationHash, RefLocationCmp>; using Locations = std::unordered_set, RefLocationHash, RefLocationCmp>; + using StatesSet = std::unordered_set; + using TargetToStateUnorderedSetMap = TargetHashMap; using Instructions = std::unordered_map< std::string, std::unordered_map< unsigned int, std::unordered_map>>>; - std::unordered_set broken_traces; - std::unordered_set reported_traces; + + std::unordered_set brokenTraces; + std::unordered_set reportedTraces; bool tryResolveLocations(Result &result, LocationToBlocks &locToBlocks) const; LocationToBlocks prepareAllLocations(KModule *kmodule, @@ -124,17 +133,38 @@ class TargetedExecutionManager { LocationToBlocks &locToBlocks) const; CodeGraphDistance &codeGraphDistance; + DistanceCalculator &distanceCalculator; + TargetManager &targetManager; + StatesSet localStates; public: - explicit TargetedExecutionManager(CodeGraphDistance &codeGraphDistance_) - : codeGraphDistance(codeGraphDistance_) {} - std::unordered_map> + struct KFunctionLess { + bool operator()(const KFunction *a, const KFunction *b) const { + return a->id < b->id; + } + }; + + explicit TargetedExecutionManager(CodeGraphDistance &codeGraphDistance_, + DistanceCalculator &distanceCalculator_, + TargetManager &targetManager_) + : codeGraphDistance(codeGraphDistance_), + distanceCalculator(distanceCalculator_), targetManager(targetManager_) { + } + ~TargetedExecutionManager() = default; + + std::map, KFunctionLess> prepareTargets(KModule *kmodule, SarifReport paths); void reportFalseNegative(ExecutionState &state, ReachWithError error); // Return true if report is successful bool reportTruePositive(ExecutionState &state, ReachWithError error); + + void update(ExecutionState *current, + const std::vector &addedStates, + const std::vector &removedStates); + + void update(ref e) override; }; } // namespace klee diff --git a/lib/Core/TargetedExecutionReporter.cpp b/lib/Core/TargetedExecutionReporter.cpp index 02b4011802..02da08ab43 100644 --- a/lib/Core/TargetedExecutionReporter.cpp +++ b/lib/Core/TargetedExecutionReporter.cpp @@ -15,10 +15,11 @@ using namespace klee; void klee::reportFalsePositive(confidence::ty confidence, - const std::set &errors, + const std::vector &errors, unsigned id, std::string whatToIncrease) { std::ostringstream out; - out << getErrorsString(errors) << " False Positive at trace " << id; + std::vector errorsSet(errors.begin(), errors.end()); + out << getErrorsString(errorsSet) << " False Positive at trace " << id; if (!confidence::isConfident(confidence)) { out << ". Advice: " << "increase --" << whatToIncrease << " command line parameter value"; diff --git a/lib/Core/UserSearcher.cpp b/lib/Core/UserSearcher.cpp index c2e9f0bb63..f5dd532d95 100644 --- a/lib/Core/UserSearcher.cpp +++ b/lib/Core/UserSearcher.cpp @@ -9,6 +9,7 @@ #include "UserSearcher.h" +#include "BackwardSearcher.h" #include "Executor.h" #include "Searcher.h" @@ -77,10 +78,11 @@ cl::opt BatchTime( cl::init("5s"), cl::cat(SearchCat)); cl::opt - MaxCycles("max-cycles", - cl::desc("stop execution after visiting some basic block this " - "amount of times (default=1)."), - cl::init(1), cl::cat(TerminationCat)); + MaxPropagations("max-propagations", + cl::desc("propagate at most this amount of propagations " + "with the same state (default=0 (no limit))."), + cl::init(0), cl::cat(TerminationCat)); + } // namespace void klee::initializeSearchOptions() { @@ -178,13 +180,8 @@ Searcher *klee::constructUserSearcher(Executor &executor, } if (executor.guidanceKind != Interpreter::GuidanceKind::NoGuidance) { - if (executor.guidanceKind == Interpreter::GuidanceKind::ErrorGuidance) { - delete searcher; - searcher = nullptr; - } - searcher = new GuidedSearcher( - *executor.codeGraphDistance.get(), *executor.targetCalculator, - executor.pausedStates, MaxCycles - 1, executor.theRNG, searcher); + searcher = new GuidedSearcher(searcher, *executor.distanceCalculator, + executor.theRNG); } llvm::raw_ostream &os = executor.getHandler().getInfoStream(); @@ -195,3 +192,7 @@ Searcher *klee::constructUserSearcher(Executor &executor, return searcher; } + +BackwardSearcher *klee::constructUserBackwardSearcher() { + return new RecencyRankedSearcher(MaxPropagations - 1); +} diff --git a/lib/Core/UserSearcher.h b/lib/Core/UserSearcher.h index 657a2d755c..c9d3f3030f 100644 --- a/lib/Core/UserSearcher.h +++ b/lib/Core/UserSearcher.h @@ -13,6 +13,7 @@ #include "llvm/Support/CommandLine.h" namespace klee { +class BackwardSearcher; class Executor; class Searcher; @@ -23,6 +24,8 @@ void initializeSearchOptions(); Searcher *constructUserSearcher(Executor &executor, bool stopAfterReachingTarget = true); + +BackwardSearcher *constructUserBackwardSearcher(); } // namespace klee #endif /* KLEE_USERSEARCHER_H */ diff --git a/lib/Expr/Assignment.cpp b/lib/Expr/Assignment.cpp index b176b8ed7b..d43db33870 100644 --- a/lib/Expr/Assignment.cpp +++ b/lib/Expr/Assignment.cpp @@ -22,7 +22,7 @@ void Assignment::dump() const { } for (bindings_ty::const_iterator i = bindings.begin(), e = bindings.end(); i != e; ++i) { - llvm::errs() << (*i).first->getIdentifier() << "\n["; + llvm::errs() << (*i).first->getName() << "\n["; for (int j = 0, k = (*i).second.size(); j < k; ++j) llvm::errs() << (int)(*i).second.load(j) << ","; llvm::errs() << "]\n"; @@ -34,7 +34,8 @@ ConstraintSet Assignment::createConstraintsFromAssignment() const { for (const auto &binding : bindings) { const auto &array = binding.first; const auto &values = binding.second; - ref arrayConstantSize = dyn_cast(array->size); + ref arrayConstantSize = + dyn_cast(evaluate(array->size)); assert(arrayConstantSize && "Size of symbolic array should be computed in assignment."); uint64_t arraySize = arrayConstantSize->getZExtValue(); diff --git a/lib/Expr/CMakeLists.txt b/lib/Expr/CMakeLists.txt index 648abee448..090884006e 100644 --- a/lib/Expr/CMakeLists.txt +++ b/lib/Expr/CMakeLists.txt @@ -23,6 +23,7 @@ klee_add_component(kleaverExpr ExprUtil.cpp ExprVisitor.cpp IndependentSet.cpp + Lemma.cpp Path.cpp SourceBuilder.cpp SymbolicSource.cpp diff --git a/lib/Expr/Constraints.cpp b/lib/Expr/Constraints.cpp index 4ea34c677a..0c5b35c58f 100644 --- a/lib/Expr/Constraints.cpp +++ b/lib/Expr/Constraints.cpp @@ -104,44 +104,6 @@ class ExprReplaceVisitor2 : public ExprVisitor { return Action::doChildren(); } - std::pair processUpdateList(const UpdateList &updates) { - UpdateList newUpdates = UpdateList(updates.root, 0); - std::stack> forward; - - for (auto it = updates.head; !it.isNull(); it = it->next) { - forward.push(it); - } - - bool changed = false; - while (!forward.empty()) { - ref UNode = forward.top(); - forward.pop(); - ref newIndex = visit(UNode->index); - ref newValue = visit(UNode->value); - if (newIndex != UNode->index || newValue != UNode->value) { - changed = true; - } - newUpdates.extend(newIndex, newValue); - } - return {newUpdates, changed}; - } - - Action visitRead(const ReadExpr &re) override { - auto updates = processUpdateList(re.updates); - - ref index = visit(re.index); - if (!updates.second && index == re.index) { - return Action::skipChildren(); - } else { - ref reres = ReadExpr::create(updates.first, index); - Action res = visitExprPost(*reres.get()); - if (res.kind == Action::ChangeTo) { - reres = res.argument; - } - return Action::changeTo(reres); - } - } - Action visitSelect(const SelectExpr &sexpr) override { auto cond = visit(sexpr.cond); if (auto CE = dyn_cast(cond)) { @@ -149,40 +111,9 @@ class ExprReplaceVisitor2 : public ExprVisitor { : Action::changeTo(visit(sexpr.falseExpr)); } - std::vector> splittedCond; - Expr::splitAnds(cond, splittedCond); - - ExprHashMap> localReplacements; - for (auto scond : splittedCond) { - if (const EqExpr *ee = dyn_cast(scond)) { - if (isa(ee->left)) { - localReplacements.insert(std::make_pair(ee->right, ee->left)); - } else { - localReplacements.insert( - std::make_pair(scond, ConstantExpr::alloc(1, Expr::Bool))); - } - } else { - localReplacements.insert( - std::make_pair(scond, ConstantExpr::alloc(1, Expr::Bool))); - } - } - - replacements.push_back(localReplacements); - visited.pushFrame(); auto trueExpr = visit(sexpr.trueExpr); - visited.popFrame(); - replacements.pop_back(); - // Reuse for false branch replacements - localReplacements.clear(); - localReplacements.insert( - std::make_pair(cond, ConstantExpr::alloc(0, Expr::Bool))); - - replacements.push_back(localReplacements); - visited.pushFrame(); auto falseExpr = visit(sexpr.falseExpr); - visited.popFrame(); - replacements.pop_back(); if (trueExpr != sexpr.trueExpr || falseExpr != sexpr.falseExpr) { ref seres = SelectExpr::create(cond, trueExpr, falseExpr); @@ -329,7 +260,7 @@ const ExprHashMap &PathConstraints::simplificationMap() const { const ConstraintSet &PathConstraints::cs() const { return constraints; } -const PathConstraints::ordered_constraints_ty & +const PathConstraints::path_ordered_constraints_ty & PathConstraints::orderedCS() const { return orderedConstraints; } @@ -358,7 +289,7 @@ ExprHashSet PathConstraints::addConstraint(ref e, const Assignment &delta, added.insert(expr); pathIndexes.insert({expr, currIndex}); _simplificationMap[expr].insert(expr); - orderedConstraints[currIndex].insert(expr); + orderedConstraints[currIndex].push_back(expr); constraints.addConstraint(expr, delta); } } @@ -404,7 +335,7 @@ PathConstraints PathConstraints::concat(const PathConstraints &l, auto index = r.pathIndexes.at(i); index.block += offset; path.pathIndexes.insert({i, index}); - path.orderedConstraints[index].insert(i); + path.orderedConstraints[index].push_back(i); } for (const auto &i : r.constraints.cs()) { path.constraints.addConstraint(i, {}); @@ -427,18 +358,28 @@ Simplificator::simplifyExpr(const constraints_ty &constraints, for (auto &constraint : constraints) { if (const EqExpr *ee = dyn_cast(constraint)) { + ref left = ee->left; + ref right = ee->right; + if (right < left) { + left = ee->right; + right = ee->left; + } if (isa(ee->left)) { equalities.insert(std::make_pair(ee->right, ee->left)); equalitiesParents.insert({ee->right, constraint}); } else { - equalities.insert( - std::make_pair(constraint, ConstantExpr::alloc(1, Expr::Bool))); + equalities.insert(std::make_pair(constraint, Expr::createTrue())); + equalities.insert(std::make_pair(right, left)); equalitiesParents.insert({constraint, constraint}); + equalitiesParents.insert({right, constraint}); } } else { - equalities.insert( - std::make_pair(constraint, ConstantExpr::alloc(1, Expr::Bool))); + equalities.insert(std::make_pair(constraint, Expr::createTrue())); equalitiesParents.insert({constraint, constraint}); + if (const NotExpr *ne = dyn_cast(constraint)) { + equalities.insert(std::make_pair(ne->expr, Expr::createFalse())); + equalitiesParents.insert({ne->expr, constraint}); + } } } @@ -522,12 +463,11 @@ Simplificator::gatherReplacements(constraints_ty constraints) { result.equalitiesParents.insert({ee->right, constraint}); } else { result.equalities.insert( - std::make_pair(constraint, ConstantExpr::alloc(1, Expr::Bool))); + std::make_pair(constraint, Expr::createTrue())); result.equalitiesParents.insert({constraint, constraint}); } } else { - result.equalities.insert( - std::make_pair(constraint, ConstantExpr::alloc(1, Expr::Bool))); + result.equalities.insert(std::make_pair(constraint, Expr::createTrue())); result.equalitiesParents.insert({constraint, constraint}); } } @@ -540,13 +480,11 @@ void Simplificator::addReplacement(Replacements &replacements, ref expr) { replacements.equalities.insert(std::make_pair(ee->right, ee->left)); replacements.equalitiesParents.insert({ee->right, expr}); } else { - replacements.equalities.insert( - std::make_pair(expr, ConstantExpr::alloc(1, Expr::Bool))); + replacements.equalities.insert(std::make_pair(expr, Expr::createTrue())); replacements.equalitiesParents.insert({expr, expr}); } } else { - replacements.equalities.insert( - std::make_pair(expr, ConstantExpr::alloc(1, Expr::Bool))); + replacements.equalities.insert(std::make_pair(expr, Expr::createTrue())); replacements.equalitiesParents.insert({expr, expr}); } } diff --git a/lib/Expr/Expr.cpp b/lib/Expr/Expr.cpp index 2969d72c9b..f9fcb9e8da 100644 --- a/lib/Expr/Expr.cpp +++ b/lib/Expr/Expr.cpp @@ -528,7 +528,7 @@ Expr::~Expr() { } } -ref Expr::createCachedExpr(const ref &e) { +ref Expr::createCachedExpr(ref e) { std::pair success = cachedExpressions.cache.insert(e.get()); @@ -1579,6 +1579,34 @@ ref SelectExpr::create(ref c, ref t, ref f) { return AndExpr::create(c, t); } } + } else if (isa(t) || isa(f)) { + if (SelectExpr *se = dyn_cast(t)) { // c1 ? (c2 ? t2 : f2) : f1 + if (se->trueExpr == + f) { // c1 ? (c2 ? f1 : f2) : f1 <=> c1 /\ not c2 ? f2 : f1 + return SelectExpr::create( + AndExpr::create(c, Expr::createIsZero(se->cond)), se->falseExpr, f); + } + if (se->falseExpr == + f) { // c1 ? (c2 ? t2 : f1) : f1 <=> c1 /\ c2 ? t2 : f1 + return SelectExpr::create(AndExpr::create(c, se->cond), se->trueExpr, + f); + } + } + if (SelectExpr *se = dyn_cast(f)) { // c1 ? t1 : (c2 ? t2 : f2) + if (se->trueExpr == + t) { // c1 ? t1 : (c2 ? t1 : f2) <=> not c1 /\ not c2 ? f2 : t1 + return SelectExpr::create(AndExpr::create(Expr::createIsZero(c), + Expr::createIsZero(se->cond)), + se->falseExpr, t); + } + if (se->falseExpr == + t) { // c1 ? t1 : (c2 ? t2 : t1) <=> not c1 /\ c2 ? t2 : t1 + return SelectExpr::create( + AndExpr::create(Expr::createIsZero(c), se->cond), se->trueExpr, t); + } + } + } else if (!isa(t) && isa(f)) { + return SelectExpr::alloc(Expr::createIsZero(c), f, t); } return SelectExpr::alloc(c, t, f); @@ -1596,6 +1624,21 @@ ref ConcatExpr::create(const ref &l, const ref &r) { if (ConstantExpr *rCE = dyn_cast(r)) return lCE->Concat(rCE); + if (isa(l) || isa(r)) { + if (SelectExpr *se = dyn_cast(l)) { + if (isa(se->trueExpr)) { + return SelectExpr::create(se->cond, ConcatExpr::create(se->trueExpr, r), + ConcatExpr::create(se->falseExpr, r)); + } + } + if (SelectExpr *se = dyn_cast(r)) { + if (isa(se->trueExpr)) { + return SelectExpr::create(se->cond, ConcatExpr::create(l, se->trueExpr), + ConcatExpr::create(l, se->falseExpr)); + } + } + } + // Merge contiguous Extracts if (ExtractExpr *ee_left = dyn_cast(l)) { if (ExtractExpr *ee_right = dyn_cast(r)) { @@ -1652,6 +1695,12 @@ ref ExtractExpr::create(ref expr, unsigned off, Width w) { return expr; } else if (ConstantExpr *CE = dyn_cast(expr)) { return CE->Extract(off, w); + } else if (SelectExpr *se = dyn_cast(expr)) { + if (isa(se->trueExpr)) { + return SelectExpr::create(se->cond, + ExtractExpr::create(se->trueExpr, off, w), + ExtractExpr::create(se->falseExpr, off, w)); + } } else { // Extract(Concat) if (ConcatExpr *ce = dyn_cast(expr)) { @@ -1696,6 +1745,11 @@ ref NotExpr::create(const ref &e) { NotExpr::create(OE->right)); } + if (SelectExpr *SE = dyn_cast(e)) { + return SelectExpr::create(SE->cond, NotExpr::create(SE->trueExpr), + NotExpr::create(SE->falseExpr)); + } + return NotExpr::alloc(e); } @@ -1709,9 +1763,14 @@ ref ZExtExpr::create(const ref &e, Width w) { return ExtractExpr::create(e, 0, w); } else if (ConstantExpr *CE = dyn_cast(e)) { return CE->ZExt(w); - } else { - return ZExtExpr::alloc(e, w); + } else if (SelectExpr *se = dyn_cast(e)) { + if (isa(se->trueExpr)) { + return SelectExpr::create(se->cond, ZExtExpr::create(se->trueExpr, w), + ZExtExpr::create(se->falseExpr, w)); + } } + + return ZExtExpr::alloc(e, w); } ref SExtExpr::create(const ref &e, Width w) { @@ -1722,9 +1781,14 @@ ref SExtExpr::create(const ref &e, Width w) { return ExtractExpr::create(e, 0, w); } else if (ConstantExpr *CE = dyn_cast(e)) { return CE->SExt(w); - } else { - return SExtExpr::alloc(e, w); + } else if (SelectExpr *se = dyn_cast(e)) { + if (isa(se->trueExpr)) { + return SelectExpr::create(se->cond, SExtExpr::create(se->trueExpr, w), + SExtExpr::create(se->falseExpr, w)); + } } + + return SExtExpr::alloc(e, w); } /***/ @@ -2028,6 +2092,18 @@ static ref AShrExpr_create(const ref &l, const ref &r) { #define BCREATE_R(_e_op, _op, partialL, partialR) \ ref _e_op ::create(const ref &l, const ref &r) { \ assert(l->getWidth() == r->getWidth() && "type mismatch"); \ + if (SelectExpr *sel = dyn_cast(l)) { \ + if (isa(sel->trueExpr)) { \ + return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \ + _e_op::create(sel->falseExpr, r)); \ + } \ + } \ + if (SelectExpr *ser = dyn_cast(r)) { \ + if (isa(ser->trueExpr)) { \ + return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \ + _e_op::create(l, ser->falseExpr)); \ + } \ + } \ if (ConstantExpr *cl = dyn_cast(l)) { \ if (ConstantExpr *cr = dyn_cast(r)) \ return cl->_op(cr); \ @@ -2041,6 +2117,18 @@ static ref AShrExpr_create(const ref &l, const ref &r) { #define BCREATE(_e_op, _op) \ ref _e_op ::create(const ref &l, const ref &r) { \ assert(l->getWidth() == r->getWidth() && "type mismatch"); \ + if (SelectExpr *sel = dyn_cast(l)) { \ + if (isa(sel->trueExpr)) { \ + return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \ + _e_op::create(sel->falseExpr, r)); \ + } \ + } \ + if (SelectExpr *ser = dyn_cast(r)) { \ + if (isa(ser->trueExpr)) { \ + return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \ + _e_op::create(l, ser->falseExpr)); \ + } \ + } \ if (ConstantExpr *cl = dyn_cast(l)) \ if (ConstantExpr *cr = dyn_cast(r)) \ return cl->_op(cr); \ @@ -2064,6 +2152,18 @@ BCREATE(AShrExpr, AShr) #define CMPCREATE(_e_op, _op) \ ref _e_op ::create(const ref &l, const ref &r) { \ assert(l->getWidth() == r->getWidth() && "type mismatch"); \ + if (SelectExpr *sel = dyn_cast(l)) { \ + if (isa(sel->trueExpr)) { \ + return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \ + _e_op::create(sel->falseExpr, r)); \ + } \ + } \ + if (SelectExpr *ser = dyn_cast(r)) { \ + if (isa(ser->trueExpr)) { \ + return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \ + _e_op::create(l, ser->falseExpr)); \ + } \ + } \ if (ConstantExpr *cl = dyn_cast(l)) \ if (ConstantExpr *cr = dyn_cast(r)) \ return cl->_op(cr); \ @@ -2073,15 +2173,26 @@ BCREATE(AShrExpr, AShr) #define CMPCREATE_T(_e_op, _op, _reflexive_e_op, partialL, partialR) \ ref _e_op ::create(const ref &l, const ref &r) { \ assert(l->getWidth() == r->getWidth() && "type mismatch"); \ + if (SelectExpr *sel = dyn_cast(l)) { \ + if (isa(sel->trueExpr)) { \ + return SelectExpr::create(sel->cond, _e_op::create(sel->trueExpr, r), \ + _e_op::create(sel->falseExpr, r)); \ + } \ + } \ + if (SelectExpr *ser = dyn_cast(r)) { \ + if (isa(ser->trueExpr)) { \ + return SelectExpr::create(ser->cond, _e_op::create(l, ser->trueExpr), \ + _e_op::create(l, ser->falseExpr)); \ + } \ + } \ if (ConstantExpr *cl = dyn_cast(l)) { \ if (ConstantExpr *cr = dyn_cast(r)) \ return cl->_op(cr); \ return partialR(cl, r.get()); \ } else if (ConstantExpr *cr = dyn_cast(r)) { \ return partialL(l.get(), cr); \ - } else { \ - return _e_op##_create(l.get(), r.get()); \ } \ + return _e_op##_create(l.get(), r.get()); \ } static ref EqExpr_create(const ref &l, const ref &r) { @@ -2118,20 +2229,22 @@ static ref EqExpr_create(const ref &l, const ref &r) { if (al->right == ar->right) { return EqExpr::create(al->left, ar->left); } - return EqExpr::alloc(l, r); } else if (isa(l) || isa(r)) { if (SelectExpr *se = dyn_cast(l)) { - return SelectExpr::create(se->cond, EqExpr::create(se->trueExpr, r), - EqExpr::create(se->falseExpr, r)); + if (isa(se->trueExpr)) { + return SelectExpr::create(se->cond, EqExpr::create(se->trueExpr, r), + EqExpr::create(se->falseExpr, r)); + } } if (SelectExpr *se = dyn_cast(r)) { - return SelectExpr::create(se->cond, EqExpr::create(se->trueExpr, l), - EqExpr::create(se->falseExpr, l)); + if (isa(se->trueExpr)) { + return SelectExpr::create(se->cond, EqExpr::create(l, se->trueExpr), + EqExpr::create(l, se->falseExpr)); + } } - return EqExpr::alloc(l, r); - } else { - return EqExpr::alloc(l, r); } + + return EqExpr::alloc(l, r); } /// Tries to optimize EqExpr cl == rd, where cl is a ConstantExpr and @@ -2235,6 +2348,9 @@ static ref EqExpr_createPartialR(const ref &cl, Expr *r) { return EqExpr_createPartialR( cast(SubExpr::create(se->left, cl)), se->right.get()); } + if (cl->isZero()) { + return EqExpr::create(se->left, se->right); + } } else if (rk == Expr::Read && ConstArrayOpt) { return TryConstArrayOpt(cl, static_cast(r)); } @@ -2268,6 +2384,10 @@ static ref UltExpr_create(const ref &l, const ref &r) { Expr::Width t = l->getWidth(); if (t == Expr::Bool) { // !l && r return AndExpr::create(Expr::createIsZero(l), r); + } else if (r->isOne()) { + return EqExpr::create(l, ConstantExpr::create(0, t)); + } else if (r->isZero()) { + return Expr::createFalse(); } else { return UltExpr::alloc(l, r); } @@ -2276,6 +2396,8 @@ static ref UltExpr_create(const ref &l, const ref &r) { static ref UleExpr_create(const ref &l, const ref &r) { if (l->getWidth() == Expr::Bool) { // !(l && !r) return OrExpr::create(Expr::createIsZero(l), r); + } else if (r->isZero()) { + return EqExpr::create(l, r); } else { return UleExpr::alloc(l, r); } diff --git a/lib/Expr/ExprEvaluator.cpp b/lib/Expr/ExprEvaluator.cpp index 9419cd2762..bcadb59156 100644 --- a/lib/Expr/ExprEvaluator.cpp +++ b/lib/Expr/ExprEvaluator.cpp @@ -76,6 +76,16 @@ ExprVisitor::Action ExprEvaluator::visitRead(const ReadExpr &re) { } } +ExprVisitor::Action ExprEvaluator::visitSelect(const SelectExpr &se) { + auto cond = visit(se.cond); + if (auto CE = dyn_cast(cond)) { + return CE->isTrue() ? Action::changeTo(visit(se.trueExpr)) + : Action::changeTo(visit(se.falseExpr)); + } + + return Action::doChildren(); +} + // we need to check for div by zero during partial evaluation, // if this occurs then simply ignore the 0 divisor and use the // original expression. diff --git a/lib/Expr/ExprPPrinter.cpp b/lib/Expr/ExprPPrinter.cpp index d5d3c4cf96..4684f3f403 100644 --- a/lib/Expr/ExprPPrinter.cpp +++ b/lib/Expr/ExprPPrinter.cpp @@ -530,6 +530,7 @@ void ExprPPrinter::printSignleArray(llvm::raw_ostream &os, const Array *a) { void ExprPPrinter::printSignleSource(llvm::raw_ostream &os, const ref s) { PPrinter p(os); + p.printArrayDecls = true; PrintContext PC(os); p.printSource(s, PC); } @@ -594,10 +595,11 @@ void ExprPPrinter::printQuery( } } + PC.breakLine(); PC << "(query ["; // Ident at constraint list; - unsigned indent = PC.pos; + unsigned indent = PC.pos - 1; for (auto it = constraints.cs().begin(), ie = constraints.cs().end(); it != ie;) { p.print(*it, PC); @@ -607,12 +609,12 @@ void ExprPPrinter::printQuery( } PC << ']'; - p.printSeparator(PC, constraints.cs().empty(), indent - 1); + p.printSeparator(PC, constraints.cs().empty(), indent); p.print(q, PC); // Print expressions to obtain values for, if any. if (evalExprsBegin != evalExprsEnd) { - p.printSeparator(PC, q->isFalse(), indent - 1); + p.printSeparator(PC, q->isFalse(), indent); PC << '['; for (const ref *it = evalExprsBegin; it != evalExprsEnd; ++it) { p.print(*it, PC, /*printConstWidth*/ true); @@ -627,7 +629,7 @@ void ExprPPrinter::printQuery( if (evalExprsBegin == evalExprsEnd) PC << " []"; - PC.breakLine(indent - 1); + PC.breakLine(indent); PC << '['; for (const Array *const *it = evalArraysBegin; it != evalArraysEnd; ++it) { PC << (*it)->getIdentifier(); @@ -640,3 +642,52 @@ void ExprPPrinter::printQuery( PC << ')'; PC.breakLine(); } + +void ExprPPrinter::printLemma(llvm::raw_ostream &os, const Lemma &l) { + PPrinter p(os); + + for (const auto &constraint : l.constraints) { + p.scan(constraint); + } + + PrintContext PC(os); + + PC << "(lemma"; + PC.breakLine(1); + + // print path + PC << l.path.toString(); + + PC.breakLine(); + PC.breakLine(1); + + PC << "("; + + std::vector sortedArray(p.usedArrays.begin(), + p.usedArrays.end()); + + std::sort(sortedArray.begin(), sortedArray.end(), ArrayPtrsByDependency()); + + for (auto it = sortedArray.begin(), ie = sortedArray.end(); it != ie; ++it) { + const Array *A = *it; + PC << A->getIdentifier() << " : "; + p.printArrayDecl(A, PC); + if (it + 1 != ie) { + PC.breakLine(2); + } + } + PC << ")"; + PC.breakLine(); + PC.breakLine(1); + + PC << "("; + for (auto it = l.constraints.begin(), ie = l.constraints.end(); it != ie; + ++it) { + if (it != l.constraints.begin()) { + PC.breakLine(2); + } + p.print(*it, PC); + } + PC << "))"; + PC.breakLine(); +} diff --git a/lib/Expr/ExprVisitor.cpp b/lib/Expr/ExprVisitor.cpp index 0cc373e668..6d2e509f33 100644 --- a/lib/Expr/ExprVisitor.cpp +++ b/lib/Expr/ExprVisitor.cpp @@ -27,12 +27,13 @@ ref ExprVisitor::visit(const ref &e) { if (!UseVisitorHash || isa(e)) { return visitActual(e); } else { - auto cached = visited.get(e); - if (cached.second) { - return cached.first; + visited_ty::iterator it = visited.find(e); + + if (it != visited.end()) { + return it->second; } else { - auto res = visitActual(cached.first); - visited.add({e, res}); + ref res = visitActual(e); + visited.insert(std::make_pair(e, res)); return res; } } diff --git a/lib/Expr/Lemma.cpp b/lib/Expr/Lemma.cpp new file mode 100644 index 0000000000..8ae9c99232 --- /dev/null +++ b/lib/Expr/Lemma.cpp @@ -0,0 +1,113 @@ +// -*- C++ -*- +#include "klee/Expr/Lemma.h" +#include "klee/Expr/ArrayCache.h" +#include "klee/Expr/ExprBuilder.h" +#include "klee/Expr/ExprPPrinter.h" +#include "klee/Expr/Parser/Parser.h" +#include "klee/Module/KModule.h" +#include "klee/Support/DebugFlags.h" +#include "klee/Support/ErrorHandling.h" +#include "klee/Support/OptionCategories.h" + +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include + +using namespace llvm; + +namespace klee { + +cl::opt SummaryFile("ksummary-file", + cl::desc("File to use to read/write lemmas"), + cl::init(""), cl::cat(ExecCat)); + +void Summary::addLemma(ref lemma) { + if (!lemmas.count(lemma)) { + lemmas.insert(lemma); + + if (debugPrints.isSet(DebugPrint::Lemma)) { + llvm::errs() << "[lemma] New Lemma ------------------------\n"; + llvm::errs() << lemma->path.toString() << "\n"; + llvm::errs() << "Constraints [\n"; + for (auto i : lemma->constraints) { + i->print(llvm::errs()); + } + llvm::errs() << "]\n"; + llvm::errs() << "[lemma] New Lemma End --------------------\n"; + } + + std::error_code ec; + raw_fd_ostream os(getFilename(), ec, sys::fs::CD_OpenAlways, + sys::fs::FA_Write, sys::fs::OF_Append); + if (ec) { + klee_error("Error while trying to write to .ksummary file."); + } + ExprPPrinter::printLemma(os, *lemma); + dumped.insert(lemma); + os << "\n"; + } +} + +void Summary::dumpToFile(KModule *km) { + std::error_code ec; + raw_fd_ostream os(getFilename(), ec, sys::fs::CD_OpenAlways, + sys::fs::FA_Write, sys::fs::OF_Append); + if (ec) { + klee_error("Error while trying to write to .ksummary file."); + } + for (auto &l : lemmas) { + if (!dumped.count(l)) { + ExprPPrinter::printLemma(os, *l); + dumped.insert(l); + if (lemmas.size() != dumped.size()) { + os << "\n"; + } + } + } +} + +void Summary::readFromFile(KModule *km, ArrayCache *cache) { + int fd; + auto error = sys::fs::openFile(getFilename(), fd, sys::fs::CD_OpenAlways, + sys::fs::FA_Read, sys::fs::OF_None); + if (error) { + klee_error("Could not open the .ksummary file."); + } + + // what is fileSize? + auto MBResult = MemoryBuffer::getOpenFile(fd, "", -1); + if (!MBResult) { + klee_error("Error during reading the .ksummary file."); + } + std::unique_ptr &MB = *MBResult; + std::unique_ptr Builder(createDefaultExprBuilder()); + expr::Parser *P = expr::Parser::Create("LemmaParser", MB.get(), Builder.get(), + cache, km, true); + while (auto parsed = P->ParseTopLevelDecl()) { + if (auto lemmaDecl = dyn_cast(parsed)) { + ref l(new Lemma(lemmaDecl->path, lemmaDecl->constraints)); + if (!lemmas.count(l)) { + lemmas.insert(l); + dumped.insert(l); + } + } + } + + delete P; +} + +std::string Summary::getFilename() { + if (SummaryFile == "") { + return ih->getOutputFilename("summary.ksummary"); + } else { + return SummaryFile; + } +} + +}; // namespace klee diff --git a/lib/Expr/Lexer.cpp b/lib/Expr/Lexer.cpp index 26eed1b172..6981d411a0 100644 --- a/lib/Expr/Lexer.cpp +++ b/lib/Expr/Lexer.cpp @@ -48,6 +48,8 @@ const char *Token::getKindName() const { return "KWFalse"; case KWQuery: return "KWQuery"; + case KWLemma: + return "KWLemma"; case KWPath: return "KWPath"; case KWReserved: @@ -185,6 +187,8 @@ Token &Lexer::SetIdentifierTokenKind(Token &Result) { return SetTokenKind(Result, Token::KWFalse); if (memcmp("query", Result.start, 5) == 0) return SetTokenKind(Result, Token::KWQuery); + if (memcmp("lemma", Result.start, 5) == 0) + return SetTokenKind(Result, Token::KWLemma); break; case 6: diff --git a/lib/Expr/Parser.cpp b/lib/Expr/Parser.cpp index 5f09603afd..fb9e7a5d7c 100644 --- a/lib/Expr/Parser.cpp +++ b/lib/Expr/Parser.cpp @@ -287,6 +287,7 @@ class ParserImpl : public Parser { /* Commands */ DeclResult ParseQueryCommand(); + DeclResult ParseLemmaCommand(); /* Etc. */ @@ -611,6 +612,9 @@ DeclResult ParserImpl::ParseCommandDecl() { case Token::KWQuery: return ParseQueryCommand(); + case Token::KWLemma: + return ParseLemmaCommand(); + default: Error("malformed command (unexpected keyword)."); SkipUntilRParen(); @@ -745,6 +749,75 @@ DeclResult ParserImpl::ParseQueryCommand() { return new QueryCommand(Constraints, km, Res.get(), Values, Objects); } +DeclResult ParserImpl::ParseLemmaCommand() { + ExprSymTab.clear(); + VersionSymTab.clear(); + ArraySymTab.clear(); + + ConsumeExpectedToken(Token::KWLemma); + + // -- Parsing path + ConsumeLParen(); + ConsumeExpectedToken(Token::KWPath); + ConsumeExpectedToken(Token::Colon); + auto firstInstructionExpr = ParseNumber(64); + + std::vector kblocks; + std::stack stack; + bool firstParsed = false; + while (!stack.empty() || !firstParsed) { + if (Tok.kind == Token::LParen) { + ConsumeLParen(); + assert(Tok.kind == Token::Identifier); + auto functionaName = Tok.getString(); + assert(km->functionNameMap.count(functionaName)); + stack.push(km->functionNameMap.at(functionaName)); + ConsumeExpectedToken(Token::Identifier); + ConsumeExpectedToken(Token::Colon); + firstParsed = true; + } else if (Tok.kind == Token::RParen) { + ConsumeRParen(); + stack.pop(); + } else { + assert(Tok.kind == Token::Identifier); + auto label = Tok.getString(); + assert(stack.top()->getLabelMap().count(label)); + kblocks.push_back(stack.top()->getLabelMap().at(label)); + ConsumeExpectedToken(Token::Identifier); + } + } + + auto lastInstructionExpr = ParseNumber(64); + ConsumeRParen(); + + auto firstInstruction = + dyn_cast(firstInstructionExpr.get())->getZExtValue(); + auto lastInstruction = + dyn_cast(lastInstructionExpr.get())->getZExtValue(); + + auto path = Path(firstInstruction, kblocks, lastInstruction); + // -- Path parsed + + ConsumeLParen(); + while (Tok.kind != Token::RParen) { + ParseArrayDecl(); + } + ConsumeRParen(); + + ExprOrderedSet constraints; + + ConsumeLParen(); + while (Tok.kind != Token::RParen) { + auto res = ParseExpr(TypeResult()); + assert(res.isValid()); + constraints.insert(res.get()); + } + ConsumeRParen(); + + ConsumeRParen(); + return new LemmaCommand(constraints, path); +} + /// ParseNumberOrExpr - Parse an expression whose type cannot be /// predicted. NumberOrExprResult ParserImpl::ParseNumberOrExpr() { diff --git a/lib/Module/CodeGraphDistance.cpp b/lib/Module/CodeGraphDistance.cpp index 9fb483355b..55aab2bd00 100644 --- a/lib/Module/CodeGraphDistance.cpp +++ b/lib/Module/CodeGraphDistance.cpp @@ -12,9 +12,11 @@ #include "llvm/IR/CFG.h" #include +#include #include using namespace klee; +using namespace llvm; void CodeGraphDistance::calculateDistance(KBlock *bb) { auto blockMap = bb->parent->blockMap; @@ -170,3 +172,75 @@ CodeGraphDistance::getSortedBackwardDistance(KFunction *kf) { calculateBackwardDistance(kf); return functionSortedBackwardDistance.at(kf); } + +KBlock *CodeGraphDistance::getNearestJoinBlock(KBlock *kb) { + for (auto &kbd : getSortedBackwardDistance(kb)) { +#if LLVM_VERSION_CODE >= LLVM_VERSION(9, 0) + if (kbd.first->basicBlock->hasNPredecessorsOrMore(2) || + kbd.first->basicBlock->hasNPredecessors(0)) + return kbd.first; +#else + if (kbd.first->basicBlock->hasNUsesOrMore(2) || + kbd.first->basicBlock->hasNUses(0)) + return kbd.first; +#endif + } + return nullptr; +} + +KBlock *CodeGraphDistance::getNearestJoinOrCallBlock(KBlock *kb) { + for (auto &kbd : getSortedBackwardDistance(kb)) { +#if LLVM_VERSION_CODE >= LLVM_VERSION(9, 0) + if (kbd.first->basicBlock->hasNPredecessorsOrMore(2) || + kbd.first->basicBlock->hasNPredecessors(0) || + (isa(kbd.first) && + dyn_cast(kbd.first)->internal() && + !dyn_cast(kbd.first)->intrinsic())) + return kbd.first; +#else + if (kbd.first->basicBlock->hasNUsesOrMore(2) || + kbd.first->basicBlock->hasNUses(0) || + (isa(kbd.first) && + dyn_cast(kbd.first)->internal() && + !dyn_cast(kbd.first)->intrinsic())) + return kbd.first; +#endif + } + return nullptr; +} + +std::vector> +CodeGraphDistance::dismantle(KBlock *from, std::vector to) { + for (auto block : to) { + assert(from->parent == block->parent && + "to and from KBlocks are from different functions."); + } + auto kf = from->parent; + + auto distance = getDistance(from); + std::vector> dismantled; + std::queue queue; + std::unordered_set used; + for (auto block : to) { + used.insert(block); + queue.push(block); + } + while (!queue.empty()) { + auto block = queue.front(); + queue.pop(); + for (auto const &pred : predecessors(block->basicBlock)) { + auto nearest = getNearestJoinOrCallBlock(kf->blockMap[pred]); + if (distance.count(nearest)) { + if (!used.count(nearest)) { + used.insert(nearest); + queue.push(nearest); + } + if (std::find(dismantled.begin(), dismantled.end(), + std::make_pair(nearest, block)) == dismantled.end()) { + dismantled.push_back(std::make_pair(nearest, block)); + } + } + } + } + return dismantled; +} diff --git a/lib/Module/SarifReport.cpp b/lib/Module/SarifReport.cpp index a185a896ef..88b36986e4 100644 --- a/lib/Module/SarifReport.cpp +++ b/lib/Module/SarifReport.cpp @@ -45,7 +45,7 @@ tryConvertLocationJson(const LocationJson &locationJson) { region->endColumn); } -std::set +std::vector tryConvertRuleJson(const std::string &ruleId, const std::string &toolName, const optional &errorMessage) { if (toolName == "SecB") { @@ -77,6 +77,8 @@ tryConvertRuleJson(const std::string &ruleId, const std::string &toolName, } else { return {ReachWithError::UseAfterFree, ReachWithError::DoubleFree}; } + } else if ("core.Reach" == ruleId) { + return {ReachWithError::Reachable}; } else { return {}; } @@ -96,6 +98,16 @@ tryConvertRuleJson(const std::string &ruleId, const std::string &toolName, } else { return {}; } + } else if (toolName == "Cooddy") { + if ("NULL.DEREF" == ruleId || "NULL.UNTRUSTED.DEREF" == ruleId) { + return {ReachWithError::NullPointerException}; + } else if ("MEM.DOUBLE.FREE" == ruleId) { + return {ReachWithError::DoubleFree}; + } else if ("MEM.USE.FREE" == ruleId) { + return {ReachWithError::UseAfterFree}; + } else { + return {}; + } } else { return {}; } @@ -104,7 +116,7 @@ tryConvertRuleJson(const std::string &ruleId, const std::string &toolName, optional tryConvertResultJson(const ResultJson &resultJson, const std::string &toolName, unsigned id) { - std::set errors = {ReachWithError::None}; + std::vector errors = {}; if (!resultJson.ruleId.has_value()) { errors = {ReachWithError::Reachable}; } else { @@ -157,7 +169,7 @@ const char *getErrorString(ReachWithError error) { return ReachWithErrorNames[error]; } -std::string getErrorsString(const std::set &errors) { +std::string getErrorsString(const std::vector &errors) { if (errors.size() == 1) { return getErrorString(*errors.begin()); } diff --git a/lib/Module/Target.cpp b/lib/Module/Target.cpp index 08b7feca25..a1e3596f1a 100644 --- a/lib/Module/Target.cpp +++ b/lib/Module/Target.cpp @@ -39,35 +39,33 @@ std::string Target::toString() const { return repr.str(); } -Target::EquivTargetHashSet Target::cachedTargets; -Target::TargetHashSet Target::targets; +Target::TargetCacheSet Target::cachedTargets; + +ref Target::createCachedTarget(ref target) { + std::pair success = + cachedTargets.cache.insert(target.get()); -ref Target::getFromCacheOrReturn(Target *target) { - std::pair success = - cachedTargets.insert(target); if (success.second) { // Cache miss - targets.insert(target); + target->isCached = true; return target; } // Cache hit - delete target; - target = *(success.first); - return target; + return (ref)*(success.first); } -ref Target::create(const std::set &_errors, +ref Target::create(const std::vector &_errors, unsigned _id, optional _loc, KBlock *_block) { Target *target = new Target(_errors, _id, _loc, _block); - return getFromCacheOrReturn(target); + return createCachedTarget(target); } ref Target::create(KBlock *_block) { return create({ReachWithError::None}, 0, nonstd::nullopt, _block); } -bool Target::isTheSameAsIn(KInstruction *instr) const { +bool Target::isTheSameAsIn(const KInstruction *instr) const { if (!loc.has_value()) { return false; } @@ -79,10 +77,16 @@ bool Target::isTheSameAsIn(KInstruction *instr) const { instr->info->column <= *errLoc.endColumn)); } +bool Target::mustVisitForkBranches(KInstruction *instr) const { + // null check after deref error is checked after fork + // but reachability of this target from instr children + // will always give false, so we need to force visiting + // fork branches here + return isTheSameAsIn(instr) && + isThatError(ReachWithError::NullCheckAfterDerefException); +} + int Target::compare(const Target &other) const { - if (errors != other.errors) { - return errors < other.errors ? -1 : 1; - } if (id != other.id) { return id < other.id ? -1 : 1; } @@ -92,10 +96,25 @@ int Target::compare(const Target &other) const { if (block->id != other.block->id) { return block->id < other.block->id ? -1 : 1; } + + if (errors.size() != other.errors.size()) { + return errors.size() < other.errors.size() ? -1 : 1; + } + + for (unsigned i = 0; i < errors.size(); ++i) { + if (errors.at(i) != other.errors.at(i)) { + return errors.at(i) < other.errors.at(i) ? -1 : 1; + } + } + return 0; } -bool Target::equals(const Target &other) const { return compare(other) == 0; } +bool Target::equals(const Target &b) const { + return (toBeCleared || b.toBeCleared) || (isCached && b.isCached) + ? this == &b + : compare(b) == 0; +} bool Target::operator<(const Target &other) const { return compare(other) == -1; @@ -104,8 +123,8 @@ bool Target::operator<(const Target &other) const { bool Target::operator==(const Target &other) const { return equals(other); } Target::~Target() { - if (targets.find(this) != targets.end()) { - cachedTargets.erase(this); - targets.erase(this); + if (isCached) { + toBeCleared = true; + cachedTargets.cache.erase(this); } } diff --git a/lib/Module/TargetForest.cpp b/lib/Module/TargetForest.cpp index d3c80efd30..f512bf7cc5 100644 --- a/lib/Module/TargetForest.cpp +++ b/lib/Module/TargetForest.cpp @@ -15,13 +15,13 @@ #include "klee/Module/KModule.h" #include "klee/Module/TargetHash.h" +#include "klee/Support/ErrorHandling.h" + using namespace klee; using namespace llvm; -TargetForest::UnorderedTargetsSet::EquivUnorderedTargetsSetHashSet +TargetForest::UnorderedTargetsSet::UnorderedTargetsSetCacheSet TargetForest::UnorderedTargetsSet::cachedVectors; -TargetForest::UnorderedTargetsSet::UnorderedTargetsSetHashSet - TargetForest::UnorderedTargetsSet::vectors; TargetForest::UnorderedTargetsSet::UnorderedTargetsSet( const ref &target) { @@ -30,21 +30,46 @@ TargetForest::UnorderedTargetsSet::UnorderedTargetsSet( } TargetForest::UnorderedTargetsSet::UnorderedTargetsSet( - const TargetsSet &targets) { + const TargetHashSet &targets) { for (const auto &p : targets) { targetsVec.push_back(p); } sortAndComputeHash(); } -void TargetForest::UnorderedTargetsSet::sortAndComputeHash() { - std::sort(targetsVec.begin(), targetsVec.end(), RefTargetLess{}); - RefTargetHash hasher; - std::size_t seed = targetsVec.size(); - for (const auto &r : targetsVec) { - seed ^= hasher(r) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +unsigned TargetForest::UnorderedTargetsSet::sortAndComputeHash() { + std::sort(targetsVec.begin(), targetsVec.end(), TargetLess{}); + unsigned res = targetsVec.size(); + for (auto r : targetsVec) { + res = (res * Expr::MAGIC_HASH_CONSTANT) + r->hash(); + } + hashValue = res; + return res; +} + +int TargetForest::UnorderedTargetsSet::compare( + const TargetForest::UnorderedTargetsSet &other) const { + if (targetsVec != other.targetsVec) { + return targetsVec < other.targetsVec ? -1 : 1; } - hashValue = seed; + return 0; +} + +bool TargetForest::UnorderedTargetsSet::equals( + const TargetForest::UnorderedTargetsSet &b) const { + return (toBeCleared || b.toBeCleared) || (isCached && b.isCached) + ? this == &b + : compare(b) == 0; +} + +bool TargetForest::UnorderedTargetsSet::operator<( + const TargetForest::UnorderedTargetsSet &other) const { + return compare(other) == -1; +} + +bool TargetForest::UnorderedTargetsSet::operator==( + const TargetForest::UnorderedTargetsSet &other) const { + return equals(other); } ref @@ -54,30 +79,28 @@ TargetForest::UnorderedTargetsSet::create(const ref &target) { } ref -TargetForest::UnorderedTargetsSet::create(const TargetsSet &targets) { +TargetForest::UnorderedTargetsSet::create(const TargetHashSet &targets) { UnorderedTargetsSet *vec = new UnorderedTargetsSet(targets); return create(vec); } ref -TargetForest::UnorderedTargetsSet::create(UnorderedTargetsSet *vec) { - std::pair success = - cachedVectors.insert(vec); +TargetForest::UnorderedTargetsSet::create(ref vec) { + std::pair success = + cachedVectors.cache.insert(vec.get()); if (success.second) { // Cache miss - vectors.insert(vec); + vec->isCached = true; return vec; } // Cache hit - delete vec; - vec = *(success.first); - return vec; + return (ref)*(success.first); } TargetForest::UnorderedTargetsSet::~UnorderedTargetsSet() { - if (vectors.find(this) != vectors.end()) { - vectors.erase(this); - cachedVectors.erase(this); + if (isCached) { + toBeCleared = true; + cachedVectors.cache.erase(this); } } @@ -90,7 +113,7 @@ void TargetForest::Layer::addTrace( const auto &loc = result.locations[i]; auto it = locToBlocks.find(loc); assert(it != locToBlocks.end()); - TargetsSet targets; + TargetHashSet targets; for (auto block : it->second) { ref target = nullptr; if (i == result.locations.size() - 1) { @@ -203,8 +226,7 @@ void TargetForest::Layer::removeTarget(ref target) { bool TargetForest::Layer::deepFind(ref target) const { if (empty()) return false; - auto res = targetsToVector.find(target); - if (res != targetsToVector.end()) { + if (targetsToVector.count(target) != 0) { return true; } for (auto &f : forest) { @@ -216,18 +238,17 @@ bool TargetForest::Layer::deepFind(ref target) const { bool TargetForest::Layer::deepFindIn(ref child, ref target) const { - auto res = targetsToVector.find(child); - if (res == targetsToVector.end()) { + if (targetsToVector.count(child) == 0) { return false; } + auto &res = targetsToVector.at(child); if (child == target) { return true; } - for (auto &targetsVec : res->second) { - auto it = forest.find(targetsVec); - assert(it != forest.end()); - if (it->second->deepFind(target)) { + for (auto &targetsVec : res) { + auto &it = forest.at(targetsVec); + if (it->deepFind(target)) { return true; } } @@ -343,9 +364,8 @@ TargetForest::Layer *TargetForest::Layer::replaceChildWith( TargetForest::Layer *TargetForest::Layer::replaceChildWith( ref child, - const std::unordered_set, - RefUnorderedTargetsSetHash, - RefUnorderedTargetsSetCmp> &other) const { + const std::unordered_set, UnorderedTargetsSetHash, + UnorderedTargetsSetCmp> &other) const { std::vector layers; for (auto &targetsVec : other) { auto it = forest.find(targetsVec); @@ -371,16 +391,6 @@ TargetForest::Layer *TargetForest::Layer::replaceChildWith( return result; } -bool TargetForest::Layer::allNodesRefCountOne() const { - bool all = true; - for (const auto &it : forest) { - all &= it.second->_refCount.getCount() == 1; - assert(all); - all &= it.second->allNodesRefCountOne(); - } - return all; -} - void TargetForest::Layer::dump(unsigned n) const { llvm::errs() << "THE " << n << " LAYER:\n"; llvm::errs() << "Confidence: " << confidence << "\n"; @@ -396,62 +406,64 @@ void TargetForest::Layer::dump(unsigned n) const { } } -TargetForest::History::EquivTargetsHistoryHashSet - TargetForest::History::cachedHistories; -TargetForest::History::TargetsHistoryHashSet TargetForest::History::histories; +TargetsHistory::TargetHistoryCacheSet TargetsHistory::cachedHistories; -ref -TargetForest::History::create(ref _target, - ref _visitedTargets) { - History *history = new History(_target, _visitedTargets); +ref +TargetsHistory::create(ref _target, + ref _visitedTargets) { + ref history = new TargetsHistory(_target, _visitedTargets); - std::pair success = - cachedHistories.insert(history); + std::pair success = + cachedHistories.cache.insert(history.get()); if (success.second) { // Cache miss - histories.insert(history); + history->isCached = true; return history; } // Cache hit - delete history; - history = *(success.first); - return history; + return (ref)*(success.first); } -ref TargetForest::History::create(ref _target) { +ref TargetsHistory::create(ref _target) { return create(_target, nullptr); } -ref TargetForest::History::create() { - return create(nullptr); -} +ref TargetsHistory::create() { return create(nullptr); } -int TargetForest::History::compare(const History &h) const { +int TargetsHistory::compare(const TargetsHistory &h) const { if (this == &h) return 0; + if (size() != h.size()) { + return (size() < h.size()) ? -1 : 1; + } + + if (size() == 0) { + return 0; + } + if (target && h.target) { - if (target != h.target) + if (target != h.target) { return (target < h.target) ? -1 : 1; - } else { - return h.target ? -1 : (target ? 1 : 0); + } } if (visitedTargets && h.visitedTargets) { - if (h.visitedTargets != h.visitedTargets) + if (h.visitedTargets != h.visitedTargets) { return (visitedTargets < h.visitedTargets) ? -1 : 1; - } else { - return h.visitedTargets ? -1 : (visitedTargets ? 1 : 0); + } } return 0; } -bool TargetForest::History::equals(const History &h) const { - return compare(h) == 0; +bool TargetsHistory::equals(const TargetsHistory &b) const { + return (toBeCleared || b.toBeCleared) || (isCached && b.isCached) + ? this == &b + : compare(b) == 0; } -void TargetForest::History::dump() const { +void TargetsHistory::dump() const { if (target) { llvm::errs() << target->toString() << "\n"; } else { @@ -463,10 +475,10 @@ void TargetForest::History::dump() const { visitedTargets->dump(); } -TargetForest::History::~History() { - if (histories.find(this) != histories.end()) { - histories.erase(this); - cachedHistories.erase(this); +TargetsHistory::~TargetsHistory() { + if (isCached) { + toBeCleared = true; + cachedHistories.cache.erase(this); } } @@ -485,7 +497,7 @@ void TargetForest::stepTo(ref loc) { loc->isReported = true; } if (forest->empty() && !loc->shouldFailOnThisTarget()) { - history = History::create(); + history = TargetsHistory::create(); } } @@ -510,17 +522,21 @@ void TargetForest::blockIn(ref subtarget, ref target) { forest = forest->blockLeafInChild(subtarget, target); } +void TargetForest::block(const ref &target) { + if (!forest->deepFind(target)) { + return; + } + + forest = forest->blockLeaf(target); +} + void TargetForest::dump() const { - llvm::errs() << "History:\n"; + llvm::errs() << "TargetHistory:\n"; history->dump(); llvm::errs() << "Forest:\n"; forest->dump(1); } -bool TargetForest::allNodesRefCountOne() const { - return forest->allNodesRefCountOne(); -} - void TargetForest::Layer::addLeafs( std::vector, confidence::ty>> &leafs, confidence::ty parentConfidence) const { @@ -597,28 +613,3 @@ TargetForest::Layer *TargetForest::Layer::divideConfidenceBy( } return result; } - -void TargetForest::Layer::collectHowManyEventsInTracesWereReached( - std::unordered_map> - &traceToEventCount, - unsigned reached, unsigned total) const { - total++; - for (const auto &p : forest) { - auto targetsVec = p.first; - auto child = p.second; - bool isReported = false; - for (auto &target : targetsVec->getTargets()) { - if (target->isReported) { - isReported = true; - } - } - auto reachedCurrent = isReported ? reached + 1 : reached; - auto target = *targetsVec->getTargets().begin(); - if (target->shouldFailOnThisTarget()) { - traceToEventCount[target->getId()] = - std::make_pair(reachedCurrent, total); - } - child->collectHowManyEventsInTracesWereReached(traceToEventCount, - reachedCurrent, total); - } -} diff --git a/lib/Module/TargetHash.cpp b/lib/Module/TargetHash.cpp index b187311b83..05f703ed10 100644 --- a/lib/Module/TargetHash.cpp +++ b/lib/Module/TargetHash.cpp @@ -14,31 +14,12 @@ using namespace llvm; using namespace klee; -unsigned TargetHash::operator()(const Target *t) const { - if (t) { - return t->hash(); - } else { - return 0; - } -} - -bool TargetCmp::operator()(const Target *a, const Target *b) const { - return a == b; -} - -bool EquivTargetCmp::operator()(const Target *a, const Target *b) const { - if (a == NULL || b == NULL) - return false; - return a->compare(*b) == 0; -} - -unsigned RefTargetHash::operator()(const ref &t) const { +unsigned TargetHash::operator()(const ref &t) const { return t->hash(); } -bool RefTargetCmp::operator()(const ref &a, - const ref &b) const { - return a.get() == b.get(); +bool TargetCmp::operator()(const ref &a, const ref &b) const { + return a == b; } std::size_t TransitionHash::operator()(const Transition &p) const { diff --git a/lib/Runner/run_klee.cpp b/lib/Runner/run_klee.cpp index 6637bc2512..cc68b4f8d8 100644 --- a/lib/Runner/run_klee.cpp +++ b/lib/Runner/run_klee.cpp @@ -359,6 +359,7 @@ class KleeHandler : public InterpreterHandler { unsigned m_numGeneratedTests; // Number of tests successfully generated unsigned m_pathsCompleted; // number of completed paths unsigned m_pathsExplored; // number of partially explored and completed paths + unsigned m_summarizedLocations; // used for writing .ktest files int m_argc; @@ -373,13 +374,15 @@ class KleeHandler : public InterpreterHandler { unsigned getNumTestCases() { return m_numGeneratedTests; } unsigned getNumPathsCompleted() { return m_pathsCompleted; } unsigned getNumPathsExplored() { return m_pathsExplored; } + unsigned getSummarizedLocaitons() { return m_summarizedLocations; } void incPathsCompleted() { ++m_pathsCompleted; } + void incSummarizedLocations() { ++m_summarizedLocations; } void incPathsExplored(std::uint32_t num = 1) { m_pathsExplored += num; } void setInterpreter(Interpreter *i); - void processTestCase(const ExecutionState &state, const char *errorMessage, - const char *errorSuffix); + void processTestCase(const ExecutionState &state, const char *message, + const char *suffix, bool isError = false); std::string getOutputFilename(const std::string &filename); std::unique_ptr @@ -404,7 +407,8 @@ class KleeHandler : public InterpreterHandler { KleeHandler::KleeHandler(int argc, char **argv) : m_interpreter(0), m_pathWriter(0), m_symPathWriter(0), m_outputDirectory(), m_numTotalTests(0), m_numGeneratedTests(0), - m_pathsCompleted(0), m_pathsExplored(0), m_argc(argc), m_argv(argv) { + m_pathsCompleted(0), m_pathsExplored(0), m_summarizedLocations(0), + m_argc(argc), m_argv(argv) { // create output directory (OutputDir or "klee-out-") bool dir_given = OutputDir != ""; @@ -540,8 +544,9 @@ KleeHandler::openTestFile(const std::string &suffix, unsigned id) { /* Outputs all files (.ktest, .kquery, .cov etc.) describing a test case */ void KleeHandler::processTestCase(const ExecutionState &state, - const char *errorMessage, - const char *errorSuffix) { + const char *message, const char *suffix, + bool isError) { + unsigned id = ++m_numTotalTests; if (!WriteNone) { KTest ktest; ktest.numArgs = m_argc; @@ -556,8 +561,6 @@ void KleeHandler::processTestCase(const ExecutionState &state, const auto start_time = time::getWallTime(); - unsigned id = ++m_numTotalTests; - if (success) { if (!kTest_toFile( &ktest, @@ -579,10 +582,10 @@ void KleeHandler::processTestCase(const ExecutionState &state, } delete[] ktest.objects; - if (errorMessage) { - auto f = openTestFile(errorSuffix, id); + if (message) { + auto f = openTestFile(suffix, id); if (f) - *f << errorMessage; + *f << message; } if (m_pathWriter) { @@ -597,32 +600,6 @@ void KleeHandler::processTestCase(const ExecutionState &state, } } - if (errorMessage || WriteKQueries) { - std::string constraints; - m_interpreter->getConstraintLog(state, constraints, Interpreter::KQUERY); - auto f = openTestFile("kquery", id); - if (f) - *f << constraints; - } - - if (WriteCVCs) { - // FIXME: If using Z3 as the core solver the emitted file is actually - // SMT-LIBv2 not CVC which is a bit confusing - std::string constraints; - m_interpreter->getConstraintLog(state, constraints, Interpreter::STP); - auto f = openTestFile("cvc", id); - if (f) - *f << constraints; - } - - if (WriteSMT2s) { - std::string constraints; - m_interpreter->getConstraintLog(state, constraints, Interpreter::SMTLIB2); - auto f = openTestFile("smt2", id); - if (f) - *f << constraints; - } - if (m_symPathWriter) { std::vector symbolicBranches; m_symPathWriter->readStream(m_interpreter->getSymbolicPathStreamID(state), @@ -635,27 +612,6 @@ void KleeHandler::processTestCase(const ExecutionState &state, } } - if (WriteKPaths) { - std::string blockPath; - m_interpreter->getBlockPath(state, blockPath); - auto f = openTestFile("kpath", id); - if (f) - *f << blockPath; - } - - if (WriteCov) { - std::map> cov; - m_interpreter->getCoveredLines(state, cov); - auto f = openTestFile("cov", id); - if (f) { - for (const auto &entry : cov) { - for (const auto &line : entry.second) { - *f << *entry.first << ':' << line << '\n'; - } - } - } - } - if (m_numGeneratedTests == MaxTests) m_interpreter->setHaltExecution(HaltExecution::MaxTests); @@ -667,9 +623,56 @@ void KleeHandler::processTestCase(const ExecutionState &state, } } // if (!WriteNone) - if (errorMessage && OptExitOnError) { + if (WriteKQueries) { + std::string constraints; + m_interpreter->getConstraintLog(state, constraints, Interpreter::KQUERY); + auto f = openTestFile("kquery", id); + if (f) + *f << constraints; + } + + if (WriteCVCs) { + // FIXME: If using Z3 as the core solver the emitted file is actually + // SMT-LIBv2 not CVC which is a bit confusing + std::string constraints; + m_interpreter->getConstraintLog(state, constraints, Interpreter::STP); + auto f = openTestFile("cvc", id); + if (f) + *f << constraints; + } + + if (WriteSMT2s) { + std::string constraints; + m_interpreter->getConstraintLog(state, constraints, Interpreter::SMTLIB2); + auto f = openTestFile("smt2", id); + if (f) + *f << constraints; + } + + if (WriteKPaths) { + std::string blockPath; + m_interpreter->getBlockPath(state, blockPath); + auto f = openTestFile("kpath", id); + if (f) + *f << blockPath; + } + + if (WriteCov) { + std::map> cov; + m_interpreter->getCoveredLines(state, cov); + auto f = openTestFile("cov", id); + if (f) { + for (const auto &entry : cov) { + for (const auto &line : entry.second) { + *f << *entry.first << ':' << line << '\n'; + } + } + } + } + + if (isError && OptExitOnError) { m_interpreter->prepareForEarlyExit(); - klee_error("EXITING ON ERROR:\n%s\n", errorMessage); + klee_error("EXITING ON ERROR:\n%s\n", message); } } @@ -1907,7 +1910,10 @@ int run_klee(int argc, char **argv, char **envp) { << handler->getNumPathsExplored() - handler->getNumPathsCompleted() << '\n' << "KLEE: done: generated tests = " << handler->getNumTestCases() - << '\n'; + << '\n' + << "KLEE: done: newly summarized locations = " + << handler->getSummarizedLocaitons() << '\n' + << "KLEE: done: queries = " << queries << '\n'; bool useColors = llvm::errs().is_displayed(); if (useColors) diff --git a/lib/Solver/CMakeLists.txt b/lib/Solver/CMakeLists.txt index d1e4a2d1e7..5fd51f00f6 100644 --- a/lib/Solver/CMakeLists.txt +++ b/lib/Solver/CMakeLists.txt @@ -12,7 +12,6 @@ klee_add_component(kleaverSolver CexCachingSolver.cpp ConstantDivision.cpp ConstructSolverChain.cpp - ConcretizationManager.cpp ConcretizingSolver.cpp CoreSolver.cpp DummySolver.cpp diff --git a/lib/Solver/ConcretizationManager.cpp b/lib/Solver/ConcretizationManager.cpp deleted file mode 100644 index 2c4a4a7a60..0000000000 --- a/lib/Solver/ConcretizationManager.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "klee/Solver/ConcretizationManager.h" -#include "klee/Expr/Assignment.h" -#include "klee/Expr/Constraints.h" -#include "klee/Expr/IndependentSet.h" -#include "klee/Solver/Solver.h" -#include -#include - -using namespace klee; - -std::pair ConcretizationManager::get(const ConstraintSet &set, - ref query) { - if (simplifyExprs) { - query = Simplificator::simplifyExpr(set, query).simplified; - } - CacheEntry ce(set.cs(), set.symcretes(), query); - concretizations_map::iterator it = concretizations.find(ce); - if (it != concretizations.end()) { - return {it->second, true}; - } else { - return {Assignment(true), false}; - } -} - -bool ConcretizationManager::contains(const ConstraintSet &set, - ref query) { - if (simplifyExprs) { - query = Simplificator::simplifyExpr(set, query).simplified; - } - CacheEntry ce(set.cs(), set.symcretes(), query); - concretizations_map::iterator it = concretizations.find(ce); - return it != concretizations.end(); -} - -void ConcretizationManager::add(const Query &query, const Assignment &assign) { - ref expr = query.expr; - if (simplifyExprs) { - expr = Simplificator::simplifyExpr(query.constraints, expr).simplified; - } - CacheEntry ce(query.constraints.cs(), query.constraints.symcretes(), expr); - concretizations.insert(std::make_pair(ce, assign)); - assert(concretizations.find(ce) != concretizations.end()); - assert(contains(query.constraints, expr)); -} diff --git a/lib/Solver/ConcretizingSolver.cpp b/lib/Solver/ConcretizingSolver.cpp index 8d1886ae57..037a33eba4 100644 --- a/lib/Solver/ConcretizingSolver.cpp +++ b/lib/Solver/ConcretizingSolver.cpp @@ -8,7 +8,6 @@ #include "klee/Expr/Symcrete.h" #include "klee/Solver/AddressGenerator.h" -#include "klee/Solver/ConcretizationManager.h" #include "klee/Solver/Solver.h" #include "klee/Solver/SolverImpl.h" #include "klee/Solver/SolverUtil.h" @@ -24,13 +23,11 @@ namespace klee { class ConcretizingSolver : public SolverImpl { private: Solver *solver; - ConcretizationManager *concretizationManager; AddressGenerator *addressGenerator; public: - ConcretizingSolver(Solver *_solver, ConcretizationManager *_cm, - AddressGenerator *_ag) - : solver(_solver), concretizationManager(_cm), addressGenerator(_ag) {} + ConcretizingSolver(Solver *_solver, AddressGenerator *_ag) + : solver(_solver), addressGenerator(_ag) {} ~ConcretizingSolver() { delete solver; } @@ -196,6 +193,18 @@ bool ConcretizingSolver::relaxSymcreteConstraints(const Query &query, } } + for (const ref &symcrete : currentlyBrokenSymcretes) { + constraints_ty required; + IndependentElementSet eltsClosure = getIndependentConstraints( + Query(query.constraints, + AndExpr::create(query.expr, + Expr::createIsZero(symcrete->symcretized))), + required); + for (ref symcrete : eltsClosure.symcretes) { + currentlyBrokenSymcretes.insert(symcrete); + } + } + for (const ref &symcrete : currentlyBrokenSymcretes) { brokenSymcretes.insert(symcrete); for (const Array *array : symcrete->dependentArrays()) { @@ -230,7 +239,6 @@ bool ConcretizingSolver::relaxSymcreteConstraints(const Query &query, Simplificator::simplifyExpr(query.constraints, symbolicSizesSum) .simplified; - ref minimalValueOfSum; ref response; /* Compute assignment for symcretes. */ @@ -242,13 +250,14 @@ bool ConcretizingSolver::relaxSymcreteConstraints(const Query &query, Query(queryConstraints, UgtExpr::create( symbolicSizesSum, - ConstantExpr::create(SymbolicAllocationThreshhold, + ConstantExpr::create(SymbolicAllocationThreshold, symbolicSizesSum->getWidth()))), response)) { return false; } if (!response->tryGetInitialValuesFor(objects, brokenSymcretizedValues)) { + ref minimalValueOfSum; /* Receive model with a smallest sum as possible. */ if (!solver->impl->computeMinimalUnsignedValue( Query(queryConstraints, symbolicSizesSum), minimalValueOfSum)) { @@ -271,6 +280,13 @@ bool ConcretizingSolver::relaxSymcreteConstraints(const Query &query, assignment.bindings[objects[idx]] = brokenSymcretizedValues[idx]; } + ExprHashMap> concretizations; + + for (ref symcrete : query.constraints.symcretes()) { + concretizations[symcrete->symcretized] = + assignment.evaluate(symcrete->symcretized); + } + for (const ref &symcrete : brokenSymcretes) { ref sizeSymcrete = dyn_cast(symcrete); @@ -280,15 +296,10 @@ bool ConcretizingSolver::relaxSymcreteConstraints(const Query &query, /* Receive address array linked with this size array to request address * concretization. */ - uint64_t newSize = - cast(assignment.evaluate(symcrete->symcretized)) - ->getZExtValue(); - - /* TODO: we should be sure that `addressSymcrete` constains only one - * dependent array. */ - assert(sizeSymcrete->addressSymcrete.dependentArrays().size() == 1); - const Array *addressArray = - sizeSymcrete->addressSymcrete.dependentArrays().back(); + ref condcretized = assignment.evaluate(symcrete->symcretized); + + uint64_t newSize = cast(condcretized)->getZExtValue(); + void *address = addressGenerator->allocate( sizeSymcrete->addressSymcrete.symcretized, newSize); unsigned char *charAddressIterator = @@ -296,11 +307,22 @@ bool ConcretizingSolver::relaxSymcreteConstraints(const Query &query, SparseStorage storage(sizeof(address)); storage.store(0, charAddressIterator, charAddressIterator + sizeof(address)); - assignment.bindings[addressArray] = storage; + + concretizations[sizeSymcrete->addressSymcrete.symcretized] = + ConstantExpr::create( + reinterpret_cast(address), + sizeSymcrete->addressSymcrete.symcretized->getWidth()); } - if (!solver->impl->check(constructConcretizedQuery(query, assignment), - result)) { + ref concretizationCondition = query.expr; + for (const auto &concretization : concretizations) { + concretizationCondition = + OrExpr::create(Expr::createIsZero(EqExpr::create( + concretization.first, concretization.second)), + concretizationCondition); + } + + if (!solver->impl->check(query.withExpr(concretizationCondition), result)) { return false; } @@ -373,36 +395,14 @@ bool ConcretizingSolver::computeValidity( // appropriate for the remain branch. if (isa(queryResult)) { - concretizationManager->add( - query, - cast(negatedQueryResult)->initialValuesFor(objects)); if (!relaxSymcreteConstraints(query, queryResult)) { return false; } - if (ref queryInvalidResponse = - dyn_cast(queryResult)) { - concretizationManager->add( - query.negateExpr(), queryInvalidResponse->initialValuesFor(objects)); - } } else if (isa(negatedQueryResult)) { - concretizationManager->add( - query.negateExpr(), - cast(queryResult)->initialValuesFor(objects)); if (!relaxSymcreteConstraints(query.negateExpr(), negatedQueryResult)) { return false; } - if (ref negatedQueryInvalidResponse = - dyn_cast(negatedQueryResult)) { - concretizationManager->add( - query, negatedQueryInvalidResponse->initialValuesFor(objects)); - } } else { - concretizationManager->add( - query.negateExpr(), - cast(queryResult)->initialValuesFor(objects)); - concretizationManager->add( - query, - cast(negatedQueryResult)->initialValuesFor(objects)); } return true; @@ -428,13 +428,6 @@ bool ConcretizingSolver::check(const Query &query, } } - if (ref resultInvalidResponse = - dyn_cast(result)) { - concretizationManager->add( - query.negateExpr(), - resultInvalidResponse->initialValuesFor(assign.keys())); - } - return true; } @@ -445,10 +438,6 @@ char *ConcretizingSolver::getConstraintLog(const Query &query) { bool ConcretizingSolver::computeTruth(const Query &query, bool &isValid) { if (!query.containsSymcretes()) { if (solver->impl->computeTruth(query, isValid)) { - if (!isValid) { - concretizationManager->add(query.negateExpr(), - query.constraints.concretization()); - } return true; } return false; @@ -485,10 +474,6 @@ bool ConcretizingSolver::computeTruth(const Query &query, bool &isValid) { } } - if (!isValid) { - concretizationManager->add(query.negateExpr(), assign); - } - return true; } @@ -535,7 +520,6 @@ bool ConcretizingSolver::computeValidityCore(const Query &query, if (!isValid) { validityCore = ValidityCore(); - concretizationManager->add(query.negateExpr(), assign); } return true; @@ -589,14 +573,11 @@ bool ConcretizingSolver::computeInitialValues( dyn_cast(result)) { hasSolution = true; assign = resultInvalidResponse->initialValuesFor(assign.keys()); - concretizationManager->add(query.negateExpr(), assign); values = std::vector>(); return solver->impl->computeInitialValues( constructConcretizedQuery(query, assign), objects, values, hasSolution); } - } else { - concretizationManager->add(query.negateExpr(), assign); } return true; @@ -612,9 +593,7 @@ void ConcretizingSolver::setCoreSolverTimeout(time::Span timeout) { } Solver *createConcretizingSolver(Solver *s, - ConcretizationManager *concretizationManager, AddressGenerator *addressGenerator) { - return new Solver( - new ConcretizingSolver(s, concretizationManager, addressGenerator)); + return new Solver(new ConcretizingSolver(s, addressGenerator)); } } // namespace klee diff --git a/lib/Solver/ConstructSolverChain.cpp b/lib/Solver/ConstructSolverChain.cpp index 3e8308441c..cb25e99c6a 100644 --- a/lib/Solver/ConstructSolverChain.cpp +++ b/lib/Solver/ConstructSolverChain.cpp @@ -22,14 +22,12 @@ #include namespace klee { -class ConcretizationManager; class AddressGenerator; Solver *constructSolverChain(Solver *coreSolver, std::string querySMT2LogPath, std::string baseSolverQuerySMT2LogPath, std::string queryKQueryLogPath, std::string baseSolverQueryKQueryLogPath, - ConcretizationManager *concretizationManager, AddressGenerator *addressGenerator) { Solver *solver = coreSolver; const time::Span minQueryTimeToLog(MinQueryTimeToLog); @@ -63,9 +61,8 @@ Solver *constructSolverChain(Solver *coreSolver, std::string querySMT2LogPath, if (UseIndependentSolver) solver = createIndependentSolver(solver); - if (UseConcretizingSolver && concretizationManager) - solver = createConcretizingSolver(solver, concretizationManager, - addressGenerator); + if (UseConcretizingSolver) + solver = createConcretizingSolver(solver, addressGenerator); if (DebugValidateSolver) solver = createValidatingSolver(solver, coreSolver); diff --git a/lib/Solver/QueryLoggingSolver.cpp b/lib/Solver/QueryLoggingSolver.cpp index 32f1d7d996..ce53f40a7e 100644 --- a/lib/Solver/QueryLoggingSolver.cpp +++ b/lib/Solver/QueryLoggingSolver.cpp @@ -10,6 +10,7 @@ #include "klee/Config/config.h" #include "klee/Expr/Constraints.h" +#include "klee/Expr/ExprUtil.h" #include "klee/Statistics/Statistics.h" #include "klee/Support/ErrorHandling.h" #include "klee/Support/FileHandling.h" @@ -169,8 +170,17 @@ bool QueryLoggingSolver::computeInitialValues( std::vector> &values, bool &hasSolution) { startQuery(query, "InitialValues", 0, &objects); - bool success = - solver->impl->computeInitialValues(query, objects, values, hasSolution); + ExprHashSet expressions; + expressions.insert(query.constraints.cs().begin(), + query.constraints.cs().end()); + expressions.insert(query.expr); + + std::vector allObjects; + findSymbolicObjects(expressions.begin(), expressions.end(), allObjects); + std::vector> allValues; + + bool success = solver->impl->computeInitialValues(query, allObjects, + allValues, hasSolution); finishQuery(success); @@ -178,6 +188,11 @@ bool QueryLoggingSolver::computeInitialValues( logBuffer << queryCommentSign << " Solvable: " << (hasSolution ? "true" : "false") << "\n"; if (hasSolution) { + ref invalidResponse = + new InvalidResponse(allObjects, allValues); + success = invalidResponse->tryGetInitialValuesFor(objects, values); + assert(success); + Assignment allSolutionAssignment(allObjects, allValues, true); std::vector>::iterator values_it = values.begin(); @@ -190,7 +205,7 @@ bool QueryLoggingSolver::computeInitialValues( logBuffer << queryCommentSign << " " << array->getIdentifier() << " = ["; ref arrayConstantSize = - dyn_cast(solutionAssignment.evaluate(array->size)); + dyn_cast(allSolutionAssignment.evaluate(array->size)); assert(arrayConstantSize && "Array of symbolic size had not receive value for size!"); diff --git a/lib/Solver/Solver.cpp b/lib/Solver/Solver.cpp index 3a104701fe..70aa655708 100644 --- a/lib/Solver/Solver.cpp +++ b/lib/Solver/Solver.cpp @@ -325,12 +325,7 @@ bool Query::containsSizeSymcretes() const { } void Query::dump() const { - llvm::errs() << "Constraints [\n"; - - for (const auto &constraint : constraints.cs()) - constraint->dump(); // TODO - - llvm::errs() << "]\n"; + constraints.dump(); llvm::errs() << "Query [\n"; expr->dump(); llvm::errs() << "]\n"; diff --git a/lib/Solver/SolverCmdLine.cpp b/lib/Solver/SolverCmdLine.cpp index fad32223ae..f1a66b20be 100644 --- a/lib/Solver/SolverCmdLine.cpp +++ b/lib/Solver/SolverCmdLine.cpp @@ -220,8 +220,8 @@ llvm::cl::opt llvm::cl::desc("Produce unsat core (default=true)."), llvm::cl::cat(klee::SolvingCat)); -llvm::cl::opt SymbolicAllocationThreshhold( - "symbolic-allocation-threshhold", +llvm::cl::opt SymbolicAllocationThreshold( + "symbolic-allocation-threshold", llvm::cl::desc("Maximum possible sum of sizes for all symbolic allocation " "before minimazation (default 1Kb)"), llvm::cl::init(1024), llvm::cl::cat(klee::SolvingCat)); diff --git a/lib/Support/CMakeLists.txt b/lib/Support/CMakeLists.txt index bf98ce5c10..c08c5099fd 100644 --- a/lib/Support/CMakeLists.txt +++ b/lib/Support/CMakeLists.txt @@ -8,6 +8,7 @@ #===------------------------------------------------------------------------===# klee_add_component(kleeSupport CompressionStream.cpp + DebugFlags.cpp ErrorHandling.cpp FileHandling.cpp MemoryUsage.cpp diff --git a/lib/Support/DebugFlags.cpp b/lib/Support/DebugFlags.cpp new file mode 100644 index 0000000000..2573f648a2 --- /dev/null +++ b/lib/Support/DebugFlags.cpp @@ -0,0 +1,29 @@ +#include "klee/Support/DebugFlags.h" +#include "llvm/Support/CommandLine.h" + +namespace klee { +cl::bits debugPrints( + "debug", cl::desc("What categories to debug-print"), + cl::values(clEnumValN(DebugPrint::Forward, "forward", "Forward"), + clEnumValN(DebugPrint::Backward, "backward", "Backward"), + clEnumValN(DebugPrint::Init, "init", "Requested inits"), + clEnumValN(DebugPrint::Reached, "reached", + "Reached isolated states"), + clEnumValN(DebugPrint::Lemma, "lemma", "Lemmas"), + clEnumValN(DebugPrint::RootPob, "rootpob", "Root pobs"), + clEnumValN(DebugPrint::ClosePob, "closepob", "Close pob"), + clEnumValN(DebugPrint::Conflict, "conflict", "Conflicts"), + clEnumValN(DebugPrint::PathForest, "pathforest", "Path forest")), + cl::CommaSeparated); + +cl::bits debugConstraints( + "debug-constraints", + cl::desc("What categories to print constraints for in debug prints"), + cl::values(clEnumValN(DebugPrint::Forward, "forward", "Forward"), + clEnumValN(DebugPrint::Backward, "backward", "Backward"), + clEnumValN(DebugPrint::Reached, "reached", + "Reached isolated states"), + clEnumValN(DebugPrint::Lemma, "lemma", "Lemmas"), + clEnumValN(DebugPrint::Conflict, "conflict", "Conflicts")), + cl::CommaSeparated); +}; // namespace klee diff --git a/runtime/klee-libc/strcmp.c b/runtime/klee-libc/strcmp.c index cdb002c739..2a81fe9f21 100644 --- a/runtime/klee-libc/strcmp.c +++ b/runtime/klee-libc/strcmp.c @@ -10,5 +10,5 @@ int strcmp(const char *a, const char *b) { while (*a && *a == *b) ++a, ++b; - return *a - *b; + return *(const unsigned char *)a - *(const unsigned char *)b; } diff --git a/test/Bidirectional/fib-sum-dec.c b/test/Bidirectional/fib-sum-dec.c new file mode 100644 index 0000000000..667ffe409a --- /dev/null +++ b/test/Bidirectional/fib-sum-dec.c @@ -0,0 +1,38 @@ +// REQUIRES: z3 +// RUN: %clang %s -emit-llvm %O0opt -c -o %t.bc +// RUN: rm -rf %t.klee-out +// RUN: %klee --write-kqueries --output-dir=%t.klee-out --solver-backend=z3 --execution-mode=bidirectional --max-propagations=3 --max-stack-frames=4 --skip-not-lazy-initialized --skip-not-symbolic-objects --debug=rootpob,backward,conflict %t.bc 2> %t.log +// RUN: FileCheck %s -input-file=%t.log +// RUN: sed -n '/\[pob\]/,$p' %t.log > %t.log.tail +// RUN: diff %t.log.tail %s.good + +#include "klee/klee.h" +#include +#include + +int dec(int n) { + return --n; +} + +int sum(int a, int b) { + return a + b; +} + +int fib(int n) { + if (n == 0 || n == 1) + return 1; + return sum(fib(dec(n)), fib(dec(dec(n)))); +} + +int main() { + int n = 0; + klee_make_symbolic(&n, sizeof(n), "n"); + klee_assume(n > 0); + int f1 = fib(n); + int f2 = fib(n + 1); + int f3 = fib(n + 2); + klee_assert(f1 + f2 == f3); + return 0; +} + +// CHECK: KLEE: done: newly summarized locations = 1 diff --git a/test/Bidirectional/fib-sum-dec.c.good b/test/Bidirectional/fib-sum-dec.c.good new file mode 100644 index 0000000000..06b5a97411 --- /dev/null +++ b/test/Bidirectional/fib-sum-dec.c.good @@ -0,0 +1,280 @@ +[pob] New root proof obligation at: Target 0: %9 in function fib +KLEE: WARNING: Maximum stack size reached. +[pob] New root proof obligation at: Target 0: %8 in function fib +[backward] State: (path: 0 (fib: %1) 8) at 0 at %9 in function fib (load) +[backward] Pob: (path: 0 (fib: %9) 0) +[backward] State: (path: 0 (main: %21) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0 (fib: %1 %9) 0) +[backward] State: (path: 0 (main: %16) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0 (fib: %1 %9) 0) +[backward] State: (path: 0 (main: %26) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0 (fib: %1 %9) 0) +[backward] State: (path: 0 (fib: %13) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0 (fib: %1 %9) 0) +[backward] State: (path: 0 (fib: %21) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0 (fib: %1 %9) 0) +[backward] State: (path: 0 (fib: %1) 8) at 0 at %8 in function fib (store) +[backward] Pob: (path: 0 (fib: %8) 0) +[backward] State: (path: 0 (main: %21) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0 (fib: %1 %8) 0) +[backward] State: (path: 0 (main: %16) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0 (fib: %1 %8) 0) +[backward] State: (path: 0 (main: %26) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0 (fib: %1 %8) 0) +[backward] State: (path: 0 (fib: %13) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0 (fib: %1 %8) 0) +[backward] State: (path: 0 (fib: %21) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0 (fib: %1 %8) 0) +[backward] State: (path: 1 (fib: %11) 1) at 0 at %13 in function fib (call) +[backward] Pob: (path: 0 (fib: %13 (fib: %1 %9)) 0) +[backward] State: (path: 1 (fib: %19) 1) at 0 at %21 in function fib (call) +[backward] Pob: (path: 0 (fib: %21 (fib: %1 %9)) 0) +[backward] State: (path: 1 (fib: %11) 1) at 0 at %13 in function fib (call) +[backward] Pob: (path: 0 (fib: %13 (fib: %1 %8)) 0) +[backward] State: (path: 1 (fib: %19) 1) at 0 at %21 in function fib (call) +[backward] Pob: (path: 0 (fib: %21 (fib: %1 %8)) 0) +[backward] State: (path: 1 (main: %21 %23) 3) at 0 at %26 in function main (call) +[backward] Pob: (path: 0 (main: %26 (fib: %1 %9)) 0) +[backward] State: (path: 1 (main: %21 %23) 3) at 0 at %26 in function main (call) +[backward] Pob: (path: 0 (main: %26 (fib: %1 %8)) 0) +[backward] State: (path: 1 (main: %16 %18) 3) at 0 at %21 in function main (call) +[backward] Pob: (path: 0 (main: %21 (fib: %1 %9)) 0) +[backward] State: (path: 1 (main: %16 %18) 3) at 0 at %21 in function main (call) +[backward] Pob: (path: 0 (main: %21 (fib: %1 %8)) 0) +[backward] State: (path: 0 (dec: %1 %5) 0) at 0 at %5 in function dec (ret) +[backward] Pob: (path: 1 (fib: %11 %13 (fib: %1 %9)) 0) +[backward] State: (path: 0 (dec: %1 %5) 0) at 0 at %5 in function dec (ret) +[backward] Pob: (path: 1 (fib: %19 %21 (fib: %1 %9)) 0) +[backward] State: (path: 0 (dec: %1 %5) 0) at 0 at %5 in function dec (ret) +[backward] Pob: (path: 1 (fib: %11 %13 (fib: %1 %8)) 0) +[backward] State: (path: 0 (dec: %1 %5) 0) at 0 at %5 in function dec (ret) +[backward] Pob: (path: 1 (fib: %19 %21 (fib: %1 %8)) 0) +[backward] State: (path: 0 (fib: %26 %28) 0) at 0 at %28 in function fib (ret) +[backward] Pob: (path: 1 (main: %21 %23 %26 (fib: %1 %9)) 0) +[backward] State: (path: 0 (fib: %26 %28) 0) at 0 at %28 in function fib (ret) +[backward] Pob: (path: 1 (main: %21 %23 %26 (fib: %1 %8)) 0) +[backward] State: (path: 0 (fib: %26 %28) 0) at 0 at %28 in function fib (ret) +[backward] Pob: (path: 1 (main: %16 %18 %21 (fib: %1 %9)) 0) +[backward] State: (path: 0 (fib: %26 %28) 0) at 0 at %28 in function fib (ret) +[backward] Pob: (path: 1 (main: %16 %18 %21 (fib: %1 %8)) 0) +[backward] State: (path: 0 (fib: %1 %8) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %21 %23 %26 (fib: %1 %9)) 0) +[backward] State: (path: 0 (fib: %11) 0) at 0 at %1 in function dec (alloca) +[backward] Pob: (path: 0(fib: (dec: %1 %5) %11 %13 (fib: %1 %9)) 0) +[backward] State: (path: 0 (fib: %11) 0) at 0 at %1 in function dec (alloca) +[backward] Pob: (path: 0(fib: (dec: %1 %5) %11 %13 (fib: %1 %8)) 0) +[backward] State: (path: 0 (fib: %19) 0) at 0 at %1 in function dec (alloca) +[backward] Pob: (path: 0(fib: (dec: %1 %5) %19 %21 (fib: %1 %9)) 0) +[backward] State: (path: 0 (fib: %19) 0) at 0 at %1 in function dec (alloca) +[backward] Pob: (path: 0(fib: (dec: %1 %5) %19 %21 (fib: %1 %8)) 0) +[backward] State: (path: 0 (fib: %1 %8) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %21 %23 %26 (fib: %1 %8)) 0) +[backward] State: (path: 0 (fib: %1 %8) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %16 %18 %21 (fib: %1 %9)) 0) +[backward] State: (path: 0 (fib: %1 %8) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %16 %18 %21 (fib: %1 %8)) 0) +[backward] State: (path: 0 (main: %21) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0(main: (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %9)) 0) +[backward] State: (path: 1 (fib: %23 %25) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %21 %23 %26 (fib: %1 %9)) 0) +[backward] State: (path: 1 (fib: %23 %25) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %21 %23 %26 (fib: %1 %8)) 0) +[backward] State: (path: 1 (fib: %23 %25) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %16 %18 %21 (fib: %1 %9)) 0) +[backward] State: (path: 1 (fib: %23 %25) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %16 %18 %21 (fib: %1 %8)) 0) +[backward] State: (path: 0 (main: %0 %7 %8 %13 %14) 1) at 0 at %16 in function main (call) +[backward] Pob: (path: 0 (main: %16 (fib: %1 %9)) 0) +[backward] State: (path: 0 (main: %0 %7 %8 %13 %14) 1) at 0 at %16 in function main (call) +[backward] Pob: (path: 0 (main: %16 (fib: %1 %8)) 0) +[backward] State: (path: 0 (main: %21) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0(main: (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %8)) 0) +[backward] State: (path: 0 (main: %16) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0(main: (fib: %1 %8 %26 %28) %16 %18 %21 (fib: %1 %9)) 0) +[backward] State: (path: 1 (fib: %17) 1) at 0 at %19 in function fib (call) +[backward] Pob: (path: 0 (fib: %19 (dec: %1 %5) %19 %21 (fib: %1 %9)) 0) +[backward] State: (path: 1 (fib: %17) 1) at 0 at %19 in function fib (call) +[backward] Pob: (path: 0 (fib: %19 (dec: %1 %5) %19 %21 (fib: %1 %8)) 0) +[backward] State: (path: 0 (main: %16) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0(main: (fib: %1 %8 %26 %28) %16 %18 %21 (fib: %1 %8)) 0) +[backward] State: (path: 1 (main: %16 %18) 3) at 0 at %21 in function main (call) +[backward] Pob: (path: 0 (main: %21 (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %9)) 0) +[backward] State: (path: 0 (sum: %2 %8) 0) at 0 at %8 in function sum (ret) +[backward] Pob: (path: 1(main: (fib: %23 %25 %26 %28) %21 %23 %26 (fib: %1 %9)) 0) +[backward] State: (path: 0 (sum: %2 %8) 0) at 0 at %8 in function sum (ret) +[backward] Pob: (path: 1(main: (fib: %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8)) 0) +[backward] State: (path: 0 (sum: %2 %8) 0) at 0 at %8 in function sum (ret) +[backward] Pob: (path: 1(main: (fib: %23 %25 %26 %28) %16 %18 %21 (fib: %1 %9)) 0) +[backward] State: (path: 0 (sum: %2 %8) 0) at 0 at %8 in function sum (ret) +[backward] Pob: (path: 1(main: (fib: %23 %25 %26 %28) %16 %18 %21 (fib: %1 %8)) 0) +[backward] State: (path: 0 0) at 0 at %0 in function main (alloca) +[backward] Pob: (path: 0 (main: %0 %7 %8 %13 %14 %16 (fib: %1 %9)) 0) +[backward] State: (path: 0 0) at 0 at %0 in function main (alloca) +[backward] Pob: (path: 0 (main: %0 %7 %8 %13 %14 %16 (fib: %1 %8)) 0) +KLEE: WARNING: Maximum stack size reached. +[pob] New root proof obligation at: Target 0: %34 in function main +KLEE: WARNING: Maximum stack size reached. +[backward] State: (path: 1 (main: %26 %28) 6) at 0 at %34 in function main (call) +[backward] Pob: (path: 0 (main: %34) 0) +[backward] State: (path: 0 (fib: %26 %28) 0) at 0 at %28 in function fib (ret) +[backward] Pob: (path: 1 (main: %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %1 %8) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %23 %25) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (main: %26) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0(main: (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (sum: %2 %8) 0) at 0 at %8 in function sum (ret) +[backward] Pob: (path: 1(main: (fib: %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (main: %21 %23) 3) at 0 at %26 in function main (call) +[backward] Pob: (path: 0 (main: %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %23) 0) at 0 at %2 in function sum (alloca) +[backward] Pob: (path: 0(main:(fib: (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %26 %28) 0) at 0 at %28 in function fib (ret) +[backward] Pob: (path: 1 (main: %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %23 %25) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (sum: %2 %8) 0) at 0 at %8 in function sum (ret) +[backward] Pob: (path: 1(main: (fib: %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %21) 1) at 0 at %23 in function fib (call) +[backward] Pob: (path: 0(main: (fib: %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %23) 0) at 0 at %2 in function sum (alloca) +[backward] Pob: (path: 0(main:(fib: (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %21) 1) at 0 at %23 in function fib (call) +[backward] Pob: (path: 0(main: (fib: %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %1 %8) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (main: %21) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0(main: (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (main: %16 %18) 3) at 0 at %21 in function main (call) +[backward] Pob: (path: 0 (main: %21 (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %26 %28) 0) at 0 at %28 in function fib (ret) +[backward] Pob: (path: 1(main: (fib: %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %1 %8) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main:(fib: (fib: %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %21) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0(main:(fib: (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %19) 1) at 0 at %21 in function fib (call) +[backward] Pob: (path: 0(main: (fib: %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (dec: %1 %5) 0) at 0 at %5 in function dec (ret) +[backward] Pob: (path: 1(main: (fib: %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %19) 0) at 0 at %1 in function dec (alloca) +[backward] Pob: (path: 0(main:(fib: (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %17) 1) at 0 at %19 in function fib (call) +[backward] Pob: (path: 0(main: (fib: %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %23 %25) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main:(fib: (fib: %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (dec: %1 %5) 0) at 0 at %5 in function dec (ret) +[backward] Pob: (path: 1(main: (fib: %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %17) 0) at 0 at %1 in function dec (alloca) +[backward] Pob: (path: 0(main:(fib: (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (sum: %2 %8) 0) at 0 at %8 in function sum (ret) +[backward] Pob: (path: 1(main:(fib: (fib: %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %23) 0) at 0 at %2 in function sum (alloca) +[backward] Pob: (path: 0(main:(fib:(fib: (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %21) 1) at 0 at %23 in function fib (call) +[backward] Pob: (path: 0(main:(fib: (fib: %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %13 %15) 1) at 0 at %17 in function fib (call) +[backward] Pob: (path: 0(main: (fib: %17 (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %26 %28) 0) at 0 at %28 in function fib (ret) +[backward] Pob: (path: 1(main: (fib: %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %1 %8) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main:(fib: (fib: %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %21) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0(main:(fib: (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %19) 1) at 0 at %21 in function fib (call) +[backward] Pob: (path: 0(main: (fib: %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (dec: %1 %5) 0) at 0 at %5 in function dec (ret) +[backward] Pob: (path: 1(main: (fib: %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %19) 0) at 0 at %1 in function dec (alloca) +[backward] Pob: (path: 0(main:(fib: (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %17) 1) at 0 at %19 in function fib (call) +[backward] Pob: (path: 0(main: (fib: %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %23 %25) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main:(fib: (fib: %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (dec: %1 %5) 0) at 0 at %5 in function dec (ret) +[backward] Pob: (path: 1(main: (fib: %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %17) 0) at 0 at %1 in function dec (alloca) +[backward] Pob: (path: 0(main:(fib: (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %13 %15) 1) at 0 at %17 in function fib (call) +[backward] Pob: (path: 0(main: (fib: %17 (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (sum: %2 %8) 0) at 0 at %8 in function sum (ret) +[backward] Pob: (path: 1(main:(fib: (fib: %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %23) 0) at 0 at %2 in function sum (alloca) +[backward] Pob: (path: 0(main:(fib:(fib: (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %21) 1) at 0 at %23 in function fib (call) +[backward] Pob: (path: 0(main:(fib: (fib: %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %26 %28) 0) at 0 at %28 in function fib (ret) +[backward] Pob: (path: 1 (main: %16 %18 %21 (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %23 %25) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %16 %18 %21 (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (sum: %2 %8) 0) at 0 at %8 in function sum (ret) +[backward] Pob: (path: 1(main: (fib: %23 %25 %26 %28) %16 %18 %21 (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %23) 0) at 0 at %2 in function sum (alloca) +[backward] Pob: (path: 0(main:(fib: (sum: %2 %8) %23 %25 %26 %28) %16 %18 %21 (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %21) 1) at 0 at %23 in function fib (call) +[backward] Pob: (path: 0(main: (fib: %23 (sum: %2 %8) %23 %25 %26 %28) %16 %18 %21 (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %26 %28) 0) at 0 at %28 in function fib (ret) +[backward] Pob: (path: 1(main:(fib: (fib: %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %1 %8) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main:(fib:(fib: (fib: %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %21) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0(main:(fib:(fib: (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %19) 1) at 0 at %21 in function fib (call) +[backward] Pob: (path: 0(main:(fib: (fib: %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (dec: %1 %5) 0) at 0 at %5 in function dec (ret) +[backward] Pob: (path: 1(main:(fib: (fib: %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %19) 0) at 0 at %1 in function dec (alloca) +[backward] Pob: (path: 0(main:(fib:(fib: (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %17) 1) at 0 at %19 in function fib (call) +[backward] Pob: (path: 0(main:(fib: (fib: %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (dec: %1 %5) 0) at 0 at %5 in function dec (ret) +[backward] Pob: (path: 1(main:(fib: (fib: %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %17) 0) at 0 at %1 in function dec (alloca) +[backward] Pob: (path: 0(main:(fib:(fib: (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %13 %15) 1) at 0 at %17 in function fib (call) +[backward] Pob: (path: 0(main:(fib: (fib: %17 (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %26 %28) 0) at 0 at %28 in function fib (ret) +[backward] Pob: (path: 1(main: (fib: %13 %15 %17 (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %1 %8) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main:(fib: (fib: %26 %28) %13 %15 %17 (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %13) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0(main:(fib: (fib: %1 %8 %26 %28) %13 %15 %17 (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %11) 1) at 0 at %13 in function fib (call) +[backward] Pob: (path: 0(main: (fib: %13 (fib: %1 %8 %26 %28) %13 %15 %17 (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %23 %25) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main:(fib: (fib: %26 %28) %13 %15 %17 (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (sum: %2 %8) 0) at 0 at %8 in function sum (ret) +[backward] Pob: (path: 1(main:(fib: (fib: %23 %25 %26 %28) %13 %15 %17 (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %23) 0) at 0 at %2 in function sum (alloca) +[backward] Pob: (path: 0(main:(fib:(fib: (sum: %2 %8) %23 %25 %26 %28) %13 %15 %17 (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %21) 1) at 0 at %23 in function fib (call) +[backward] Pob: (path: 0(main:(fib: (fib: %23 (sum: %2 %8) %23 %25 %26 %28) %13 %15 %17 (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %1 %8) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main: (fib: %26 %28) %16 %18 %21 (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (main: %16) 0) at 0 at %1 in function fib (alloca) +[backward] Pob: (path: 0(main: (fib: %1 %8 %26 %28) %16 %18 %21 (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (main: %0 %7 %8 %13 %14) 1) at 0 at %16 in function main (call) +[backward] Pob: (path: 0 (main: %16 (fib: %1 %8 %26 %28) %16 %18 %21 (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[conflict] Conflict in backward: (path: 0 (main: %0 %7 %8 %13 %14 %16 (fib: %1 %8 %26 %28) %16 %18 %21 (fib: %1 %8 %26 %28) %21 %23 %26 (fib: %1 %8 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %23 %25) 1) at 0 at %26 in function fib (load) +[backward] Pob: (path: 0(main:(fib:(fib: (fib: %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (dec: %1 %5) 0) at 0 at %5 in function dec (ret) +[backward] Pob: (path: 1(main: (fib: %11 %13 (fib: %1 %8 %26 %28) %13 %15 %17 (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %11) 0) at 0 at %1 in function dec (alloca) +[backward] Pob: (path: 0(main:(fib: (dec: %1 %5) %11 %13 (fib: %1 %8 %26 %28) %13 %15 %17 (dec: %1 %5) %17 %19 (dec: %1 %5) %19 %21 (fib: %1 %8 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (sum: %2 %8) 0) at 0 at %8 in function sum (ret) +[backward] Pob: (path: 1(main:(fib:(fib: (fib: %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 0 (fib: %23) 0) at 0 at %2 in function sum (alloca) +[backward] Pob: (path: 0(main:(fib:(fib:(fib: (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +[backward] State: (path: 1 (fib: %21) 1) at 0 at %23 in function fib (call) +[backward] Pob: (path: 0(main:(fib:(fib: (fib: %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %21 %23 (sum: %2 %8) %23 %25 %26 %28) %26 %28 %34) 0) +KLEE: 0 states remaining +KLEE: Distances from nearest states to remaining targets: + +KLEE: done: total instructions = 1750 +KLEE: done: completed paths = 2 +KLEE: done: partially completed paths = 3 +KLEE: done: generated tests = 5 +KLEE: done: newly summarized locations = 1 +KLEE: done: queries = 807 diff --git a/test/Feature/BlockPath.ll b/test/Feature/BlockPath.ll new file mode 100644 index 0000000000..b46b04e96f --- /dev/null +++ b/test/Feature/BlockPath.ll @@ -0,0 +1,82 @@ +; REQUIRES: geq-llvm-12.0 +; RUN: %llvmas %s -o %t1.bc +; RUN: rm -rf %t.klee-out +; RUN: %klee --output-dir=%t.klee-out --libc=klee --write-kpaths %t1.bc +; RUN: grep "(path: 0 (main: %0 %5 %6 %8 (abs: %1 %8 %11 %13) %8 %10 %16 %17 %19" %t.klee-out/test000001.kpath +; RUN: grep "(path: 0 (main: %0 %5 %6 %8 (abs: %1 %6 %11 %13) %8 %10 %16 %17 %19" %t.klee-out/test000002.kpath + +@.str = private unnamed_addr constant [2 x i8] c"x\00", align 1 + +define i32 @abs(i32 %0) { + %2 = alloca i32, align 4 + %3 = alloca i32, align 4 + store i32 %0, i32* %3, align 4 + %4 = load i32, i32* %3, align 4 + %5 = icmp sge i32 %4, 0 + br i1 %5, label %6, label %8 + +6: ; preds = %1 + %7 = load i32, i32* %3, align 4 + store i32 %7, i32* %2, align 4 + br label %11 + +8: ; preds = %1 + %9 = load i32, i32* %3, align 4 + %10 = sub nsw i32 0, %9 + store i32 %10, i32* %2, align 4 + br label %11 + +11: ; preds = %8, %6 + %12 = load i32, i32* %2, align 4 + br label %13 + +13: ; preds = %11 + ret i32 %12 +} + +define i32 @main() { + %1 = alloca i32, align 4 + %2 = alloca i32, align 4 + %3 = alloca i32, align 4 + store i32 0, i32* %1, align 4 + %4 = bitcast i32* %2 to i8* + br label %5 + +5: ; preds = %0 + call void @klee_make_symbolic(i8* %4, i64 4, i8* getelementptr inbounds ([2 x i8], [2 x i8]* @.str, i64 0, i64 0)) + br label %6 + +6: ; preds = %5 + %7 = load i32, i32* %2, align 4 + br label %8 + +8: ; preds = %6 + %9 = call i32 @abs(i32 %7) #4 + br label %10 + +10: ; preds = %8 + store i32 %9, i32* %3, align 4 + %11 = load i32, i32* %3, align 4 + %12 = icmp ne i32 %11, -2147483648 + %13 = load i32, i32* %3, align 4 + %14 = icmp slt i32 %13, 0 + %or.cond = select i1 %12, i1 %14, i1 false + br i1 %or.cond, label %15, label %16 + +15: ; preds = %10 + store i32 1, i32* %1, align 4 + br label %17 + +16: ; preds = %10 + store i32 0, i32* %1, align 4 + br label %17 + +17: ; preds = %16, %15 + %18 = load i32, i32* %1, align 4 + br label %19 + +19: ; preds = %17 + ret i32 %18 +} + +declare void @klee_make_symbolic(i8*, i64, i8*) diff --git a/test/Feature/LazyInitialization/ImpossibleAddressForLI.c b/test/Feature/LazyInitialization/ImpossibleAddressForLI.c index 9dd2a4b80a..abc643eb05 100644 --- a/test/Feature/LazyInitialization/ImpossibleAddressForLI.c +++ b/test/Feature/LazyInitialization/ImpossibleAddressForLI.c @@ -17,4 +17,4 @@ int main() { } // CHECK: KLEE: done: completed paths = 1 -// CHECK: KLEE: done: generated tests = 4 +// CHECK: KLEE: done: generated tests = 3 diff --git a/test/Feature/LazyInitialization/SingleInitializationAndAccess.c b/test/Feature/LazyInitialization/SingleInitializationAndAccess.c index 172e9850b9..1def3a9876 100644 --- a/test/Feature/LazyInitialization/SingleInitializationAndAccess.c +++ b/test/Feature/LazyInitialization/SingleInitializationAndAccess.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -emit-llvm -g -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --skip-not-symbolic-objects --skip-local=false %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --skip-not-symbolic-objects --skip-local=false --use-sym-size-li --min-number-elements-li=1 %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include @@ -9,11 +9,9 @@ int main() { int *x; klee_make_symbolic(&x, sizeof(x), "*x"); - // CHECK: SingleInitializationAndAccess.c:[[@LINE+2]]: memory error: null pointer exception - // CHECK: SingleInitializationAndAccess.c:[[@LINE+1]]: memory error: out of bound pointer + // CHECK: SingleInitializationAndAccess.c:[[@LINE+1]]: memory error: null pointer exception *x = 10; - // CHECK-NOT: SingleInitializationAndAccess.c:[[@LINE+2]]: memory error: null pointer exception - // CHECK: SingleInitializationAndAccess.c:[[@LINE+1]]: memory error: out of bound pointer + // CHECK-NOT: SingleInitializationAndAccess.c:[[@LINE+1]]: memory error: null pointer exception if (*x == 10) { // CHECK-NOT: SingleInitializationAndAccess.c:[[@LINE+2]]: memory error: null pointer exception // CHECK-NOT: SingleInitializationAndAccess.c:[[@LINE+1]]: memory error: out of bound pointer @@ -28,4 +26,4 @@ int main() { } // CHECK: KLEE: done: completed paths = 1 -// CHECK: KLEE: done: generated tests = 5 +// CHECK: KLEE: done: generated tests = 4 diff --git a/test/Feature/MemoryLimit.c b/test/Feature/MemoryLimit.c index 640850d2aa..330b2547ea 100644 --- a/test/Feature/MemoryLimit.c +++ b/test/Feature/MemoryLimit.c @@ -7,13 +7,13 @@ // RUN: %clang -emit-llvm -DLITTLE_ALLOC -g -c %s -o %t.little.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --max-memory=20 %t.little.bc > %t.little.log +// RUN: %klee --output-dir=%t.klee-out --max-memory=20 --max-cycles-before-stuck=0 %t.little.bc > %t.little.log // RUN: FileCheck -check-prefix=CHECK-LITTLE -input-file=%t.little.log %s // RUN: FileCheck -check-prefix=CHECK-WRN -input-file=%t.klee-out/warnings.txt %s // RUN: %clang -emit-llvm -g -c %s -o %t.big.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --max-memory=20 %t.big.bc > %t.big.log 2> %t.big.err +// RUN: %klee --output-dir=%t.klee-out --max-memory=20 --max-cycles-before-stuck=0 %t.big.bc > %t.big.log 2> %t.big.err // RUN: FileCheck -check-prefix=CHECK-BIG -input-file=%t.big.log %s // RUN: FileCheck -check-prefix=CHECK-WRN -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Feature/MultipleReallocResolution.c b/test/Feature/MultipleReallocResolution.c index bc531555a8..d7a182ab11 100644 --- a/test/Feature/MultipleReallocResolution.c +++ b/test/Feature/MultipleReallocResolution.c @@ -1,6 +1,6 @@ // RUN: %clang %s -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out %t1.bc +// RUN: %klee --use-guided-search=none --output-dir=%t.klee-out %t1.bc // RUN: ls %t.klee-out/ | grep .err | wc -l | grep 2 // RUN: ls %t.klee-out/ | grep .ptr.err | wc -l | grep 2 diff --git a/test/Feature/NotCallingUnreachableBranchesInForkErrorGuided.c b/test/Feature/NotCallingUnreachableBranchesInForkErrorGuided.c new file mode 100644 index 0000000000..784c0151e2 --- /dev/null +++ b/test/Feature/NotCallingUnreachableBranchesInForkErrorGuided.c @@ -0,0 +1,15 @@ +// RUN: %clang %s -emit-llvm -g -c -o %t1.bc +// RUN: rm -rf %t.klee-out +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=error --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/info %s +// CHECK: KLEE: done: total queries = 1 +#include "assert.h" +#include "klee/klee.h" + +int main(int x) { + if (x > 0) { + return 0; + } + x = 42; + return 0; +} diff --git a/test/Feature/NotCallingUnreachableBranchesInForkErrorGuided.c.json b/test/Feature/NotCallingUnreachableBranchesInForkErrorGuided.c.json new file mode 100644 index 0000000000..ec8403ab45 --- /dev/null +++ b/test/Feature/NotCallingUnreachableBranchesInForkErrorGuided.c.json @@ -0,0 +1,65 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "SecB" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/tmp/NotCallingUnreachableBranchesInForkErrorGuided.c" + }, + "region": { + "startLine": 9, + "startColumn": 14 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/tmp/NotCallingUnreachableBranchesInForkErrorGuided.c" + }, + "region": { + "startLine": 13, + "startColumn": 5 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "Reached", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/tmp/NotCallingUnreachableBranchesInForkErrorGuided.c" + }, + "region": { + "startLine": 13, + "startColumn": 5 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Feature/RecursionPruning/sum.c b/test/Feature/RecursionPruning/sum.c index d4a917398c..bc74f0e85f 100644 --- a/test/Feature/RecursionPruning/sum.c +++ b/test/Feature/RecursionPruning/sum.c @@ -13,9 +13,9 @@ int main() { sn = sn + a; } if (sn > 10) { - printf("sn > 10"); + printf("sn > 10\n"); } else { - printf("sn <= 10"); + printf("sn <= 10\n"); } } diff --git a/test/Feature/SymbolicSizes/FirstAndLastElements.c b/test/Feature/SymbolicSizes/FirstAndLastElements.c index ae92970c91..da74d0569b 100644 --- a/test/Feature/SymbolicSizes/FirstAndLastElements.c +++ b/test/Feature/SymbolicSizes/FirstAndLastElements.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --out-of-mem-allocs=true --use-merged-pointer-dereference=true %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --check-out-of-memory --use-sym-size-alloc --use-merged-pointer-dereference=true %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include diff --git a/test/Feature/SymbolicSizes/ImplicitArrayExtension.c b/test/Feature/SymbolicSizes/ImplicitArrayExtension.c index 389a1adcab..604d137360 100644 --- a/test/Feature/SymbolicSizes/ImplicitArrayExtension.c +++ b/test/Feature/SymbolicSizes/ImplicitArrayExtension.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --out-of-mem-allocs=true --solver-backend=z3 %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --check-out-of-memory --use-sym-size-alloc --solver-backend=z3 %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include diff --git a/test/Feature/SymbolicSizes/ImplicitSizeConcretization.c b/test/Feature/SymbolicSizes/ImplicitSizeConcretization.c index 1c13f15cd6..0ca567204c 100644 --- a/test/Feature/SymbolicSizes/ImplicitSizeConcretization.c +++ b/test/Feature/SymbolicSizes/ImplicitSizeConcretization.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --out-of-mem-allocs --output-dir=%t.klee-out --use-merged-pointer-dereference=true %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --check-out-of-memory --use-sym-size-alloc --use-merged-pointer-dereference=true %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include diff --git a/test/Feature/SymbolicSizes/IntArray.c b/test/Feature/SymbolicSizes/IntArray.c index c709975480..e820df0a04 100644 --- a/test/Feature/SymbolicSizes/IntArray.c +++ b/test/Feature/SymbolicSizes/IntArray.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --skip-not-lazy-initialized %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --use-sym-size-alloc --use-sym-size-li --min-number-elements-li=1 --skip-not-lazy-initialized %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include diff --git a/test/Feature/SymbolicSizes/LazyInstantiationOfSymbolicSize.c b/test/Feature/SymbolicSizes/LazyInstantiationOfSymbolicSize.c index d027d2adf6..51ad20892e 100644 --- a/test/Feature/SymbolicSizes/LazyInstantiationOfSymbolicSize.c +++ b/test/Feature/SymbolicSizes/LazyInstantiationOfSymbolicSize.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --skip-not-lazy-initialized %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --use-sym-size-alloc --use-sym-size-li --min-number-elements-li=1 --skip-not-lazy-initialized %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include @@ -21,4 +21,4 @@ int main() { } // CHECK: KLEE: done: completed paths = 1 -// CHECK: KLEE: done: generated tests = 5 +// CHECK: KLEE: done: generated tests = 4 diff --git a/test/Feature/SymbolicSizes/LowerOutOfBound.c b/test/Feature/SymbolicSizes/LowerOutOfBound.c index 80634e364f..0a97e5c63a 100644 --- a/test/Feature/SymbolicSizes/LowerOutOfBound.c +++ b/test/Feature/SymbolicSizes/LowerOutOfBound.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --use-sym-size-alloc %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include diff --git a/test/Feature/SymbolicSizes/MinimizeSize.c b/test/Feature/SymbolicSizes/MinimizeSize.c index 7ee93ff88d..5da9f65c0f 100644 --- a/test/Feature/SymbolicSizes/MinimizeSize.c +++ b/test/Feature/SymbolicSizes/MinimizeSize.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --out-of-mem-allocs=true --solver-backend=z3 --use-merged-pointer-dereference=true %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --check-out-of-memory --solver-backend=z3 --use-sym-size-alloc --use-merged-pointer-dereference=true %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include diff --git a/test/Feature/SymbolicSizes/MultipleAllocations.c b/test/Feature/SymbolicSizes/MultipleAllocations.c index 1e5c6a5130..4ef3193a47 100644 --- a/test/Feature/SymbolicSizes/MultipleAllocations.c +++ b/test/Feature/SymbolicSizes/MultipleAllocations.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --out-of-mem-allocs=true --solver-backend=z3 %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --check-out-of-memory --use-sym-size-alloc --solver-backend=z3 %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include @@ -13,6 +13,6 @@ int main() { char *s2 = (char *)malloc(n / 2); // 0, 0 char *s3 = (char *)malloc(n / 3 - 2); // 6, 3, 0 - // CHECK: MultipleAllocations.c:[[@LINE+1]]: memory error: out of bound pointer + // CHECK-NOT: MultipleAllocations.c:[[@LINE+1]]: memory error: out of bound pointer s1[1] = 29; } diff --git a/test/Feature/SymbolicSizes/NegativeIndexArray.c b/test/Feature/SymbolicSizes/NegativeIndexArray.c index ee27bcf30a..2d4e0a6368 100644 --- a/test/Feature/SymbolicSizes/NegativeIndexArray.c +++ b/test/Feature/SymbolicSizes/NegativeIndexArray.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --out-of-mem-allocs --use-merged-pointer-dereference=true %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --check-out-of-memory --use-sym-size-alloc --use-merged-pointer-dereference=true %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include "stdlib.h" diff --git a/test/Feature/SymbolicSizes/NegativeSize.c b/test/Feature/SymbolicSizes/NegativeSize.c index f4e9b3db81..72c0b9c4c7 100644 --- a/test/Feature/SymbolicSizes/NegativeSize.c +++ b/test/Feature/SymbolicSizes/NegativeSize.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --out-of-mem-allocs=true --solver-backend=z3 %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --check-out-of-memory --use-sym-size-alloc --solver-backend=z3 %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include diff --git a/test/Feature/SymbolicSizes/RecomputeModelTwoArrays.c b/test/Feature/SymbolicSizes/RecomputeModelTwoArrays.c index c266e7c398..7d3bab5940 100644 --- a/test/Feature/SymbolicSizes/RecomputeModelTwoArrays.c +++ b/test/Feature/SymbolicSizes/RecomputeModelTwoArrays.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --out-of-mem-allocs=true --solver-backend=z3 --use-merged-pointer-dereference=true %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --check-out-of-memory --use-sym-size-alloc --solver-backend=z3 --use-merged-pointer-dereference=true %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include diff --git a/test/Feature/SymbolicSizes/SegmentComparator.c b/test/Feature/SymbolicSizes/SegmentComparator.c index f2cc574c23..3a7f16db76 100644 --- a/test/Feature/SymbolicSizes/SegmentComparator.c +++ b/test/Feature/SymbolicSizes/SegmentComparator.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --skip-not-lazy-initialized %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --use-sym-size-alloc --skip-not-lazy-initialized %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include diff --git a/test/Feature/SymbolicSizes/SymbolicArrayOnStack.c b/test/Feature/SymbolicSizes/SymbolicArrayOnStack.c index 11b1d9e622..acf086a65f 100644 --- a/test/Feature/SymbolicSizes/SymbolicArrayOnStack.c +++ b/test/Feature/SymbolicSizes/SymbolicArrayOnStack.c @@ -1,18 +1,18 @@ // REQUIRES: z3 // RUN: %clang %s -emit-llvm %O0opt -c -g -o %t.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --out-of-mem-allocs=true --solver-backend=z3 --use-merged-pointer-dereference=true %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --check-out-of-memory --solver-backend=z3 --use-sym-size-alloc --use-merged-pointer-dereference=true %t.bc 2>&1 | FileCheck %s #include "klee/klee.h" int main() { int x = klee_int("x"); int f[x]; - // CHECK-DAG: SymbolicArrayOnStack.c:[[@LINE+2]]: memory error: out of bound pointer - // CHECK-DAG: SymbolicArrayOnStack.c:[[@LINE+1]]: memory error: null pointer exception + // CHECK: SymbolicArrayOnStack.c:[[@LINE+2]]: memory error: out of bound pointer + // CHECK-NOT: SymbolicArrayOnStack.c:[[@LINE+1]]: memory error: null pointer exception f[0] = 10; } // CHECK: KLEE: done: completed paths = 1 -// CHECK: KLEE: done: partially completed paths = 2 -// CHECK: KLEE: done: generated tests = 3 +// CHECK: KLEE: done: partially completed paths = 1 +// CHECK: KLEE: done: generated tests = 2 diff --git a/test/Feature/SymbolicSizes/SymbolicArrayOnStackWithSkipSymbolics.c b/test/Feature/SymbolicSizes/SymbolicArrayOnStackWithSkipSymbolics.c index ebd31bac29..dbb2331aac 100644 --- a/test/Feature/SymbolicSizes/SymbolicArrayOnStackWithSkipSymbolics.c +++ b/test/Feature/SymbolicSizes/SymbolicArrayOnStackWithSkipSymbolics.c @@ -1,18 +1,18 @@ // REQUIRES: z3 // RUN: %clang %s -emit-llvm %O0opt -c -g -o %t.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --out-of-mem-allocs=true --skip-not-symbolic-objects --use-merged-pointer-dereference=true %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --check-out-of-memory --use-sym-size-alloc --skip-not-symbolic-objects --use-merged-pointer-dereference=true %t.bc 2>&1 | FileCheck %s #include "klee/klee.h" int main() { int x = klee_int("x"); int f[x]; - // CHECK-DAG: SymbolicArrayOnStackWithSkipSymbolics.c:[[@LINE+2]]: memory error: out of bound pointer - // CHECK-DAG: SymbolicArrayOnStackWithSkipSymbolics.c:[[@LINE+1]]: memory error: null pointer exception + // CHECK: SymbolicArrayOnStackWithSkipSymbolics.c:[[@LINE+2]]: memory error: out of bound pointer + // CHECK-NOT: SymbolicArrayOnStackWithSkipSymbolics.c:[[@LINE+1]]: memory error: null pointer exception f[0] = 10; } // CHECK: KLEE: done: completed paths = 1 -// CHECK: KLEE: done: partially completed paths = 2 -// CHECK: KLEE: done: generated tests = 3 +// CHECK: KLEE: done: partially completed paths = 1 +// CHECK: KLEE: done: generated tests = 2 diff --git a/test/Feature/SymbolicSizes/VoidStar.c b/test/Feature/SymbolicSizes/VoidStar.c index 9194ad2866..48924f7b69 100644 --- a/test/Feature/SymbolicSizes/VoidStar.c +++ b/test/Feature/SymbolicSizes/VoidStar.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --out-of-mem-allocs=true --skip-not-lazy-initialized --use-merged-pointer-dereference=true %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --check-out-of-memory --use-sym-size-alloc --skip-not-lazy-initialized --use-merged-pointer-dereference=true %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include @@ -19,7 +19,7 @@ void foo(void *s) { ((char *)s)[1] = 'a'; // CHECK: VoidStar.c:[[@LINE+1]]: memory error: out of bound pointer struct Node *node = ((struct Node *)s)->next; - // CHECK: VoidStar.c:[[@LINE+1]]: memory error: out of bound pointer + // CHECK: VoidStar.c:[[@LINE+1]]: memory error: null pointer exception node->x = 20; } @@ -32,5 +32,5 @@ int main() { } // CHECK: KLEE: done: completed paths = 1 -// CHECK: KLEE: done: partially completed paths = 5 -// CHECK: KLEE: done: generated tests = 6 +// CHECK: KLEE: done: partially completed paths = 4 +// CHECK: KLEE: done: generated tests = 5 diff --git a/test/Industry/MemoryLeak/LocalVar_Alloc_in_Loop_Exit_in_Loop_BadCase01.c b/test/Industry/MemoryLeak/LocalVar_Alloc_in_Loop_Exit_in_Loop_BadCase01.c index 405d751b7f..4b77bd1b66 100644 --- a/test/Industry/MemoryLeak/LocalVar_Alloc_in_Loop_Exit_in_Loop_BadCase01.c +++ b/test/Industry/MemoryLeak/LocalVar_Alloc_in_Loop_Exit_in_Loop_BadCase01.c @@ -56,7 +56,7 @@ void call_func(int num) // REQUIRES: z3 // RUN: %clang %s -emit-llvm -c -g -O0 -Xclang -disable-O0-optnone -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --write-kqueries --max-cycles=0 --use-guided-search=error --check-out-of-memory --mock-external-calls --skip-not-symbolic-objects --skip-not-lazy-initialized --use-lazy-initialization=only --analysis-reproduce=%s.json %t1.bc +// RUN: %klee --output-dir=%t.klee-out --write-kqueries --max-cycles-before-stuck=0 --use-guided-search=error --check-out-of-memory --mock-external-calls --skip-not-symbolic-objects --skip-not-lazy-initialized --use-lazy-initialization=only --analysis-reproduce=%s.json %t1.bc // RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s // CHECK: KLEE: WARNING: 100.00% Reachable Reachable at trace 1 diff --git a/test/Industry/NullReturn_Scene_BadCase06.c b/test/Industry/NullReturn_Scene_BadCase06.c index 0710dc14b5..8ad07ac540 100644 --- a/test/Industry/NullReturn_Scene_BadCase06.c +++ b/test/Industry/NullReturn_Scene_BadCase06.c @@ -58,5 +58,5 @@ void TestBad6(unsigned int count) // RUN: %klee --output-dir=%t.klee-out --use-guided-search=error --mock-external-calls --check-out-of-memory --skip-not-symbolic-objects --skip-not-lazy-initialized --use-lazy-initialization=only --analysis-reproduce=%s.json %t1.bc // RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s -// CHECK: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 -// CHECK: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 2 +// CHECK-DAG: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 +// CHECK-DAG: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 2 diff --git a/test/Industry/fp_forward_null_address.c b/test/Industry/fp_forward_null_address.c index 7f6d612f6d..f39297d2a4 100644 --- a/test/Industry/fp_forward_null_address.c +++ b/test/Industry/fp_forward_null_address.c @@ -25,5 +25,5 @@ void foo() // REQUIRES: z3 // RUN: %clang %s -emit-llvm -c -g -O0 -Xclang -disable-O0-optnone -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --max-cycles=150 --use-guided-search=error --mock-external-calls --skip-not-symbolic-objects --skip-not-lazy-initialized --use-lazy-initialization=only --analysis-reproduce=%s.json %t1.bc +// RUN: %klee --output-dir=%t.klee-out --max-cycles-before-stuck=150 --use-guided-search=error --mock-external-calls --skip-not-symbolic-objects --skip-not-lazy-initialized --use-lazy-initialization=only --analysis-reproduce=%s.json %t1.bc // RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/if2.c b/test/Industry/if2.c index 6936bea6ab..30ca2c7d88 100644 --- a/test/Industry/if2.c +++ b/test/Industry/if2.c @@ -11,6 +11,8 @@ int main(int x) { // REQUIRES: z3 // RUN: %clang %s -emit-llvm -c -g -O0 -Xclang -disable-O0-optnone -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=error --mock-external-calls --skip-not-symbolic-objects --skip-not-lazy-initialized --check-out-of-memory --max-stepped-instructions=19 --max-cycles=0 --use-lazy-initialization=only --analysis-reproduce=%s.json %t1.bc +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=error --mock-external-calls --skip-not-symbolic-objects --skip-not-lazy-initialized --check-out-of-memory --search=bfs --max-stepped-instructions=19 --max-cycles-before-stuck=0 --use-lazy-initialization=only --analysis-reproduce=%s.json %t1.bc // RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s -check-prefix=CHECK-NONE -// CHECK-NONE: KLEE: WARNING: 50.00% NullPointerException False Positive at trace 1 \ No newline at end of file +// CHECK-NONE: KLEE: WARNING: 50.00% NullPointerException False Positive at trace 1 +// RUN: FileCheck -input-file=%t.klee-out/messages.txt %s -check-prefix=CHECK-DISTANCE +// CHECK-DISTANCE: KLEE: (0, 1, 1) for Target 1: error in %13 in function main (lines 8 to 8) diff --git a/test/Industry/while_true.c b/test/Industry/while_true.c index 7fa0c973d0..e0adb538e7 100644 --- a/test/Industry/while_true.c +++ b/test/Industry/while_true.c @@ -11,10 +11,15 @@ int main() { // REQUIRES: z3 // RUN: %clang %s -emit-llvm -c -g -O0 -Xclang -disable-O0-optnone -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=error --mock-external-calls --skip-not-symbolic-objects --skip-not-lazy-initialized --check-out-of-memory --max-stepped-instructions=10 --max-cycles=0 --use-lazy-initialization=only --analysis-reproduce=%s.json %t1.bc +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=error --mock-external-calls --skip-not-symbolic-objects --skip-not-lazy-initialized --check-out-of-memory --max-stepped-instructions=10 --max-cycles-before-stuck=0 --use-lazy-initialization=only --analysis-reproduce=%s.json %t1.bc // RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s -check-prefix=CHECK-NONE // CHECK-NONE: KLEE: WARNING: 0.00% NullPointerException False Positive at trace 1 +// RUN: FileCheck -input-file=%t.klee-out/messages.txt %s -check-prefix=CHECK-REACH-1 +// CHECK-REACH-1: (0, 1, 4) for Target 1: error in %17 in function main (lines 8 to 8) + // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=error --mock-external-calls --skip-not-symbolic-objects --skip-not-lazy-initialized --check-out-of-memory --max-stepped-instructions=5000 --max-cycles=0 --use-lazy-initialization=only --analysis-reproduce=%s.json %t1.bc +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=error --mock-external-calls --skip-not-symbolic-objects --skip-not-lazy-initialized --check-out-of-memory --max-stepped-instructions=5000 --max-cycles-before-stuck=0 --use-lazy-initialization=only --analysis-reproduce=%s.json %t1.bc // RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s -check-prefix=CHECK-ALL -// CHECK-ALL: KLEE: WARNING: 99.00% NullPointerException False Positive at trace 1 \ No newline at end of file +// CHECK-ALL: KLEE: WARNING: 99.00% NullPointerException False Positive at trace 1 +// RUN: FileCheck -input-file=%t.klee-out/messages.txt %s -check-prefix=CHECK-REACH-2 +// CHECK-REACH-2: (0, 1, 1) for Target 1: error in %17 in function main (lines 8 to 8) diff --git a/test/Solver/CexCacheValidityCoresCheck.c b/test/Solver/CexCacheValidityCoresCheck.c index 3ad96f999d..82b1faa2f4 100644 --- a/test/Solver/CexCacheValidityCoresCheck.c +++ b/test/Solver/CexCacheValidityCoresCheck.c @@ -2,10 +2,10 @@ // RUN: %clang %s -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t1.klee-out // RUN: rm -rf %t2.klee-out -// RUN: %klee --output-dir=%t1.klee-out -cex-cache-validity-cores=false --solver-backend=z3 %t1.bc -// RUN: %klee --output-dir=%t2.klee-out -cex-cache-validity-cores=true --solver-backend=z3 %t1.bc -// RUN: %klee-stats --print-columns 'QCexCHits' --table-format=csv %t1.klee-out > %t1.stats -// RUN: %klee-stats --print-columns 'QCexCHits' --table-format=csv %t2.klee-out > %t2.stats +// RUN: %klee --output-dir=%t1.klee-out --use-guided-search=none --cex-cache-validity-cores=false --solver-backend=z3 %t1.bc +// RUN: %klee --output-dir=%t2.klee-out --use-guided-search=none --cex-cache-validity-cores=true --solver-backend=z3 %t1.bc +// RUN: %klee-stats --print-columns 'QCexCHits,Queries' --table-format=csv %t1.klee-out > %t1.stats +// RUN: %klee-stats --print-columns 'QCexCHits,Queries' --table-format=csv %t2.klee-out > %t2.stats // RUN: FileCheck -check-prefix=CHECK-CACHE-OFF -input-file=%t1.stats %s // RUN: FileCheck -check-prefix=CHECK-CACHE-ON -input-file=%t2.stats %s #include "klee/klee.h" @@ -29,7 +29,7 @@ int main(int argc, char **argv) { } } } -// CHECK-CACHE-ON: QCexCHits -// CHECK-CACHE-ON: 466 -// CHECK-CACHE-OFF: QCexCHits -// CHECK-CACHE-OFF: 410 +// CHECK-CACHE-ON: QCexCHits,Queries +// CHECK-CACHE-ON: 1461,202 +// CHECK-CACHE-OFF: QCexCHits,Queries +// CHECK-CACHE-OFF: 1011,652 diff --git a/test/regression/2023-02-01-replay-test-with-lazy-initialized-objects.c b/test/regression/2023-02-01-replay-test-with-lazy-initialized-objects.c index 4f789d9aeb..1cac47fe6f 100644 --- a/test/regression/2023-02-01-replay-test-with-lazy-initialized-objects.c +++ b/test/regression/2023-02-01-replay-test-with-lazy-initialized-objects.c @@ -3,10 +3,10 @@ // RUN: rm -rf %t.klee-out // RUN: %klee --output-dir=%t.klee-out --libc=klee --posix-runtime --solver-backend=z3 --skip-not-lazy-initialized --skip-not-symbolic-objects %t.bc > %t.log -// RUN: test -f %t.klee-out/test000008.ktest +// RUN: test -f %t.klee-out/test000006.ktest // RUN: %cc %s %libkleeruntest -Wl,-rpath %libkleeruntestdir -o %t_runner -// RUN: env KTEST_FILE=%t.klee-out/test000008.ktest %t_runner | FileCheck %s +// RUN: env KTEST_FILE=%t.klee-out/test000006.ktest %t_runner | FileCheck %s #include diff --git a/tools/kleaver/main.cpp b/tools/kleaver/main.cpp index 6f43175b82..bceb03dc48 100644 --- a/tools/kleaver/main.cpp +++ b/tools/kleaver/main.cpp @@ -208,7 +208,7 @@ static bool EvaluateInputAST(const char *Filename, const llvm::MemoryBuffer *MB, coreSolver, getQueryLogPath(ALL_QUERIES_SMT2_FILE_NAME), getQueryLogPath(SOLVER_QUERIES_SMT2_FILE_NAME), getQueryLogPath(ALL_QUERIES_KQUERY_FILE_NAME), - getQueryLogPath(SOLVER_QUERIES_KQUERY_FILE_NAME), nullptr, nullptr); + getQueryLogPath(SOLVER_QUERIES_KQUERY_FILE_NAME), nullptr); unsigned Index = 0; for (std::vector::iterator it = Decls.begin(), ie = Decls.end(); diff --git a/utbot-build.sh b/utbot-build.sh new file mode 100755 index 0000000000..12f4182699 --- /dev/null +++ b/utbot-build.sh @@ -0,0 +1,35 @@ +#!/bin/bash +# This script is used to build KLEE as UTBot backend + +set -e +set -o pipefail +mkdir -p build +cd build + +$UTBOT_CMAKE_BINARY -G Ninja \ + -DCMAKE_PREFIX_PATH=$UTBOT_INSTALL_DIR/lib/cmake/z3 \ + -DCMAKE_LIBRARY_PATH=$UTBOT_INSTALL_DIR/lib \ + -DCMAKE_INCLUDE_PATH=$UTBOT_INSTALL_DIR/include \ + -DENABLE_SOLVER_Z3=TRUE \ + -DENABLE_POSIX_RUNTIME=TRUE \ + -DENABLE_KLEE_UCLIBC=ON \ + -DKLEE_UCLIBC_PATH=$UTBOT_ALL/klee-uclibc \ + -DENABLE_FLOATING_POINT=TRUE \ + -DENABLE_FP_RUNTIME=TRUE \ + -DLLVM_CONFIG_BINARY=$UTBOT_INSTALL_DIR/bin/llvm-config \ + -DLLVMCC=/utbot_distr/install/bin/clang \ + -DLLVMCXX=/utbot_distr/install/bin/clang++ \ + -DENABLE_UNIT_TESTS=TRUE \ + -DENABLE_SYSTEM_TESTS=TRUE \ + -DGTEST_SRC_DIR=$UTBOT_ALL/gtest \ + -DGTEST_INCLUDE_DIR=$UTBOT_ALL/gtest/googletest/include \ + -DCMAKE_INSTALL_PREFIX=$UTBOT_ALL/klee \ + -DENABLE_KLEE_LIBCXX=TRUE \ + -DKLEE_LIBCXX_DIR=$UTBOT_ALL/libcxx/install \ + -DKLEE_LIBCXX_INCLUDE_DIR=$UTBOT_ALL/libcxx/install/include/c++/v1 \ + -DENABLE_KLEE_EH_CXX=TRUE \ + -DKLEE_LIBCXXABI_SRC_DIR=$UTBOT_ALL/libcxx/libcxxabi \ + .. + +$UTBOT_CMAKE_BINARY --build . +$UTBOT_CMAKE_BINARY --install .