From fce69b8136cc1e5f037a25a4b33a64dd72c4bfb2 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 27 Jun 2023 10:08:57 +0400 Subject: [PATCH 01/45] [fix] Evaluate only one branch corresponding to the evaluated condition --- include/klee/Expr/ExprEvaluator.h | 15 ++++++++------- lib/Expr/ExprEvaluator.cpp | 10 ++++++++++ 2 files changed, 18 insertions(+), 7 deletions(-) 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/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. From d0aa95166447f2212024cd417d67e607495882a0 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 27 Jun 2023 10:13:57 +0400 Subject: [PATCH 02/45] [fix] Replace multiset with a set --- lib/Core/ExecutionState.cpp | 6 ++++-- lib/Core/ExecutionState.h | 3 ++- lib/Core/Searcher.cpp | 4 ++-- lib/Core/Searcher.h | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index 909d4b64f0..0bf790fcaf 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -386,10 +386,12 @@ void ExecutionState::increaseLevel() { KModule *kmodule = kf->parent; if (prevPC->inst->isTerminator() && kmodule->inMainModule(kf->function)) { - multilevel.insert(srcbb); + multilevel[srcbb]++; 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(); } diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index 8d9db68d2b..685c2c0cbb 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -232,7 +233,7 @@ 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; std::unordered_set level; std::unordered_set transitionLevel; diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp index ab6ef2e1a9..8fb96ad364 100644 --- a/lib/Core/Searcher.cpp +++ b/lib/Core/Searcher.cpp @@ -438,7 +438,7 @@ void TargetedSearcher::removeReached() { GuidedSearcher::GuidedSearcher( CodeGraphDistance &codeGraphDistance, TargetCalculator &stateHistory, std::set &pausedStates, - std::size_t bound, RNG &rng, Searcher *baseSearcher) + unsigned long long bound, RNG &rng, Searcher *baseSearcher) : baseSearcher(baseSearcher), codeGraphDistance(codeGraphDistance), stateHistory(stateHistory), pausedStates(pausedStates), bound(bound), theRNG(rng) { @@ -468,7 +468,7 @@ ExecutionState &GuidedSearcher::selectState() { bool GuidedSearcher::isStuck(ExecutionState &state) { KInstruction *prevKI = state.prevPC; return (prevKI->inst->isTerminator() && - state.multilevel.count(state.getPCBlock()) > bound); + state.multilevel[state.getPCBlock()] > bound); } bool GuidedSearcher::updateTargetedSearcher( diff --git a/lib/Core/Searcher.h b/lib/Core/Searcher.h index f307f46a77..b48ea44431 100644 --- a/lib/Core/Searcher.h +++ b/lib/Core/Searcher.h @@ -221,7 +221,7 @@ class GuidedSearcher final : public Searcher { TargetCalculator &stateHistory; TargetHashSet reachedTargets; std::set &pausedStates; - std::size_t bound; + unsigned long long bound; RNG &theRNG; unsigned index{1}; @@ -257,7 +257,7 @@ class GuidedSearcher final : public Searcher { GuidedSearcher( CodeGraphDistance &codeGraphDistance, TargetCalculator &stateHistory, std::set &pausedStates, - std::size_t bound, RNG &rng, Searcher *baseSearcher = nullptr); + unsigned long long bound, RNG &rng, Searcher *baseSearcher = nullptr); ~GuidedSearcher() override = default; ExecutionState &selectState() override; void update(ExecutionState *current, From e55ed7dd822abbad05942b40c986445e1e231052 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 27 Jun 2023 10:22:23 +0400 Subject: [PATCH 03/45] [fix] Fix tricky independence related bug: `ConcretizingSolver` should consider all the dependent symcretes and get the new concretization correctly --- include/klee/Expr/Symcrete.h | 13 +++++--- lib/Core/Executor.cpp | 5 +-- lib/Solver/ConcretizingSolver.cpp | 51 +++++++++++++++++++++++-------- 3 files changed, 49 insertions(+), 20 deletions(-) diff --git a/include/klee/Expr/Symcrete.h b/include/klee/Expr/Symcrete.h index aacfa44b83..c8ebab70c0 100644 --- a/include/klee/Expr/Symcrete.h +++ b/include/klee/Expr/Symcrete.h @@ -84,7 +84,10 @@ typedef std::set, SymcreteLess> SymcreteOrderedSet; class AddressSymcrete : public Symcrete { public: - AddressSymcrete(ref s, SymcreteKind kind) : Symcrete(s, kind) { + const Array *addressArray; + + AddressSymcrete(const Array *_addressArray, ref s, SymcreteKind kind) + : Symcrete(s, kind), addressArray(_addressArray) { assert((kind == SymcreteKind::SK_ALLOC_ADDRESS || kind == SymcreteKind::SK_LI_ADDRESS) && "wrong kind"); @@ -98,8 +101,8 @@ class AddressSymcrete : public Symcrete { class AllocAddressSymcrete : public AddressSymcrete { public: - AllocAddressSymcrete(ref s) - : AddressSymcrete(s, SymcreteKind::SK_ALLOC_ADDRESS) {} + AllocAddressSymcrete(const Array *addressArray, ref s) + : AddressSymcrete(addressArray, s, SymcreteKind::SK_ALLOC_ADDRESS) {} static bool classof(const Symcrete *symcrete) { return symcrete->getKind() == SymcreteKind::SK_ALLOC_ADDRESS; @@ -108,8 +111,8 @@ class AllocAddressSymcrete : public AddressSymcrete { class LazyInitializedAddressSymcrete : public AddressSymcrete { public: - LazyInitializedAddressSymcrete(ref s) - : AddressSymcrete(s, SymcreteKind::SK_LI_ADDRESS) {} + LazyInitializedAddressSymcrete(const Array *addressArray, ref s) + : AddressSymcrete(addressArray, s, SymcreteKind::SK_LI_ADDRESS) {} static bool classof(const Symcrete *symcrete) { return symcrete->getKind() == SymcreteKind::SK_LI_ADDRESS; diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index e42a4a5bb8..1d757d2693 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -5038,8 +5038,9 @@ MemoryObject *Executor::allocate(ExecutionState &state, ref size, ref addressSymcrete = lazyInitializationSource ? cast( - new LazyInitializedAddressSymcrete(addressExpr)) - : cast(new AllocAddressSymcrete(addressExpr)); + new LazyInitializedAddressSymcrete(addressArray, addressExpr)) + : cast( + new AllocAddressSymcrete(addressArray, addressExpr)); ref sizeSymcrete = lazyInitializationSource ? cast(new LazyInitializedSizeSymcrete( diff --git a/lib/Solver/ConcretizingSolver.cpp b/lib/Solver/ConcretizingSolver.cpp index 8d1886ae57..b7f2b93404 100644 --- a/lib/Solver/ConcretizingSolver.cpp +++ b/lib/Solver/ConcretizingSolver.cpp @@ -196,6 +196,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 +242,6 @@ bool ConcretizingSolver::relaxSymcreteConstraints(const Query &query, Simplificator::simplifyExpr(query.constraints, symbolicSizesSum) .simplified; - ref minimalValueOfSum; ref response; /* Compute assignment for symcretes. */ @@ -249,6 +260,7 @@ bool ConcretizingSolver::relaxSymcreteConstraints(const Query &query, } 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 +283,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 +299,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 +310,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()); + } + + 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(constructConcretizedQuery(query, assignment), - result)) { + if (!solver->impl->check(query.withExpr(concretizationCondition), result)) { return false; } From ee47874c6c96c1d268261550f7bc29c44b4b9f9e Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 27 Jun 2023 10:24:25 +0400 Subject: [PATCH 04/45] [fix] Symbolic size needs to be evaluated --- lib/Expr/Assignment.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Expr/Assignment.cpp b/lib/Expr/Assignment.cpp index b176b8ed7b..f4db64f42f 100644 --- a/lib/Expr/Assignment.cpp +++ b/lib/Expr/Assignment.cpp @@ -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(); From 43caa963facfbbdf6c10cb19734b45f017d106b7 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 27 Jun 2023 10:25:48 +0400 Subject: [PATCH 05/45] [chore] A few minor fixes --- lib/Core/MemoryManager.cpp | 2 ++ lib/Expr/Assignment.cpp | 2 +- lib/Expr/ExprPPrinter.cpp | 1 + lib/Solver/Solver.cpp | 7 +------ 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/Core/MemoryManager.cpp b/lib/Core/MemoryManager.cpp index 309dff8601..6d601ce3e2 100644 --- a/lib/Core/MemoryManager.cpp +++ b/lib/Core/MemoryManager.cpp @@ -117,6 +117,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/Expr/Assignment.cpp b/lib/Expr/Assignment.cpp index f4db64f42f..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"; diff --git a/lib/Expr/ExprPPrinter.cpp b/lib/Expr/ExprPPrinter.cpp index d5d3c4cf96..bf222af763 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); } 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"; From 5e1872be98c400de69a7bbb14ee91b74a0668f43 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 27 Jun 2023 10:28:53 +0400 Subject: [PATCH 06/45] [fix] Separate code related to error guided mode --- lib/Core/Searcher.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp index 8fb96ad364..0e5ce3e22a 100644 --- a/lib/Core/Searcher.cpp +++ b/lib/Core/Searcher.cpp @@ -622,15 +622,17 @@ void GuidedSearcher::innerUpdate( targetlessStates.push_back(current); } - 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()); + if (ErrorGuidance == guidance) { + if (!removedStates.empty()) { + std::vector alt = removedStates; + 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()); + } } } } From 75ec56f42e74fa21d56b35da3c5ab7f73c0052ec Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 27 Jun 2023 10:30:53 +0400 Subject: [PATCH 07/45] [feat] Improve `SelectExpr` simplification [feat] Consider full symbolic equalities during simplification --- lib/Expr/Constraints.cpp | 60 ++++++++++++---------------------------- lib/Expr/Expr.cpp | 45 ++++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 48 deletions(-) diff --git a/lib/Expr/Constraints.cpp b/lib/Expr/Constraints.cpp index 4ea34c677a..88f0e137a8 100644 --- a/lib/Expr/Constraints.cpp +++ b/lib/Expr/Constraints.cpp @@ -149,40 +149,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); @@ -427,18 +396,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 +501,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 +518,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..5b8bed1008 100644 --- a/lib/Expr/Expr.cpp +++ b/lib/Expr/Expr.cpp @@ -1579,6 +1579,32 @@ 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); + } + } } return SelectExpr::alloc(c, t, f); @@ -1813,6 +1839,10 @@ static ref AddExpr_createPartialR(const ref &cl, Expr *r) { } else if (rk == Expr::Sub && isa(r->getKid(0))) { // A + (B-c) == (A+B) - c return SubExpr::create(AddExpr::create(cl, r->getKid(0)), r->getKid(1)); + } else if (rk == Expr::Select) { + SelectExpr *se = cast(r); + return SelectExpr::create(se->cond, AddExpr::create(cl, se->trueExpr), + AddExpr::create(cl, se->falseExpr)); } else { return AddExpr::alloc(cl, r); } @@ -2118,7 +2148,6 @@ 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), @@ -2128,10 +2157,9 @@ static ref EqExpr_create(const ref &l, const ref &r) { return SelectExpr::create(se->cond, EqExpr::create(se->trueExpr, l), EqExpr::create(se->falseExpr, l)); } - 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 +2263,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)); } @@ -2276,9 +2307,11 @@ 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 { - return UleExpr::alloc(l, r); + } else if (r->isZero()) { + return EqExpr::create(l, r); } + + return UleExpr::alloc(l, r); } static ref SltExpr_create(const ref &l, const ref &r) { From e5254074a3c047f85bc83bc046726861f6ebc693 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 27 Jun 2023 10:34:49 +0400 Subject: [PATCH 08/45] [fix] Rework resolution related functions [feat] Remove `OutOfMemAllocs` option as unnecessary, `CheckOutOfMemory` option replaces it --- lib/Core/ExecutionState.cpp | 18 + lib/Core/ExecutionState.h | 3 + lib/Core/Executor.cpp | 324 ++++++++++++------ lib/Core/Executor.h | 2 +- lib/Module/SarifReport.cpp | 2 + .../SymbolicSizes/FirstAndLastElements.c | 2 +- .../SymbolicSizes/ImplicitArrayExtension.c | 2 +- .../ImplicitSizeConcretization.c | 2 +- test/Feature/SymbolicSizes/MinimizeSize.c | 2 +- .../SymbolicSizes/MultipleAllocations.c | 4 +- .../SymbolicSizes/NegativeIndexArray.c | 2 +- test/Feature/SymbolicSizes/NegativeSize.c | 2 +- .../SymbolicSizes/RecomputeModelTwoArrays.c | 2 +- .../SymbolicSizes/SymbolicArrayOnStack.c | 10 +- .../SymbolicArrayOnStackWithSkipSymbolics.c | 10 +- test/Feature/SymbolicSizes/VoidStar.c | 2 +- test/Solver/CexCacheValidityCoresCheck.c | 16 +- 17 files changed, 269 insertions(+), 136 deletions(-) diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index 0bf790fcaf..38012e861e 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -277,6 +277,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 +295,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(); diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index 685c2c0cbb..bcd5777c56 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -362,8 +362,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); diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 1d757d2693..943bf7485e 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -162,12 +162,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 " @@ -408,7 +402,7 @@ bool allLeafsAreConstant(const ref &expr) { ArrayExprHelper::collectAlternatives(*sel, alternatives); for (auto leaf : alternatives) { - if (!isa(expr)) { + if (!isa(leaf)) { return false; } } @@ -1271,6 +1265,8 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, } 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"); @@ -4800,10 +4796,12 @@ 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)); + 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 +4817,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 +4841,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 +4854,7 @@ void Executor::executeAlloc(ExecutionState &state, ref size, bool isLocal, } } } else { - bindLocal(target, state, Expr::createPointer(0)); + terminateStateEarly(state, "", StateTerminationType::SilentExit); } } @@ -4911,31 +4900,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 = + fork(estate, Expr::createIsZero(base), true, 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); 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 +4984,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; @@ -5150,15 +5182,38 @@ 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; + bool baseWasNotResolved = state.resolvedPointers.count(base) == 0; address = optimizer.optimizeExpr(address, true); ref checkOutOfBounds = Expr::createTrue(); - if (!onlyLazyInitialize) { + 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) && + baseWasNotResolved && checkAddress; + + if (!onlyLazyInitialize || !mayLazyInitialize) { ResolutionList rl; ResolutionList rlSkipped; @@ -5174,31 +5229,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, @@ -5217,8 +5251,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; } @@ -5231,7 +5263,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) { @@ -5246,45 +5278,124 @@ 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)) { baseInBounds = AndExpr::create(baseInBounds, mo->getBoundsCheckPointer(base, size)); + } + + if (hasLazyInitialized) { 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); + checkOutOfBounds = notInBounds; + } + } else if (mayBeOutOfBound) { + if (mustBeOutOfBound) { + checkOutOfBounds = Expr::createTrue(); + } else { + 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 (state.isGEPExpr(address)) { + baseInBounds = AndExpr::create(baseInBounds, + mo->getBoundsCheckPointer(base, size)); + } + + if (hasLazyInitialized && i == mayBeResolvedMemoryObjects.size() - 1) { + 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 (mayBeOutOfBound) { + checkOutOfBounds = AndExpr::create(checkOutOfBounds, notInBounds); + } } } @@ -5297,9 +5408,11 @@ bool Executor::checkResolvedMemoryObjects( return false; } } + if (!mayBeOutOfBound) { checkOutOfBounds = Expr::createFalse(); } + return true; } @@ -5466,13 +5579,13 @@ 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(); - } else { + idFastResult = *state->resolvedPointers[base].begin(); + } else if (allLeafsAreConstant(address)) { solver->setTimeout(coreSolverTimeout); if (!state->addressSpace.resolveOne(*state, solver, address, targetType, @@ -5495,13 +5608,6 @@ void Executor::executeMemoryOperation( ref inBounds = mo->getBoundsCheckPointer(address, bytes); - if (state->isGEPExpr(address)) { - inBounds = - AndExpr::create(inBounds, mo->getBoundsCheckPointer(base, size)); - inBounds = AndExpr::create(inBounds, - Expr::createIsZero(mo->getOffsetExpr(base))); - } - inBounds = optimizer.optimizeExpr(inBounds, true); inBounds = Simplificator::simplifyExpr(state->constraints.cs(), inBounds) .simplified; @@ -5595,8 +5701,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,25 +5754,26 @@ void Executor::executeMemoryOperation( ObjectState *wos = state->addressSpace.getWriteable(mo, os); if (wos->readOnly) { - branches = - fork(*state, resolveConditions[i], true, BranchType::MemOp); + branches = fork(*state, Expr::createIsZero(unboundConditions[i]), + true, 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); } @@ -5693,8 +5800,8 @@ void Executor::executeMemoryOperation( const MemoryObject *mo = op.first; const ObjectState *os = op.second; - bound->addPointerResolution(address, mo); - bound->addPointerResolution(base, mo); + 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 @@ -5850,6 +5957,9 @@ 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; } diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index add4ff1b68..de3497087c 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -360,7 +360,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); diff --git a/lib/Module/SarifReport.cpp b/lib/Module/SarifReport.cpp index a185a896ef..e0880646b9 100644 --- a/lib/Module/SarifReport.cpp +++ b/lib/Module/SarifReport.cpp @@ -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 {}; } diff --git a/test/Feature/SymbolicSizes/FirstAndLastElements.c b/test/Feature/SymbolicSizes/FirstAndLastElements.c index ae92970c91..800af271e4 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-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..56ff1843e5 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 --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..974623d058 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 --check-out-of-memory --output-dir=%t.klee-out --use-merged-pointer-dereference=true %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..a67bca1cb6 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-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..69d9633abf 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 --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..37c413bd51 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-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..ea8ab6d955 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 --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..83c52cab06 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 --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/SymbolicArrayOnStack.c b/test/Feature/SymbolicSizes/SymbolicArrayOnStack.c index 11b1d9e622..26a0e18fff 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-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..af5a4df6dd 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 --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..cd64f3d10a 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 --skip-not-lazy-initialized --use-merged-pointer-dereference=true %t1.bc 2>&1 | FileCheck %s #include "klee/klee.h" #include diff --git a/test/Solver/CexCacheValidityCoresCheck.c b/test/Solver/CexCacheValidityCoresCheck.c index 3ad96f999d..a0d68efe75 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 --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,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: 277,124 +// CHECK-CACHE-OFF: QCexCHits,Queries +// CHECK-CACHE-OFF: 226,175 From 1c67fa30fbfb54d06b61f6744061a88e33ca1ab8 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Thu, 22 Jun 2023 13:44:03 +0400 Subject: [PATCH 09/45] [fix] Collect full assigment for logging `computeInitialValues` --- lib/Solver/QueryLoggingSolver.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) 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!"); From 8e88097896869473ba32337e8cf0ba521aa68853 Mon Sep 17 00:00:00 2001 From: Victor Samoilov Date: Mon, 22 May 2023 16:10:23 +0300 Subject: [PATCH 10/45] Factor out distance calculations for searchers in DistanceCalculator --- include/klee/Module/TargetForest.h | 1 + lib/Core/CMakeLists.txt | 1 + lib/Core/DistanceCalculator.cpp | 210 +++++++++++++++++++++++++++++ lib/Core/DistanceCalculator.h | 83 ++++++++++++ lib/Core/ExecutionState.h | 2 + lib/Core/Executor.cpp | 6 +- lib/Core/Executor.h | 2 + lib/Core/Searcher.cpp | 180 ++----------------------- lib/Core/Searcher.h | 28 +--- lib/Core/UserSearcher.cpp | 2 +- lib/Module/TargetForest.cpp | 10 ++ 11 files changed, 334 insertions(+), 191 deletions(-) create mode 100644 lib/Core/DistanceCalculator.cpp create mode 100644 lib/Core/DistanceCalculator.h diff --git a/include/klee/Module/TargetForest.h b/include/klee/Module/TargetForest.h index 91cd347309..eac93e8ef4 100644 --- a/include/klee/Module/TargetForest.h +++ b/include/klee/Module/TargetForest.h @@ -298,6 +298,7 @@ class TargetForest { void add(ref); void remove(ref); void blockIn(ref, ref); + void block(const ref &); const ref getHistory() { return history; }; const ref getTargets() { return forest; }; void dump() const; diff --git a/lib/Core/CMakeLists.txt b/lib/Core/CMakeLists.txt index 202fa2fcec..b064c749f3 100644 --- a/lib/Core/CMakeLists.txt +++ b/lib/Core/CMakeLists.txt @@ -13,6 +13,7 @@ klee_add_component(kleeCore Context.cpp CoreStats.cpp CXXTypeSystem/CXXTypeManager.cpp + DistanceCalculator.cpp ExecutionState.cpp Executor.cpp ExecutorUtil.cpp diff --git a/lib/Core/DistanceCalculator.cpp b/lib/Core/DistanceCalculator.cpp new file mode 100644 index 0000000000..b87fbcf18a --- /dev/null +++ b/lib/Core/DistanceCalculator.cpp @@ -0,0 +1,210 @@ +//===-- 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" + +using namespace llvm; +using namespace klee; + +DistanceResult DistanceCalculator::getDistance(ExecutionState &es, + ref target) { + return getDistance(es.pc, es.prevPC, es.initPC, es.stack, es.error, target); +} + +DistanceResult +DistanceCalculator::getDistance(KInstruction *pc, KInstruction *prevPC, + KInstruction *initPC, + const ExecutionState::stack_ty &stack, + ReachWithError error, ref target) { + weight_type weight = 0; + + BasicBlock *pcBlock = pc->inst->getParent(); + BasicBlock *prevPCBlock = prevPC->inst->getParent(); + + 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); + } + + BasicBlock *bb = pcBlock; + KBlock *kb = pc->parent->parent->blockMap[bb]; + const auto &distanceToTargetFunction = + codeGraphDistance.getBackwardDistance(target->getBlock()->parent); + unsigned int minCallWeight = UINT_MAX, minSfNum = UINT_MAX, sfNum = 0; + for (auto sfi = stack.rbegin(), sfe = stack.rend(); sfi != sfe; sfi++) { + unsigned callWeight; + if (distanceInCallGraph(sfi->kf, kb, callWeight, distanceToTargetFunction, + target)) { + callWeight *= 2; + if (callWeight == 0 && target->shouldFailOnThisTarget()) { + return target->isTheSameAsIn(kb->getFirstInstruction()) && + target->isThatError(error) + ? DistanceResult(Done) + : DistanceResult(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; + } + + WeightResult res = Miss; + bool isInsideFunction = true; + if (minCallWeight == 0) { + res = tryGetTargetWeight(pc, initPC, pcBlock, prevPCBlock, weight, target); + } else if (minSfNum == 0) { + res = tryGetPreTargetWeight(pc, initPC, pcBlock, prevPCBlock, weight, + distanceToTargetFunction, target); + isInsideFunction = false; + } else if (minSfNum != UINT_MAX) { + res = tryGetPostTargetWeight(pc, initPC, pcBlock, prevPCBlock, weight, + target); + isInsideFunction = false; + } + if (Done == res && target->shouldFailOnThisTarget()) { + if (!target->isThatError(error)) { + res = Continue; + } + } + + return DistanceResult(res, weight, isInsideFunction); +} + +bool DistanceCalculator::distanceInCallGraph( + KFunction *kf, KBlock *kb, unsigned int &distance, + const std::unordered_map + &distanceToTargetFunction, + ref target) { + 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( + KInstruction *pc, KInstruction *initPC, BasicBlock *pcBlock, + BasicBlock *prevPCBlock, weight_type &weight, + const std::vector &localTargets, ref target) { + KFunction *currentKF = pc->parent->parent; + KBlock *initKB = initPC->parent; + KBlock *currentKB = currentKF->blockMap[pcBlock]; + KBlock *prevKB = currentKF->blockMap[prevPCBlock]; + 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 && (initKB == currentKB || prevKB != currentKB || + target->shouldFailOnThisTarget())) { + return Done; + } + + return Continue; +} + +WeightResult DistanceCalculator::tryGetPreTargetWeight( + KInstruction *pc, KInstruction *initPC, BasicBlock *pcBlock, + BasicBlock *prevPCBlock, weight_type &weight, + const std::unordered_map + &distanceToTargetFunction, + ref target) { + KFunction *currentKF = 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; + + WeightResult res = tryGetLocalWeight(pc, initPC, pcBlock, prevPCBlock, weight, + localTargets, target); + return res == Done ? Continue : res; +} + +WeightResult DistanceCalculator::tryGetPostTargetWeight( + KInstruction *pc, KInstruction *initPC, BasicBlock *pcBlock, + BasicBlock *prevPCBlock, weight_type &weight, ref target) { + KFunction *currentKF = pc->parent->parent; + std::vector &localTargets = currentKF->returnKBlocks; + + if (localTargets.empty()) + return Miss; + + WeightResult res = tryGetLocalWeight(pc, initPC, pcBlock, prevPCBlock, weight, + localTargets, target); + return res == Done ? Continue : res; +} + +WeightResult DistanceCalculator::tryGetTargetWeight( + KInstruction *pc, KInstruction *initPC, BasicBlock *pcBlock, + BasicBlock *prevPCBlock, weight_type &weight, ref target) { + std::vector localTargets = {target->getBlock()}; + WeightResult res = tryGetLocalWeight(pc, initPC, pcBlock, prevPCBlock, weight, + localTargets, target); + return res; +} diff --git a/lib/Core/DistanceCalculator.h b/lib/Core/DistanceCalculator.h new file mode 100644 index 0000000000..8f46a3696c --- /dev/null +++ b/lib/Core/DistanceCalculator.h @@ -0,0 +1,83 @@ +//===-- 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 { + Continue, + Done, + Miss, +}; + +using weight_type = unsigned; + +struct DistanceResult { + WeightResult result; + weight_type weight; + bool isInsideFunction; + + explicit DistanceResult(WeightResult result_, weight_type weight_ = 0, + bool isInsideFunction_ = true) + : result(result_), weight(weight_), isInsideFunction(isInsideFunction_) {} +}; + +class DistanceCalculator { +public: + explicit DistanceCalculator(CodeGraphDistance &codeGraphDistance_) + : codeGraphDistance(codeGraphDistance_) {} + + DistanceResult getDistance(ExecutionState &es, ref target); + DistanceResult getDistance(KInstruction *pc, KInstruction *prevPC, + KInstruction *initPC, + const ExecutionState::stack_ty &stack, + ReachWithError error, ref target); + +private: + CodeGraphDistance &codeGraphDistance; + + bool distanceInCallGraph(KFunction *kf, KBlock *kb, unsigned int &distance, + const std::unordered_map + &distanceToTargetFunction, + ref target); + WeightResult tryGetLocalWeight(KInstruction *pc, KInstruction *initPC, + llvm::BasicBlock *pcBlock, + llvm::BasicBlock *prevPCBlock, + weight_type &weight, + const std::vector &localTargets, + ref target); + WeightResult + tryGetPreTargetWeight(KInstruction *pc, KInstruction *initPC, + llvm::BasicBlock *pcBlock, + llvm::BasicBlock *prevPCBlock, weight_type &weight, + const std::unordered_map + &distanceToTargetFunction, + ref target); + WeightResult tryGetTargetWeight(KInstruction *pc, KInstruction *initPC, + llvm::BasicBlock *pcBlock, + llvm::BasicBlock *prevPCBlock, + weight_type &weight, ref target); + WeightResult tryGetPostTargetWeight(KInstruction *pc, KInstruction *initPC, + llvm::BasicBlock *pcBlock, + llvm::BasicBlock *prevPCBlock, + weight_type &weight, ref target); +}; +} // namespace klee + +#endif /* KLEE_DISTANCE_CALCULATOR_H */ diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index bcd5777c56..d15170bbd5 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -204,6 +204,8 @@ class ExecutionState { public: using stack_ty = std::vector; + using TargetHashSet = + std::unordered_set, RefTargetHash, RefTargetCmp>; // Execution - Control Flow specific diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 943bf7485e..610d853911 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -13,6 +13,7 @@ #include "CXXTypeSystem/CXXTypeManager.h" #include "Context.h" #include "CoreStats.h" +#include "DistanceCalculator.h" #include "ExecutionState.h" #include "ExternalDispatcher.h" #include "GetElementPtrTypeIterator.h" @@ -440,6 +441,7 @@ Executor::Executor(LLVMContext &ctx, const InterpreterOptions &opts, specialFunctionHandler(0), timers{time::Span(TimerInterval)}, concretizationManager(new ConcretizationManager(EqualitySubstitution)), codeGraphDistance(new CodeGraphDistance()), + distanceCalculator(new DistanceCalculator(*codeGraphDistance)), targetedExecutionManager(*codeGraphDistance), replayKTest(0), replayPath(0), usingSeeds(0), atMemoryLimit(false), inhibitForking(false), haltExecution(HaltExecution::NotHalt), ivcEnabled(false), @@ -4178,7 +4180,7 @@ void Executor::targetedRun(ExecutionState &initialState, KBlock *target, states.insert(&initialState); TargetedSearcher *targetedSearcher = - new TargetedSearcher(Target::create(target), *codeGraphDistance); + new TargetedSearcher(Target::create(target), *distanceCalculator); searcher = targetedSearcher; std::vector newStates(states.begin(), states.end()); @@ -4518,6 +4520,8 @@ void Executor::terminateStateOnExecError(ExecutionState &state, void Executor::terminateStateOnSolverError(ExecutionState &state, const llvm::Twine &message) { terminateStateOnError(state, message, StateTerminationType::Solver, ""); + SetOfStates states = {&state}; + decreaseConfidenceFromStoppedStates(states, HaltExecution::MaxSolverTime); } // XXX shoot me diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index de3497087c..8aa39b241a 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -70,6 +70,7 @@ class AddressManager; class Array; struct Cell; class CodeGraphDistance; +class DistanceCalculator; class ExecutionState; class ExternalDispatcher; class Expr; @@ -142,6 +143,7 @@ class Executor : public Interpreter { 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 diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp index 0e5ce3e22a..91e9f67e0c 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" @@ -150,133 +149,15 @@ static unsigned int ulog2(unsigned int val) { /// TargetedSearcher::TargetedSearcher(ref target, - CodeGraphDistance &_distance) + DistanceCalculator &_distanceCalculator) : states(std::make_unique< WeightedQueue>()), - target(target), codeGraphDistance(_distance), - distanceToTargetFunction( - codeGraphDistance.getBackwardDistance(target->getBlock()->parent)) {} + target(target), distanceCalculator(_distanceCalculator) {} 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; - - 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; -} - -TargetedSearcher::WeightResult -TargetedSearcher::tryGetTargetWeight(ExecutionState *es, weight_type &weight) { - std::vector localTargets = {target->getBlock()}; - WeightResult res = tryGetLocalWeight(es, weight, localTargets); - return res; -} - -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; - } - +WeightResult TargetedSearcher::tryGetWeight(ExecutionState *es, + weight_type &weight) { BasicBlock *bb = es->getPCBlock(); KBlock *kb = es->pc->parent->parent->blockMap[bb]; KInstruction *ki = es->pc; @@ -285,51 +166,14 @@ TargetedSearcher::tryGetWeight(ExecutionState *es, weight_type &weight) { 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; + auto distRes = distanceCalculator.getDistance(*es, target); + weight = ulog2(distRes.weight + es->steppedMemoryInstructions); // [0, 32] + if (!distRes.isInsideFunction) { + weight += 32; // [32, 64] } - 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; + return distRes.result; } void TargetedSearcher::update( @@ -436,10 +280,10 @@ void TargetedSearcher::removeReached() { /// GuidedSearcher::GuidedSearcher( - CodeGraphDistance &codeGraphDistance, TargetCalculator &stateHistory, + DistanceCalculator &distanceCalculator, TargetCalculator &stateHistory, std::set &pausedStates, unsigned long long bound, RNG &rng, Searcher *baseSearcher) - : baseSearcher(baseSearcher), codeGraphDistance(codeGraphDistance), + : baseSearcher(baseSearcher), distanceCalculator(distanceCalculator), stateHistory(stateHistory), pausedStates(pausedStates), bound(bound), theRNG(rng) { guidance = baseSearcher ? CoverageGuidance : ErrorGuidance; @@ -835,7 +679,7 @@ bool GuidedSearcher::tryAddTarget(ref history, assert(targetedSearchers.count(history) == 0 || targetedSearchers.at(history).count(target) == 0); targetedSearchers[history][target] = - std::make_unique(target, codeGraphDistance); + std::make_unique(target, distanceCalculator); auto it = std::find_if( historiesAndTargets.begin(), historiesAndTargets.end(), [&history, &target]( diff --git a/lib/Core/Searcher.h b/lib/Core/Searcher.h index b48ea44431..436e8dcdd4 100644 --- a/lib/Core/Searcher.h +++ b/lib/Core/Searcher.h @@ -10,6 +10,7 @@ #ifndef KLEE_SEARCHER_H #define KLEE_SEARCHER_H +#include "DistanceCalculator.h" #include "ExecutionState.h" #include "PForest.h" #include "PTree.h" @@ -34,13 +35,14 @@ 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; +class TargetReachability; /// A Searcher implements an exploration strategy for the Executor by selecting /// states for further exploration using different strategies or heuristics. @@ -129,33 +131,17 @@ class RandomSearcher final : public Searcher { /// 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; + DistanceCalculator &distanceCalculator; 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); public: - TargetedSearcher(ref target, CodeGraphDistance &distance); + TargetedSearcher(ref target, DistanceCalculator &distanceCalculator); ~TargetedSearcher() override; ExecutionState &selectState() override; @@ -217,7 +203,7 @@ class GuidedSearcher final : public Searcher { Guidance guidance; std::unique_ptr baseSearcher; TargetForestHistoryToSearcherMap targetedSearchers; - CodeGraphDistance &codeGraphDistance; + DistanceCalculator &distanceCalculator; TargetCalculator &stateHistory; TargetHashSet reachedTargets; std::set &pausedStates; @@ -255,7 +241,7 @@ class GuidedSearcher final : public Searcher { public: GuidedSearcher( - CodeGraphDistance &codeGraphDistance, TargetCalculator &stateHistory, + DistanceCalculator &distanceCalculator, TargetCalculator &stateHistory, std::set &pausedStates, unsigned long long bound, RNG &rng, Searcher *baseSearcher = nullptr); ~GuidedSearcher() override = default; diff --git a/lib/Core/UserSearcher.cpp b/lib/Core/UserSearcher.cpp index c2e9f0bb63..72800087b9 100644 --- a/lib/Core/UserSearcher.cpp +++ b/lib/Core/UserSearcher.cpp @@ -183,7 +183,7 @@ Searcher *klee::constructUserSearcher(Executor &executor, searcher = nullptr; } searcher = new GuidedSearcher( - *executor.codeGraphDistance.get(), *executor.targetCalculator, + *executor.distanceCalculator, *executor.targetCalculator, executor.pausedStates, MaxCycles - 1, executor.theRNG, searcher); } diff --git a/lib/Module/TargetForest.cpp b/lib/Module/TargetForest.cpp index d3c80efd30..bc54d1949e 100644 --- a/lib/Module/TargetForest.cpp +++ b/lib/Module/TargetForest.cpp @@ -15,6 +15,8 @@ #include "klee/Module/KModule.h" #include "klee/Module/TargetHash.h" +#include "klee/Support/ErrorHandling.h" + using namespace klee; using namespace llvm; @@ -510,6 +512,14 @@ 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"; history->dump(); From 6ac696c62cb4f162700f38718d3bed33b80c9e27 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 27 Jun 2023 10:40:23 +0400 Subject: [PATCH 11/45] [chore] Add an easy to use build script --- build.sh | 86 ++++++++++++++++++++++++++++++-------------------- utbot-build.sh | 35 ++++++++++++++++++++ 2 files changed, 86 insertions(+), 35 deletions(-) create mode 100755 utbot-build.sh 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/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 . From 0d93dc602b6b75955caf2c83f99d0e80a0988bcf Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 27 Jun 2023 11:07:06 +0400 Subject: [PATCH 12/45] [chore] Add `options.json` file --- configs/options.json | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 configs/options.json diff --git a/configs/options.json b/configs/options.json new file mode 100644 index 0000000000..736bb70193 --- /dev/null +++ b/configs/options.json @@ -0,0 +1,45 @@ +{ + "inputs" : { + "buildPath" : "", + "sarifTracesFilePath" : ".json", + "bytecodeFilePath" : ".bc", + "maxTime" : "240", + "maxSolverTime" : "5", + "maxForks" : "200", + "maxSymAlloc" : "32", + "symbolicAllocationThreshhold" : "2048" + }, + "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", + "--use-lazy-initialization=only", + "--rewrite-equalities=simple", + "--symbolic-allocation-threshhold=${symbolicAllocationThreshhold}", + "--analysis-reproduce=${sarifTracesFilePath}", + "${bytecodeFilePath}" + ] + } + ] +} \ No newline at end of file From e40996fc2c6875dd86365d55073c8b7b5d4f2517 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Thu, 8 Jun 2023 17:13:11 +0400 Subject: [PATCH 13/45] [feat] Add Cooddy report parser --- lib/Module/SarifReport.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/Module/SarifReport.cpp b/lib/Module/SarifReport.cpp index e0880646b9..219866d601 100644 --- a/lib/Module/SarifReport.cpp +++ b/lib/Module/SarifReport.cpp @@ -98,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 {}; } From ad9c928ac8094f9e11ab57f9e4dd0e2cfdba828d Mon Sep 17 00:00:00 2001 From: Yurii Kostyukov Date: Thu, 22 Jun 2023 16:04:39 +0300 Subject: [PATCH 14/45] [feat] Target progress report --- include/klee/Module/TargetHash.h | 8 ++++++++ lib/Core/DistanceCalculator.cpp | 17 ++++++++++++++++ lib/Core/DistanceCalculator.h | 15 ++++++++++----- lib/Core/Executor.cpp | 33 ++++++++++++++++++++++++++++++++ lib/Core/Executor.h | 4 ++++ lib/Core/Searcher.h | 8 -------- lib/Module/KModule.cpp | 2 ++ 7 files changed, 74 insertions(+), 13 deletions(-) diff --git a/include/klee/Module/TargetHash.h b/include/klee/Module/TargetHash.h index eccac7db77..a484b521a7 100644 --- a/include/klee/Module/TargetHash.h +++ b/include/klee/Module/TargetHash.h @@ -14,6 +14,7 @@ #include #include +#include namespace llvm { class BasicBlock; @@ -54,5 +55,12 @@ struct RefTargetLess { } }; +template +class TargetHashMap + : public std::unordered_map, T, RefTargetHash, RefTargetCmp> {}; + +class TargetHashSet + : public std::unordered_set, RefTargetHash, RefTargetCmp> {}; + } // namespace klee #endif /* KLEE_TARGETHASH_H */ diff --git a/lib/Core/DistanceCalculator.cpp b/lib/Core/DistanceCalculator.cpp index b87fbcf18a..a1e384ee67 100644 --- a/lib/Core/DistanceCalculator.cpp +++ b/lib/Core/DistanceCalculator.cpp @@ -13,9 +13,26 @@ #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(); +} + DistanceResult DistanceCalculator::getDistance(ExecutionState &es, ref target) { return getDistance(es.pc, es.prevPC, es.initPC, es.stack, es.error, target); diff --git a/lib/Core/DistanceCalculator.h b/lib/Core/DistanceCalculator.h index 8f46a3696c..eec14f8881 100644 --- a/lib/Core/DistanceCalculator.h +++ b/lib/Core/DistanceCalculator.h @@ -21,9 +21,9 @@ class CodeGraphDistance; struct Target; enum WeightResult : std::uint8_t { - Continue, - Done, - Miss, + Done = 0U, + Continue = 1U, + Miss = 2U, }; using weight_type = unsigned; @@ -33,9 +33,14 @@ struct DistanceResult { weight_type weight; bool isInsideFunction; - explicit DistanceResult(WeightResult result_, weight_type weight_ = 0, + explicit DistanceResult(WeightResult result_ = WeightResult::Miss, + weight_type weight_ = 0, bool isInsideFunction_ = true) - : result(result_), weight(weight_), isInsideFunction(isInsideFunction_) {} + : result(result_), weight(weight_), isInsideFunction(isInsideFunction_){}; + + bool operator<(const DistanceResult &b) const; + + std::string toString() const; }; class DistanceCalculator { diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 610d853911..2e8d475edf 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -4060,6 +4060,38 @@ 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.getTargets()) { + 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", distance.toString().c_str(), + target->toString().c_str()); + } +} + +void Executor::reportProgressTowardsTargets() const { + reportProgressTowardsTargets("paused ", pausedStates); + reportProgressTowardsTargets("", states); +} + void Executor::run(std::vector initialStates) { // Delay init till now so that ticks don't accrue during optimization and // such. @@ -4098,6 +4130,7 @@ void Executor::run(std::vector initialStates) { } if (guidanceKind == GuidanceKind::ErrorGuidance) { + reportProgressTowardsTargets(); bool canReachNew1 = decreaseConfidenceFromStoppedStates(pausedStates); bool canReachNew2 = decreaseConfidenceFromStoppedStates(states, haltExecution); diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index 8aa39b241a..b12e5e7f62 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -593,6 +593,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); diff --git a/lib/Core/Searcher.h b/lib/Core/Searcher.h index 436e8dcdd4..ee85de1d58 100644 --- a/lib/Core/Searcher.h +++ b/lib/Core/Searcher.h @@ -158,14 +158,6 @@ class TargetedSearcher final : public Searcher { }; 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>; diff --git a/lib/Module/KModule.cpp b/lib/Module/KModule.cpp index 03549daea7..b87e3e0ec2 100644 --- a/lib/Module/KModule.cpp +++ b/lib/Module/KModule.cpp @@ -728,6 +728,8 @@ std::string KBlock::getLabel() const { std::string _label; llvm::raw_string_ostream label_stream(_label); basicBlock->printAsOperand(label_stream, false); + label_stream << " (lines " << getFirstInstruction()->info->line << " to " + << getLastInstruction()->info->line << ")"; std::string label = label_stream.str(); return label; } From 20e8e1dde1d0c39d1b76920a2eb3e700add1e18f Mon Sep 17 00:00:00 2001 From: Yurii Kostyukov Date: Tue, 27 Jun 2023 17:19:09 +0300 Subject: [PATCH 15/45] [fix] Fixed 0 weight for targets on the same frame --- lib/Core/DistanceCalculator.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/Core/DistanceCalculator.cpp b/lib/Core/DistanceCalculator.cpp index a1e384ee67..44c2f26e72 100644 --- a/lib/Core/DistanceCalculator.cpp +++ b/lib/Core/DistanceCalculator.cpp @@ -72,14 +72,7 @@ DistanceCalculator::getDistance(KInstruction *pc, KInstruction *prevPC, if (distanceInCallGraph(sfi->kf, kb, callWeight, distanceToTargetFunction, target)) { callWeight *= 2; - if (callWeight == 0 && target->shouldFailOnThisTarget()) { - return target->isTheSameAsIn(kb->getFirstInstruction()) && - target->isThatError(error) - ? DistanceResult(Done) - : DistanceResult(Continue); - } else { - callWeight += sfNum; - } + callWeight += sfNum; if (callWeight < minCallWeight) { minCallWeight = callWeight; From 6f77fef688ecfb482072edb6499ac52ba9bad4ec Mon Sep 17 00:00:00 2001 From: Yurii Kostyukov Date: Tue, 27 Jun 2023 17:19:33 +0300 Subject: [PATCH 16/45] [test] Added report tests --- test/Industry/if2.c | 4 +++- test/Industry/while_true.c | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/Industry/if2.c b/test/Industry/if2.c index 6936bea6ab..6ab4ec7ae0 100644 --- a/test/Industry/if2.c +++ b/test/Industry/if2.c @@ -13,4 +13,6 @@ int main(int x) { // 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: 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 (lines 8 to 8) in function main diff --git a/test/Industry/while_true.c b/test/Industry/while_true.c index 7fa0c973d0..11ef6bba59 100644 --- a/test/Industry/while_true.c +++ b/test/Industry/while_true.c @@ -14,7 +14,12 @@ int main() { // 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: 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 (lines 8 to 8) in function main + // 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: 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 (lines 8 to 8) in function main From a4af7bf404198bcabdbb88f1c701d6937ecfb16f Mon Sep 17 00:00:00 2001 From: Yurii Kostyukov Date: Wed, 21 Jun 2023 17:52:54 +0300 Subject: [PATCH 17/45] [feat] Added targets to fork --- lib/Core/Executor.cpp | 31 ++++++++++++++++++++--------- lib/Core/Executor.h | 7 +++++-- lib/Core/SpecialFunctionHandler.cpp | 9 ++++----- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 2e8d475edf..56666f1995 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -1063,8 +1063,10 @@ ref Executor::maxStaticPctChecks(ExecutionState ¤t, } Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, - bool isInternal, BranchType reason) { + KBlock *ifTrueBlock, KBlock *ifFalseBlock, + BranchType reason) { PartialValidity res; + bool isInternal = ifTrueBlock == ifFalseBlock; std::map>::iterator it = seedMap.find(¤t); bool isSeeding = it != seedMap.end(); @@ -1266,6 +1268,12 @@ 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; @@ -2415,8 +2423,12 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { cond = optimizer.optimizeExpr(cond, false); + KFunction *kf = state.stack.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); // NOTE: There is a hidden dependency here, markBranchVisited // requires that we still be in the context of the branch @@ -2741,7 +2753,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); @@ -4899,7 +4911,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)); @@ -4963,7 +4975,7 @@ bool Executor::resolveExact(ExecutionState &estate, ref address, uniqueBase = toUnique(estate, uniqueBase); 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) || @@ -5001,7 +5013,7 @@ bool Executor::resolveExact(ExecutionState &estate, ref address, 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(rl.at(i), branches.first)); @@ -5600,7 +5612,7 @@ void Executor::executeMemoryOperation( uniqueBase = toUnique(estate, uniqueBase); 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) || @@ -5791,8 +5803,9 @@ void Executor::executeMemoryOperation( ObjectState *wos = state->addressSpace.getWriteable(mo, os); if (wos->readOnly) { - branches = fork(*state, Expr::createIsZero(unboundConditions[i]), - true, BranchType::MemOp); + branches = + forkInternal(*state, Expr::createIsZero(unboundConditions[i]), + BranchType::MemOp); assert(branches.first); terminateStateOnError(*branches.first, "memory error: object read only", diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index b12e5e7f62..b25e1faab6 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -424,8 +424,11 @@ 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. diff --git a/lib/Core/SpecialFunctionHandler.cpp b/lib/Core/SpecialFunctionHandler.cpp index 1a77e09687..0ecb4e2fa2 100644 --- a/lib/Core/SpecialFunctionHandler.cpp +++ b/lib/Core/SpecialFunctionHandler.cpp @@ -813,16 +813,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, From 0f2dd85ff896e6fae1766d66134092b744ff5a96 Mon Sep 17 00:00:00 2001 From: Yurii Kostyukov Date: Wed, 21 Jun 2023 14:29:11 +0300 Subject: [PATCH 18/45] [feat] Not calling solver if target is unreachable --- include/klee/Core/TerminationTypes.h | 3 +- include/klee/Module/Target.h | 5 ++ lib/Core/Executor.cpp | 98 +++++++++++++++++++++++----- lib/Core/Executor.h | 4 ++ lib/Module/Target.cpp | 9 +++ 5 files changed, 100 insertions(+), 19 deletions(-) diff --git a/include/klee/Core/TerminationTypes.h b/include/klee/Core/TerminationTypes.h index 96e6507dfa..e865de6a31 100644 --- a/include/klee/Core/TerminationTypes.h +++ b/include/klee/Core/TerminationTypes.h @@ -42,7 +42,8 @@ MARK(EXECERR, 26U) \ TTYPE(Replay, 27U, "") \ TTYPE(SilentExit, 28U, "") \ - MARK(END, 28U) + TTYPE(MissedAllTargets, 29U, "") \ + MARK(END, 29U) ///@brief Reason an ExecutionState got terminated. enum class StateTerminationType : std::uint8_t { diff --git a/include/klee/Module/Target.h b/include/klee/Module/Target.h index fd43f27023..92fdd3c424 100644 --- a/include/klee/Module/Target.h +++ b/include/klee/Module/Target.h @@ -96,6 +96,11 @@ struct Target { bool isTheSameAsIn(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; } std::string toString() const; diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 56666f1995..9b2563e8c4 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -1062,10 +1062,25 @@ 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.getTargets()) { + auto target = p.first; + if (target->mustVisitForkBranches(es.prevPC)) + return true; + auto dist = distanceCalculator->getDistance(nextInstr, es.prevPC, es.initPC, + es.stack, es.error, target); + if (dist.result != WeightResult::Miss) + return true; + } + return false; +} + Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, KBlock *ifTrueBlock, KBlock *ifFalseBlock, BranchType reason) { - PartialValidity res; bool isInternal = ifTrueBlock == ifFalseBlock; std::map>::iterator it = seedMap.find(¤t); @@ -1078,8 +1093,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; @@ -2473,12 +2526,15 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { ref errorCase = ConstantExpr::alloc(1, Expr::Bool); SmallPtrSet destinations; + KFunction *kf = state.stack.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 @@ -2565,12 +2621,15 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { // Track default branch values ref defaultValue = ConstantExpr::alloc(1, Expr::Bool); + KFunction *kf = state.stack.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 @@ -2581,6 +2640,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); @@ -2589,8 +2651,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 @@ -2610,19 +2670,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); + } } } diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index b25e1faab6..0fe7a1f0d7 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -414,6 +414,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 diff --git a/lib/Module/Target.cpp b/lib/Module/Target.cpp index 08b7feca25..99673cfcdb 100644 --- a/lib/Module/Target.cpp +++ b/lib/Module/Target.cpp @@ -79,6 +79,15 @@ 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; From 1d90d4b9c5bd9129d3b31ad80f46e74c20ce237b Mon Sep 17 00:00:00 2001 From: Yurii Kostyukov Date: Tue, 27 Jun 2023 16:39:08 +0300 Subject: [PATCH 19/45] [test] Added reach fork opt. test --- ...lingUnreachableBranchesInForkErrorGuided.c | 15 +++++ ...nreachableBranchesInForkErrorGuided.c.json | 65 +++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 test/Feature/NotCallingUnreachableBranchesInForkErrorGuided.c create mode 100644 test/Feature/NotCallingUnreachableBranchesInForkErrorGuided.c.json 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 From 0fbec009ce50ef83a1b5a8afa67b75c80888c4ae Mon Sep 17 00:00:00 2001 From: Yurii Kostyukov Date: Thu, 29 Jun 2023 15:30:57 +0300 Subject: [PATCH 20/45] [fix] Small fixes --- lib/Core/Executor.cpp | 9 ++++----- lib/Core/Searcher.h | 1 - 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 9b2563e8c4..2fd22d3e67 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -2340,8 +2340,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) && @@ -6379,11 +6378,11 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv, 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; diff --git a/lib/Core/Searcher.h b/lib/Core/Searcher.h index ee85de1d58..4302977607 100644 --- a/lib/Core/Searcher.h +++ b/lib/Core/Searcher.h @@ -39,7 +39,6 @@ class DistanceCalculator; template class DiscretePDF; template class WeightedQueue; class ExecutionState; -class Executor; class TargetCalculator; class TargetForest; class TargetReachability; From c7ed0e9f9fd8decf46c26b91e05235c5f23ef21b Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Sat, 1 Jul 2023 00:15:42 +0400 Subject: [PATCH 21/45] [chore] Make analysis results generation options independent of test generation --- lib/Runner/run_klee.cpp | 96 ++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/lib/Runner/run_klee.cpp b/lib/Runner/run_klee.cpp index 6637bc2512..acebf7880b 100644 --- a/lib/Runner/run_klee.cpp +++ b/lib/Runner/run_klee.cpp @@ -542,6 +542,7 @@ KleeHandler::openTestFile(const std::string &suffix, unsigned id) { void KleeHandler::processTestCase(const ExecutionState &state, const char *errorMessage, const char *errorSuffix) { + unsigned id = ++m_numTotalTests; if (!WriteNone) { KTest ktest; ktest.numArgs = m_argc; @@ -556,7 +557,6 @@ void KleeHandler::processTestCase(const ExecutionState &state, const auto start_time = time::getWallTime(); - unsigned id = ++m_numTotalTests; if (success) { if (!kTest_toFile( @@ -597,32 +597,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 +609,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,6 +620,53 @@ void KleeHandler::processTestCase(const ExecutionState &state, } } // if (!WriteNone) + 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 (errorMessage && OptExitOnError) { m_interpreter->prepareForEarlyExit(); klee_error("EXITING ON ERROR:\n%s\n", errorMessage); From c6299c8a687548d0ad64b0e8aeef42f507a13cf0 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Wed, 28 Jun 2023 22:09:42 +0400 Subject: [PATCH 22/45] [fix] `ExprPPrinter` --- lib/Expr/ExprPPrinter.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/Expr/ExprPPrinter.cpp b/lib/Expr/ExprPPrinter.cpp index bf222af763..b8fc326ef1 100644 --- a/lib/Expr/ExprPPrinter.cpp +++ b/lib/Expr/ExprPPrinter.cpp @@ -595,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); @@ -608,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); @@ -628,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(); From 8f1a95dfc5780aab6c0449a4db890b208f6cacd4 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Fri, 30 Jun 2023 12:36:09 +0400 Subject: [PATCH 23/45] [fix] Add a few fixes --- include/klee/Core/TerminationTypes.h | 1 + lib/Core/Executor.cpp | 12 +++++++++--- lib/Core/Memory.h | 13 +++++++------ lib/Core/TargetedExecutionManager.cpp | 3 +++ 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/include/klee/Core/TerminationTypes.h b/include/klee/Core/TerminationTypes.h index e865de6a31..9da41d31f3 100644 --- a/include/klee/Core/TerminationTypes.h +++ b/include/klee/Core/TerminationTypes.h @@ -61,6 +61,7 @@ enum Reason { MaxInstructions, MaxSteppedInstructions, MaxTime, + MaxCycles, CovCheck, NoMoreStates, ReachedTarget, diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 2fd22d3e67..2d7830f175 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -4203,10 +4203,16 @@ void Executor::run(std::vector initialStates) { } if (guidanceKind == GuidanceKind::ErrorGuidance) { + SetOfStates leftState = states; + for (auto state : pausedStates) { + leftState.erase(state); + } + reportProgressTowardsTargets(); - bool canReachNew1 = decreaseConfidenceFromStoppedStates(pausedStates); + bool canReachNew1 = decreaseConfidenceFromStoppedStates( + pausedStates, HaltExecution::MaxCycles); bool canReachNew2 = - decreaseConfidenceFromStoppedStates(states, haltExecution); + decreaseConfidenceFromStoppedStates(leftState, haltExecution); for (auto &startBlockAndWhiteList : targets) { startBlockAndWhiteList.second.reportFalsePositives(canReachNew1 || @@ -5541,7 +5547,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) { diff --git a/lib/Core/Memory.h b/lib/Core/Memory.h index bbc314e982..8a1997dc9a 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. diff --git a/lib/Core/TargetedExecutionManager.cpp b/lib/Core/TargetedExecutionManager.cpp index 3cf9cd1810..2aa1d615ee 100644 --- a/lib/Core/TargetedExecutionManager.cpp +++ b/lib/Core/TargetedExecutionManager.cpp @@ -248,6 +248,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; From f4e77c1b9034fd8c024d696f94eac62a7b93a8e8 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Sat, 1 Jul 2023 00:39:13 +0400 Subject: [PATCH 24/45] [fix] Revert `getLabel`: this function should not return anything other than the block label --- lib/Core/Executor.cpp | 6 ++++-- lib/Module/KModule.cpp | 2 -- test/Industry/if2.c | 2 +- test/Industry/while_true.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 2d7830f175..b6277cac2a 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -4155,8 +4155,10 @@ void Executor::reportProgressTowardsTargets(std::string prefix, for (auto &p : distancesTowardsTargets) { auto target = p.first; auto distance = p.second; - klee_message("%s for %s", distance.toString().c_str(), - target->toString().c_str()); + 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); } } diff --git a/lib/Module/KModule.cpp b/lib/Module/KModule.cpp index b87e3e0ec2..03549daea7 100644 --- a/lib/Module/KModule.cpp +++ b/lib/Module/KModule.cpp @@ -728,8 +728,6 @@ std::string KBlock::getLabel() const { std::string _label; llvm::raw_string_ostream label_stream(_label); basicBlock->printAsOperand(label_stream, false); - label_stream << " (lines " << getFirstInstruction()->info->line << " to " - << getLastInstruction()->info->line << ")"; std::string label = label_stream.str(); return label; } diff --git a/test/Industry/if2.c b/test/Industry/if2.c index 6ab4ec7ae0..3e4990abbe 100644 --- a/test/Industry/if2.c +++ b/test/Industry/if2.c @@ -15,4 +15,4 @@ int main(int x) { // 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 // 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 (lines 8 to 8) in function main +// 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 11ef6bba59..4b6cc204dc 100644 --- a/test/Industry/while_true.c +++ b/test/Industry/while_true.c @@ -15,11 +15,11 @@ int main() { // 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 (lines 8 to 8) in function main +// 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: 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 // 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 (lines 8 to 8) in function main +// CHECK-REACH-2: (0, 1, 1) for Target 1: error in %17 in function main (lines 8 to 8) From 9c8e608debdaf20251560355b8c72dd8bfafa7f2 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Sat, 1 Jul 2023 01:01:45 +0400 Subject: [PATCH 25/45] [feat] Add new test --- lib/Core/Executor.cpp | 3 ++- test/Feature/BlockPath.c | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 test/Feature/BlockPath.c diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index b6277cac2a..e777cdc27d 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -3127,7 +3127,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 { diff --git a/test/Feature/BlockPath.c b/test/Feature/BlockPath.c new file mode 100644 index 0000000000..252cf0e96b --- /dev/null +++ b/test/Feature/BlockPath.c @@ -0,0 +1,24 @@ +// RUN: %clang %s -emit-llvm %O0opt -c -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 %10 %12) %8 %10 %17 %19" %t.klee-out/test000001.kpath +// RUN: grep "(path: 0 (main: %0 %5 %6 %8 (abs: %1 %7 %10 %12) %8 %10 %17 %19" %t.klee-out/test000002.kpath + +#include "klee/klee.h" +#include + +int abs(int x) { + if (x >= 0) { + return x; + } + return -x; +} + +int main() { + int x; + klee_make_symbolic(&x, sizeof(x), "x"); + int y = abs(x); + if (y != INT_MIN && y < 0) { + printf(!" Reached "); + } +} From caa57ffe80efd8cc786f127abcbba8d33d73ec18 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Sat, 1 Jul 2023 13:19:37 +0400 Subject: [PATCH 26/45] [fix] Fix `strcmp` model --- runtime/klee-libc/strcmp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; } From 77b5edf44c6c69468e041294a3c9b988c85de2df Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 4 Jul 2023 23:22:37 +0400 Subject: [PATCH 27/45] [chore] Add new test for 'write-kpaths' --- test/Feature/BlockPath.c | 24 ------------ test/Feature/BlockPath.ll | 82 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 24 deletions(-) delete mode 100644 test/Feature/BlockPath.c create mode 100644 test/Feature/BlockPath.ll diff --git a/test/Feature/BlockPath.c b/test/Feature/BlockPath.c deleted file mode 100644 index 252cf0e96b..0000000000 --- a/test/Feature/BlockPath.c +++ /dev/null @@ -1,24 +0,0 @@ -// RUN: %clang %s -emit-llvm %O0opt -c -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 %10 %12) %8 %10 %17 %19" %t.klee-out/test000001.kpath -// RUN: grep "(path: 0 (main: %0 %5 %6 %8 (abs: %1 %7 %10 %12) %8 %10 %17 %19" %t.klee-out/test000002.kpath - -#include "klee/klee.h" -#include - -int abs(int x) { - if (x >= 0) { - return x; - } - return -x; -} - -int main() { - int x; - klee_make_symbolic(&x, sizeof(x), "x"); - int y = abs(x); - if (y != INT_MIN && y < 0) { - printf(!" Reached "); - } -} 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*) From 721f4fb41a1a079aef49a6ad40883eb6e32a4080 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Sat, 1 Jul 2023 00:16:56 +0400 Subject: [PATCH 28/45] [fix] Add `History::size()` --- include/klee/Module/TargetForest.h | 35 ++++++++++++++++++++---------- lib/Core/Searcher.h | 2 +- lib/Runner/run_klee.cpp | 1 - 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/include/klee/Module/TargetForest.h b/include/klee/Module/TargetForest.h index eac93e8ef4..8476e5aa61 100644 --- a/include/klee/Module/TargetForest.h +++ b/include/klee/Module/TargetForest.h @@ -218,12 +218,35 @@ class TargetForest { static EquivTargetsHistoryHashSet cachedHistories; static TargetsHistoryHashSet histories; unsigned hashValue; + unsigned sizeValue; explicit History(ref _target, ref _visitedTargets) : target(_target), visitedTargets(_visitedTargets) { computeHash(); } + 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; + } + public: const ref target; const ref visitedTargets; @@ -238,21 +261,11 @@ class TargetForest { } unsigned hash() const { return hashValue; } + unsigned size() const { return sizeValue; } 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(); diff --git a/lib/Core/Searcher.h b/lib/Core/Searcher.h index 4302977607..2951b62dd8 100644 --- a/lib/Core/Searcher.h +++ b/lib/Core/Searcher.h @@ -161,7 +161,6 @@ class GuidedSearcher final : public Searcher { using TargetToStateUnorderedSetMap = TargetHashMap>; -private: using TargetToSearcherMap = TargetHashMap>; using TargetToStateSetMap = TargetHashMap>; @@ -189,6 +188,7 @@ class GuidedSearcher final : public Searcher { using TargetForestHisoryTargetVector = std::vector, ref>>; +private: enum Guidance { CoverageGuidance, ErrorGuidance }; Guidance guidance; diff --git a/lib/Runner/run_klee.cpp b/lib/Runner/run_klee.cpp index acebf7880b..f5bfd1a9ea 100644 --- a/lib/Runner/run_klee.cpp +++ b/lib/Runner/run_klee.cpp @@ -557,7 +557,6 @@ void KleeHandler::processTestCase(const ExecutionState &state, const auto start_time = time::getWallTime(); - if (success) { if (!kTest_toFile( &ktest, From 027f212c0a13503701759156732f736e6f3f6cd7 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Sat, 1 Jul 2023 00:17:52 +0400 Subject: [PATCH 29/45] [feat] Update expressions building and simplifying --- include/klee/Expr/Expr.h | 9 +++ include/klee/Expr/ExprVisitor.h | 5 +- lib/Expr/Constraints.cpp | 38 ---------- lib/Expr/Expr.cpp | 121 +++++++++++++++++++++++++++----- lib/Expr/ExprVisitor.cpp | 11 +-- 5 files changed, 122 insertions(+), 62 deletions(-) diff --git a/include/klee/Expr/Expr.h b/include/klee/Expr/Expr.h index f57e908d79..8ce01335f5 100644 --- a/include/klee/Expr/Expr.h +++ b/include/klee/Expr/Expr.h @@ -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; @@ -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/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/lib/Expr/Constraints.cpp b/lib/Expr/Constraints.cpp index 88f0e137a8..a52db8dd2d 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)) { diff --git a/lib/Expr/Expr.cpp b/lib/Expr/Expr.cpp index 5b8bed1008..348034153d 100644 --- a/lib/Expr/Expr.cpp +++ b/lib/Expr/Expr.cpp @@ -1605,6 +1605,8 @@ ref SelectExpr::create(ref c, ref t, ref f) { 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); @@ -1622,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)) { @@ -1678,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)) { @@ -1722,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); } @@ -1735,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) { @@ -1748,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); } /***/ @@ -1839,10 +1877,6 @@ static ref AddExpr_createPartialR(const ref &cl, Expr *r) { } else if (rk == Expr::Sub && isa(r->getKid(0))) { // A + (B-c) == (A+B) - c return SubExpr::create(AddExpr::create(cl, r->getKid(0)), r->getKid(1)); - } else if (rk == Expr::Select) { - SelectExpr *se = cast(r); - return SelectExpr::create(se->cond, AddExpr::create(cl, se->trueExpr), - AddExpr::create(cl, se->falseExpr)); } else { return AddExpr::alloc(cl, r); } @@ -2058,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); \ @@ -2071,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); \ @@ -2094,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); \ @@ -2103,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) { @@ -2150,12 +2231,16 @@ static ref EqExpr_create(const ref &l, const ref &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)); + } } } @@ -2299,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); } @@ -2309,9 +2398,9 @@ static ref UleExpr_create(const ref &l, const ref &r) { return OrExpr::create(Expr::createIsZero(l), r); } else if (r->isZero()) { return EqExpr::create(l, r); + } else { + return UleExpr::alloc(l, r); } - - return UleExpr::alloc(l, r); } static ref SltExpr_create(const ref &l, const ref &r) { 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; } } From 9690f62a9a70d8971f8e3696ca670e8703f0f454 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Mon, 3 Jul 2023 18:19:01 +0400 Subject: [PATCH 30/45] [fix] Report `MissedAllTargets` states --- include/klee/Core/TerminationTypes.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/klee/Core/TerminationTypes.h b/include/klee/Core/TerminationTypes.h index 9da41d31f3..72132b109d 100644 --- a/include/klee/Core/TerminationTypes.h +++ b/include/klee/Core/TerminationTypes.h @@ -33,8 +33,9 @@ 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") \ - MARK(PROGERR, 20U) \ + TTYPE(InternalOutOfMemory, 20U, "out_of_memory.err") \ + TTYPE(MissedAllTargets, 21U, "miss_all_targets.err") \ + MARK(PROGERR, 21U) \ TTYPE(User, 23U, "user.err") \ MARK(USERERR, 23U) \ TTYPE(Execution, 25U, "exec.err") \ @@ -42,8 +43,7 @@ MARK(EXECERR, 26U) \ TTYPE(Replay, 27U, "") \ TTYPE(SilentExit, 28U, "") \ - TTYPE(MissedAllTargets, 29U, "") \ - MARK(END, 29U) + MARK(END, 28U) ///@brief Reason an ExecutionState got terminated. enum class StateTerminationType : std::uint8_t { From 152d6bf07faf6d5407cd076a35f465347dc023c8 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Mon, 3 Jul 2023 18:21:10 +0400 Subject: [PATCH 31/45] [feat] Add `LazyInitCat` and some their options [feat] Lazy initialize constant size object by default due to performanse issues [fix] Update tests --- lib/Core/AddressSpace.cpp | 6 +- lib/Core/Executor.cpp | 150 ++++++++++++++---- lib/Core/Executor.h | 5 +- lib/Core/MemoryManager.cpp | 5 + .../ImpossibleAddressForLI.c | 2 +- .../SingleInitializationAndAccess.c | 10 +- .../SymbolicSizes/FirstAndLastElements.c | 2 +- .../SymbolicSizes/ImplicitArrayExtension.c | 2 +- .../ImplicitSizeConcretization.c | 2 +- test/Feature/SymbolicSizes/IntArray.c | 2 +- .../LazyInstantiationOfSymbolicSize.c | 4 +- test/Feature/SymbolicSizes/LowerOutOfBound.c | 2 +- test/Feature/SymbolicSizes/MinimizeSize.c | 2 +- .../SymbolicSizes/MultipleAllocations.c | 2 +- .../SymbolicSizes/NegativeIndexArray.c | 2 +- test/Feature/SymbolicSizes/NegativeSize.c | 2 +- .../SymbolicSizes/RecomputeModelTwoArrays.c | 2 +- .../Feature/SymbolicSizes/SegmentComparator.c | 2 +- .../SymbolicSizes/SymbolicArrayOnStack.c | 2 +- .../SymbolicArrayOnStackWithSkipSymbolics.c | 2 +- test/Feature/SymbolicSizes/VoidStar.c | 8 +- ...eplay-test-with-lazy-initialized-objects.c | 4 +- 22 files changed, 152 insertions(+), 68 deletions(-) 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/Executor.cpp b/lib/Core/Executor.cpp index e777cdc27d..0e93712b61 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -136,6 +136,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 " @@ -202,7 +205,20 @@ 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)); + } // namespace klee namespace { @@ -235,9 +251,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 @@ -415,6 +431,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; @@ -4328,14 +4345,20 @@ void Executor::targetedRun(ExecutionState &initialState, KBlock *target, } 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)) { @@ -4915,6 +4938,11 @@ void Executor::executeAlloc(ExecutionState &state, ref size, bool isLocal, if (allocationAlignment == 0) { allocationAlignment = getAllocationAlignment(allocSite); } + + if (!isa(size) && !UseSymbolicSizeAllocation) { + size = toConstant(state, size, "symbolic size allocation"); + } + ref upperBoundSizeConstraint = Expr::createTrue(); if (!isa(size)) { upperBoundSizeConstraint = UleExpr::create( @@ -5309,14 +5337,11 @@ bool Executor::resolveMemoryObjects( // we are on an error path (no resolution, multiple resolution, one // resolution with out of bounds) - bool baseWasNotResolved = state.resolvedPointers.count(base) == 0; address = optimizer.optimizeExpr(address, true); ref checkOutOfBounds = Expr::createTrue(); - ref baseUniqueExpr = toUnique(state, base); - - bool checkAddress = isa(address) || isa(address) || - state.isGEPExpr(address); + bool checkAddress = + isa(address) || isa(address) || base != address; if (!checkAddress && isa(address)) { checkAddress = true; std::vector> alternatives; @@ -5329,8 +5354,7 @@ bool Executor::resolveMemoryObjects( } mayLazyInitialize = LazyInitialization != LazyInitializationPolicy::None && - !isa(baseUniqueExpr) && - baseWasNotResolved && checkAddress; + !isa(base); if (!onlyLazyInitialize || !mayLazyInitialize) { ResolutionList rl; @@ -5361,8 +5385,11 @@ 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*/ + UseSymbolicSizeLazyInit)) { return false; } // Lazy initialization might fail if we've got unappropriate address @@ -5409,7 +5436,7 @@ bool Executor::checkResolvedMemoryObjects( 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)); } @@ -5456,12 +5483,20 @@ bool Executor::checkResolvedMemoryObjects( } else { resolveConditions.push_back(inBounds); unboundConditions.push_back(notInBounds); + if (hasLazyInitialized /*&& !state.isolated*/) { + notInBounds = AndExpr::create( + notInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); + } checkOutOfBounds = notInBounds; } } else if (mayBeOutOfBound) { if (mustBeOutOfBound) { checkOutOfBounds = Expr::createTrue(); } else { + if (hasLazyInitialized /*&& !state.isolated*/) { + notInBounds = AndExpr::create( + notInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); + } checkOutOfBounds = notInBounds; } } @@ -5477,9 +5512,11 @@ bool Executor::checkResolvedMemoryObjects( 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)); + baseInBounds = AndExpr::create( + baseInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); } if (hasLazyInitialized && i == mayBeResolvedMemoryObjects.size() - 1) { @@ -5512,6 +5549,13 @@ bool Executor::checkResolvedMemoryObjects( resolveConditions.push_back(inBounds); resolvedMemoryObjects.push_back(mo->id); unboundConditions.push_back(notInBounds); + + if (hasLazyInitialized && + i == mayBeResolvedMemoryObjects.size() - 1 /*&& !state.isolated*/) { + notInBounds = AndExpr::create( + notInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); + } + if (mayBeOutOfBound) { checkOutOfBounds = AndExpr::create(checkOutOfBounds, notInBounds); } @@ -5663,12 +5707,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; @@ -5677,9 +5733,7 @@ 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 = forkInternal(estate, Expr::createIsZero(base), BranchType::MemOp); @@ -5704,7 +5758,7 @@ void Executor::executeMemoryOperation( state->resolvedPointers.at(base).size() == 1) { success = true; idFastResult = *state->resolvedPointers[base].begin(); - } else if (allLeafsAreConstant(address)) { + } else { solver->setTimeout(coreSolverTimeout); if (!state->addressSpace.resolveOne(*state, solver, address, targetType, @@ -5726,6 +5780,16 @@ void Executor::executeMemoryOperation( } ref inBounds = mo->getBoundsCheckPointer(address, bytes); + ref baseInBounds = Expr::createTrue(); + + if (base != address || size != bytes) { + baseInBounds = + AndExpr::create(baseInBounds, mo->getBoundsCheckPointer(base, size)); + baseInBounds = AndExpr::create( + baseInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); + } + + inBounds = AndExpr::create(inBounds, baseInBounds); inBounds = optimizer.optimizeExpr(inBounds, true); inBounds = Simplificator::simplifyExpr(state->constraints.cs(), inBounds) @@ -5920,6 +5984,16 @@ void Executor::executeMemoryOperation( const MemoryObject *mo = op.first; const ObjectState *os = op.second; + 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); @@ -5972,15 +6046,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; @@ -5990,7 +6063,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)); @@ -6016,9 +6089,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; } @@ -6041,6 +6116,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); @@ -6068,7 +6145,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*/ UseSymbolicSizeLazyInit); assert(success); assert(id); auto op = state.addressSpace.findObject(id); @@ -6606,18 +6684,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"; } } diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index 0fe7a1f0d7..ac9165f9f4 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -397,8 +397,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, @@ -547,6 +547,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 diff --git a/lib/Core/MemoryManager.cpp b/lib/Core/MemoryManager.cpp index 6d601ce3e2..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), 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/SymbolicSizes/FirstAndLastElements.c b/test/Feature/SymbolicSizes/FirstAndLastElements.c index 800af271e4..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 --check-out-of-memory --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 56ff1843e5..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 --check-out-of-memory --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 974623d058..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 --check-out-of-memory --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 a67bca1cb6..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 --check-out-of-memory --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 69d9633abf..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 --check-out-of-memory --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/NegativeIndexArray.c b/test/Feature/SymbolicSizes/NegativeIndexArray.c index 37c413bd51..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 --check-out-of-memory --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 ea8ab6d955..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 --check-out-of-memory --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 83c52cab06..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 --check-out-of-memory --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 26a0e18fff..acf086a65f 100644 --- a/test/Feature/SymbolicSizes/SymbolicArrayOnStack.c +++ b/test/Feature/SymbolicSizes/SymbolicArrayOnStack.c @@ -1,7 +1,7 @@ // 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 --check-out-of-memory --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" diff --git a/test/Feature/SymbolicSizes/SymbolicArrayOnStackWithSkipSymbolics.c b/test/Feature/SymbolicSizes/SymbolicArrayOnStackWithSkipSymbolics.c index af5a4df6dd..dbb2331aac 100644 --- a/test/Feature/SymbolicSizes/SymbolicArrayOnStackWithSkipSymbolics.c +++ b/test/Feature/SymbolicSizes/SymbolicArrayOnStackWithSkipSymbolics.c @@ -1,7 +1,7 @@ // 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 --check-out-of-memory --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" diff --git a/test/Feature/SymbolicSizes/VoidStar.c b/test/Feature/SymbolicSizes/VoidStar.c index cd64f3d10a..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 --check-out-of-memory --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/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 From fd87c86ab24492406b08c3cdfe20ce9ad8fc1a06 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 4 Jul 2023 01:08:50 +0400 Subject: [PATCH 32/45] [chore] Update `options.json` --- configs/options.json | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/configs/options.json b/configs/options.json index 736bb70193..03a3849384 100644 --- a/configs/options.json +++ b/configs/options.json @@ -7,7 +7,11 @@ "maxSolverTime" : "5", "maxForks" : "200", "maxSymAlloc" : "32", - "symbolicAllocationThreshhold" : "2048" + "SymbolicAllocationThreshold" : "2048", + "minNumberElements" : "4", + "maxCycles" : 10, + "useSymbolicSizeLI" : false, + "writeKpaths": false }, "configurations": [ { @@ -33,10 +37,13 @@ "--extern-calls-can-return-null", "--align-symbolic-pointers=false", "--write-no-tests", - "--write-kpaths", + "--write-kpaths=${writeKpaths}", "--use-lazy-initialization=only", + "--min-number-elements-li=${minNumberElements}", + "--use-sym-size-li=${useSymbolicSizeLI}", + "--max-cycles=${maxCycles}", "--rewrite-equalities=simple", - "--symbolic-allocation-threshhold=${symbolicAllocationThreshhold}", + "--symbolic-allocation-threshold=${SymbolicAllocationThreshold}", "--analysis-reproduce=${sarifTracesFilePath}", "${bytecodeFilePath}" ] From 90c63072e999e0dc396c14d13af01875ca636938 Mon Sep 17 00:00:00 2001 From: Alex Babushkin Date: Mon, 3 Jul 2023 15:04:22 +0500 Subject: [PATCH 33/45] [fix] Use getWrtiable only with writes --- lib/Core/Executor.cpp | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 0e93712b61..9e4442b3df 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -5666,18 +5666,14 @@ 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) && + !os->readOnly && isa(result) && (MockMutableGlobals != MockMutableGlobalsPolicy::PrimitiveFields || !targetType->getRawType()->isPointerTy())) { result = makeMockValue(state, "mockGlobalValue", result->getWidth()); + ObjectState *wos = state.addressSpace.getWriteable(mo, os); wos->write(mo->getOffsetExpr(address), result); } @@ -5816,8 +5812,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); @@ -5828,19 +5824,17 @@ 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) && + mo->isGlobal && !os->readOnly && isa(result) && (MockMutableGlobals != MockMutableGlobalsPolicy::PrimitiveFields || !targetType->getRawType()->isPointerTy())) { result = makeMockValue(*state, "mockGlobalValue", result->getWidth()); + ObjectState *wos = state->addressSpace.getWriteable(mo, os); wos->write(mo->getOffsetExpr(address), result); } @@ -6002,9 +5996,9 @@ void Executor::executeMemoryOperation( 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); @@ -6015,16 +6009,14 @@ 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) && + mo->isGlobal && !os->readOnly && isa(result) && (MockMutableGlobals != MockMutableGlobalsPolicy::PrimitiveFields || !targetType->getRawType()->isPointerTy())) { result = makeMockValue(*bound, "mockGlobalValue", result->getWidth()); + ObjectState *wos = bound->addressSpace.getWriteable(mo, os); wos->write(mo->getOffsetExpr(address), result); } From c6a1b96ec61a79fd0b74fb7cfec7a3c243682a6e Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 4 Jul 2023 01:33:40 +0400 Subject: [PATCH 34/45] [chore] Update `MemoryLimit.c` --- test/Feature/MemoryLimit.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/Feature/MemoryLimit.c b/test/Feature/MemoryLimit.c index 640850d2aa..f0522c4da8 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=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=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 From 9a9cf2d5849fa5352c892c989740550351ac037f Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Tue, 4 Jul 2023 17:53:34 +0400 Subject: [PATCH 35/45] [style] Fix typo --- include/klee/Solver/SolverCmdLine.h | 2 +- lib/Core/Executor.cpp | 2 +- lib/Solver/ConcretizingSolver.cpp | 2 +- lib/Solver/SolverCmdLine.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) 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/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 9e4442b3df..c2780d603d 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -5152,7 +5152,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()); diff --git a/lib/Solver/ConcretizingSolver.cpp b/lib/Solver/ConcretizingSolver.cpp index b7f2b93404..f48cb2c1f7 100644 --- a/lib/Solver/ConcretizingSolver.cpp +++ b/lib/Solver/ConcretizingSolver.cpp @@ -253,7 +253,7 @@ bool ConcretizingSolver::relaxSymcreteConstraints(const Query &query, Query(queryConstraints, UgtExpr::create( symbolicSizesSum, - ConstantExpr::create(SymbolicAllocationThreshhold, + ConstantExpr::create(SymbolicAllocationThreshold, symbolicSizesSum->getWidth()))), response)) { return false; 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)); From 3f1bbcce97b12d20cabd76370f18329828ae3c2e Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Fri, 14 Jul 2023 12:50:30 +0400 Subject: [PATCH 36/45] [feat] Add `TargetManager` [feat] Rework `TargetedSearcher` and `GuidedSearcher` --- configs/options.json | 2 +- include/klee/Core/Interpreter.h | 4 +- include/klee/Core/TargetedExecutionReporter.h | 2 +- include/klee/Core/TerminationTypes.h | 7 +- include/klee/Expr/Expr.h | 2 +- include/klee/Module/SarifReport.h | 4 +- include/klee/Module/Target.h | 62 +- include/klee/Module/TargetForest.h | 329 +++++----- include/klee/Module/TargetHash.h | 24 +- lib/Core/CMakeLists.txt | 1 + lib/Core/DistanceCalculator.cpp | 134 ++-- lib/Core/DistanceCalculator.h | 91 ++- lib/Core/ExecutionState.cpp | 70 +- lib/Core/ExecutionState.h | 32 +- lib/Core/Executor.cpp | 145 ++-- lib/Core/Executor.h | 4 +- lib/Core/Searcher.cpp | 619 +++++------------- lib/Core/Searcher.h | 108 +-- lib/Core/TargetManager.cpp | 134 ++++ lib/Core/TargetManager.h | 105 +++ lib/Core/TargetedExecutionManager.cpp | 68 +- lib/Core/TargetedExecutionManager.h | 40 +- lib/Core/TargetedExecutionReporter.cpp | 5 +- lib/Core/UserSearcher.cpp | 14 +- lib/Expr/Expr.cpp | 2 +- lib/Module/SarifReport.cpp | 6 +- lib/Module/Target.cpp | 48 +- lib/Module/TargetForest.cpp | 193 +++--- lib/Module/TargetHash.cpp | 25 +- lib/Runner/run_klee.cpp | 18 +- test/Feature/MemoryLimit.c | 4 +- test/Feature/MultipleReallocResolution.c | 2 +- test/Feature/RecursionPruning/sum.c | 4 +- ...Var_Alloc_in_Loop_Exit_in_Loop_BadCase01.c | 2 +- test/Industry/NullReturn_Scene_BadCase06.c | 4 +- test/Industry/fp_forward_null_address.c | 2 +- test/Industry/if2.c | 2 +- test/Industry/while_true.c | 4 +- 38 files changed, 1246 insertions(+), 1076 deletions(-) create mode 100644 lib/Core/TargetManager.cpp create mode 100644 lib/Core/TargetManager.h diff --git a/configs/options.json b/configs/options.json index 03a3849384..32f20ebbf7 100644 --- a/configs/options.json +++ b/configs/options.json @@ -41,7 +41,7 @@ "--use-lazy-initialization=only", "--min-number-elements-li=${minNumberElements}", "--use-sym-size-li=${useSymbolicSizeLI}", - "--max-cycles=${maxCycles}", + "--max-cycles-before-stuck=${maxCycles}", "--rewrite-equalities=simple", "--symbolic-allocation-threshold=${SymbolicAllocationThreshold}", "--analysis-reproduce=${sarifTracesFilePath}", diff --git a/include/klee/Core/Interpreter.h b/include/klee/Core/Interpreter.h index a7cde8600a..2a21d29256 100644 --- a/include/klee/Core/Interpreter.h +++ b/include/klee/Core/Interpreter.h @@ -57,8 +57,8 @@ class InterpreterHandler { virtual void incPathsCompleted() = 0; virtual void incPathsExplored(std::uint32_t num = 1) = 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; }; 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 72132b109d..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") \ @@ -34,8 +36,7 @@ TTYPE(ReportError, 18U, "report_error.err") \ TTYPE(UndefinedBehavior, 19U, "undefined_behavior.err") \ TTYPE(InternalOutOfMemory, 20U, "out_of_memory.err") \ - TTYPE(MissedAllTargets, 21U, "miss_all_targets.err") \ - MARK(PROGERR, 21U) \ + MARK(PROGERR, 20U) \ TTYPE(User, 23U, "user.err") \ MARK(USERERR, 23U) \ TTYPE(Execution, 25U, "exec.err") \ diff --git a/include/klee/Expr/Expr.h b/include/klee/Expr/Expr.h index 8ce01335f5..cc4c67cdfa 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; 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 92fdd3c424..b06f40379d 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,54 @@ 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: + struct TargetHash { + unsigned operator()(Target *const t) const { return t->hash(); } + }; - static ref getFromCacheOrReturn(Target *target); + struct TargetCmp { + bool operator()(Target *const a, Target *const b) const { + return a->equals(*b); + } + }; + + 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; 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 +111,16 @@ 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 diff --git a/include/klee/Module/TargetForest.h b/include/klee/Module/TargetForest.h index 8476e5aa61..78ec719a6a 100644 --- a/include/klee/Module/TargetForest.h +++ b/include/klee/Module/TargetForest.h @@ -26,32 +26,116 @@ #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: + 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; -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(); + /// @brief Required by klee::ref-managed objects + class ReferenceCounter _refCount; +}; + +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 +150,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 +231,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 +244,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 +269,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 +278,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 +299,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,84 +308,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; - unsigned sizeValue; - - explicit History(ref _target, ref _visitedTargets) - : target(_target), visitedTargets(_visitedTargets) { - computeHash(); - } - - 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; - } - - 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; } - unsigned size() const { return sizeValue; } - - int compare(const History &h) const; - bool equals(const History &h) const; - - 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( @@ -292,7 +325,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) @@ -312,14 +345,12 @@ class TargetForest { void remove(ref); void blockIn(ref, ref); void block(const ref &); - const ref getHistory() { return history; }; - const ref getTargets() { return forest; }; + 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); @@ -327,45 +358,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 a484b521a7..2ceb35c747 100644 --- a/include/klee/Module/TargetHash.h +++ b/include/klee/Module/TargetHash.h @@ -24,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; }; @@ -49,18 +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 -class TargetHashMap - : public std::unordered_map, T, RefTargetHash, RefTargetCmp> {}; +using TargetHashMap = std::unordered_map, T, TargetHash, TargetCmp>; -class TargetHashSet - : public std::unordered_set, RefTargetHash, RefTargetCmp> {}; +using TargetHashSet = std::unordered_set, TargetHash, TargetCmp>; } // namespace klee #endif /* KLEE_TARGETHASH_H */ diff --git a/lib/Core/CMakeLists.txt b/lib/Core/CMakeLists.txt index b064c749f3..f6a7144b5f 100644 --- a/lib/Core/CMakeLists.txt +++ b/lib/Core/CMakeLists.txt @@ -30,6 +30,7 @@ klee_add_component(kleeCore TargetCalculator.cpp TargetedExecutionReporter.cpp TargetedExecutionManager.cpp + TargetManager.cpp TimingSolver.cpp TypeManager.cpp UserSearcher.cpp diff --git a/lib/Core/DistanceCalculator.cpp b/lib/Core/DistanceCalculator.cpp index 44c2f26e72..cf0cd6a869 100644 --- a/lib/Core/DistanceCalculator.cpp +++ b/lib/Core/DistanceCalculator.cpp @@ -33,21 +33,76 @@ std::string DistanceResult::toString() const { return out.str(); } -DistanceResult DistanceCalculator::getDistance(ExecutionState &es, +unsigned DistanceCalculator::SpeculativeState::computeHash() { + unsigned res = + (reinterpret_cast(pc) * 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.frames, state.error, target); +} + +DistanceResult DistanceCalculator::getDistance(const KInstruction *pc, + TargetKind kind, + ReachWithError error, ref target) { - return getDistance(es.pc, es.prevPC, es.initPC, es.stack, es.error, target); + const KInstruction *ki = pc; + SpeculativeState specState(pc, kind, error); + if (distanceResultCache.count(target) == 0 || + distanceResultCache.at(target).count(specState) == 0) { + auto result = computeDistance(pc, kind, error, target); + distanceResultCache[target][specState] = result; + } + return distanceResultCache.at(target).at(specState); +} + +DistanceResult DistanceCalculator::computeDistance(const KInstruction *pc, + 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(pc, weight, target); + break; + + case PreTarget: + res = tryGetPreTargetWeight(pc, weight, distanceToTargetFunction, target); + isInsideFunction = false; + break; + + case PostTarget: + res = tryGetPostTargetWeight(pc, 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(KInstruction *pc, KInstruction *prevPC, - KInstruction *initPC, - const ExecutionState::stack_ty &stack, +DistanceCalculator::getDistance(const KInstruction *prevPC, + const KInstruction *pc, + const ExecutionState::frames_ty &frames, ReachWithError error, ref target) { weight_type weight = 0; - BasicBlock *pcBlock = pc->inst->getParent(); - BasicBlock *prevPCBlock = prevPC->inst->getParent(); - if (!target->shouldFailOnThisTarget() && target->atReturn()) { if (prevPC->parent == target->getBlock() && prevPC == target->getBlock()->getLastInstruction()) { @@ -62,12 +117,11 @@ DistanceCalculator::getDistance(KInstruction *pc, KInstruction *prevPC, return DistanceResult(Done); } - BasicBlock *bb = pcBlock; - KBlock *kb = pc->parent->parent->blockMap[bb]; + 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 = stack.rbegin(), sfe = stack.rend(); sfi != sfe; sfi++) { + for (auto sfi = frames.rbegin(), sfe = frames.rend(); sfi != sfe; sfi++) { unsigned callWeight; if (distanceInCallGraph(sfi->kf, kb, callWeight, distanceToTargetFunction, target)) { @@ -82,7 +136,6 @@ DistanceCalculator::getDistance(KInstruction *pc, KInstruction *prevPC, if (sfi->caller) { kb = sfi->caller->parent; - bb = kb->basicBlock; } sfNum++; @@ -90,33 +143,23 @@ DistanceCalculator::getDistance(KInstruction *pc, KInstruction *prevPC, break; } - WeightResult res = Miss; - bool isInsideFunction = true; + TargetKind kind = NoneTarget; if (minCallWeight == 0) { - res = tryGetTargetWeight(pc, initPC, pcBlock, prevPCBlock, weight, target); + kind = LocalTarget; } else if (minSfNum == 0) { - res = tryGetPreTargetWeight(pc, initPC, pcBlock, prevPCBlock, weight, - distanceToTargetFunction, target); - isInsideFunction = false; + kind = PreTarget; } else if (minSfNum != UINT_MAX) { - res = tryGetPostTargetWeight(pc, initPC, pcBlock, prevPCBlock, weight, - target); - isInsideFunction = false; - } - if (Done == res && target->shouldFailOnThisTarget()) { - if (!target->isThatError(error)) { - res = Continue; - } + kind = PostTarget; } - return DistanceResult(res, weight, isInsideFunction); + return getDistance(pc, kind, error, target); } bool DistanceCalculator::distanceInCallGraph( KFunction *kf, KBlock *kb, unsigned int &distance, const std::unordered_map &distanceToTargetFunction, - ref target) { + ref target) const { distance = UINT_MAX; const std::unordered_map &dist = codeGraphDistance.getDistance(kb); @@ -143,13 +186,10 @@ bool DistanceCalculator::distanceInCallGraph( } WeightResult DistanceCalculator::tryGetLocalWeight( - KInstruction *pc, KInstruction *initPC, BasicBlock *pcBlock, - BasicBlock *prevPCBlock, weight_type &weight, - const std::vector &localTargets, ref target) { + const KInstruction *pc, weight_type &weight, + const std::vector &localTargets, ref target) const { KFunction *currentKF = pc->parent->parent; - KBlock *initKB = initPC->parent; - KBlock *currentKB = currentKF->blockMap[pcBlock]; - KBlock *prevKB = currentKF->blockMap[prevPCBlock]; + KBlock *currentKB = pc->parent; const std::unordered_map &dist = codeGraphDistance.getDistance(currentKB); weight = UINT_MAX; @@ -162,8 +202,7 @@ WeightResult DistanceCalculator::tryGetLocalWeight( if (weight == UINT_MAX) return Miss; - if (weight == 0 && (initKB == currentKB || prevKB != currentKB || - target->shouldFailOnThisTarget())) { + if (weight == 0) { return Done; } @@ -171,11 +210,10 @@ WeightResult DistanceCalculator::tryGetLocalWeight( } WeightResult DistanceCalculator::tryGetPreTargetWeight( - KInstruction *pc, KInstruction *initPC, BasicBlock *pcBlock, - BasicBlock *prevPCBlock, weight_type &weight, + const KInstruction *pc, weight_type &weight, const std::unordered_map &distanceToTargetFunction, - ref target) { + ref target) const { KFunction *currentKF = pc->parent->parent; std::vector localTargets; for (auto &kCallBlock : currentKF->kCallBlocks) { @@ -191,30 +229,26 @@ WeightResult DistanceCalculator::tryGetPreTargetWeight( if (localTargets.empty()) return Miss; - WeightResult res = tryGetLocalWeight(pc, initPC, pcBlock, prevPCBlock, weight, - localTargets, target); + WeightResult res = tryGetLocalWeight(pc, weight, localTargets, target); return res == Done ? Continue : res; } WeightResult DistanceCalculator::tryGetPostTargetWeight( - KInstruction *pc, KInstruction *initPC, BasicBlock *pcBlock, - BasicBlock *prevPCBlock, weight_type &weight, ref target) { + const KInstruction *pc, weight_type &weight, ref target) const { KFunction *currentKF = pc->parent->parent; std::vector &localTargets = currentKF->returnKBlocks; if (localTargets.empty()) return Miss; - WeightResult res = tryGetLocalWeight(pc, initPC, pcBlock, prevPCBlock, weight, - localTargets, target); + WeightResult res = tryGetLocalWeight(pc, weight, localTargets, target); return res == Done ? Continue : res; } -WeightResult DistanceCalculator::tryGetTargetWeight( - KInstruction *pc, KInstruction *initPC, BasicBlock *pcBlock, - BasicBlock *prevPCBlock, weight_type &weight, ref target) { +WeightResult DistanceCalculator::tryGetTargetWeight(const KInstruction *pc, + weight_type &weight, + ref target) const { std::vector localTargets = {target->getBlock()}; - WeightResult res = tryGetLocalWeight(pc, initPC, pcBlock, prevPCBlock, weight, - localTargets, target); + WeightResult res = tryGetLocalWeight(pc, weight, localTargets, target); return res; } diff --git a/lib/Core/DistanceCalculator.h b/lib/Core/DistanceCalculator.h index eec14f8881..7058860792 100644 --- a/lib/Core/DistanceCalculator.h +++ b/lib/Core/DistanceCalculator.h @@ -48,40 +48,87 @@ class DistanceCalculator { explicit DistanceCalculator(CodeGraphDistance &codeGraphDistance_) : codeGraphDistance(codeGraphDistance_) {} - DistanceResult getDistance(ExecutionState &es, ref target); - DistanceResult getDistance(KInstruction *pc, KInstruction *prevPC, - KInstruction *initPC, - const ExecutionState::stack_ty &stack, + DistanceResult getDistance(const ExecutionState &es, ref target); + + DistanceResult getDistance(const KInstruction *prevPC, const KInstruction *pc, + const ExecutionState::frames_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: + const KInstruction *pc; + TargetKind kind; + ReachWithError error; + SpeculativeState(const KInstruction *pc_, TargetKind kind_, + ReachWithError error_) + : pc(pc_), 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.pc == b.pc && 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(const KInstruction *pc, TargetKind kind, + ReachWithError error, ref target); + + DistanceResult computeDistance(const KInstruction *pc, TargetKind kind, + ReachWithError error, + ref target) const; bool distanceInCallGraph(KFunction *kf, KBlock *kb, unsigned int &distance, const std::unordered_map &distanceToTargetFunction, - ref target); - WeightResult tryGetLocalWeight(KInstruction *pc, KInstruction *initPC, - llvm::BasicBlock *pcBlock, - llvm::BasicBlock *prevPCBlock, - weight_type &weight, + ref target) const; + WeightResult tryGetLocalWeight(const KInstruction *pc, weight_type &weight, const std::vector &localTargets, - ref target); + ref target) const; WeightResult - tryGetPreTargetWeight(KInstruction *pc, KInstruction *initPC, - llvm::BasicBlock *pcBlock, - llvm::BasicBlock *prevPCBlock, weight_type &weight, + tryGetPreTargetWeight(const KInstruction *pc, weight_type &weight, const std::unordered_map &distanceToTargetFunction, - ref target); - WeightResult tryGetTargetWeight(KInstruction *pc, KInstruction *initPC, - llvm::BasicBlock *pcBlock, - llvm::BasicBlock *prevPCBlock, - weight_type &weight, ref target); - WeightResult tryGetPostTargetWeight(KInstruction *pc, KInstruction *initPC, - llvm::BasicBlock *pcBlock, - llvm::BasicBlock *prevPCBlock, - weight_type &weight, ref target); + ref target) const; + WeightResult tryGetTargetWeight(const KInstruction *pc, weight_type &weight, + ref target) const; + WeightResult tryGetPostTargetWeight(const KInstruction *pc, + weight_type &weight, + ref target) const; }; } // namespace klee diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index 38012e861e..c9ec981820 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -42,6 +42,12 @@ cl::opt UseGEPOptimization( cl::desc("Lazily initialize whole objects referenced by gep expressions " "instead of only the referenced parts (default=true)"), cl::cat(ExecCat)); + +cl::opt MaxCyclesBeforeStuck( + "max-cycles-before-stuck", + cl::desc("Set target after after state visiting some basic block this " + "amount of times (default=1)."), + cl::init(1), cl::cat(TerminationCat)); } // namespace /***/ @@ -50,6 +56,19 @@ std::uint32_t ExecutionState::nextID = 1; /***/ +int CallStackFrame::compare(const CallStackFrame &other) const { + if (kf != other.kf) { + return kf < other.kf ? -1 : 1; + } + if (!caller || !other.caller) { + return !caller ? !other.caller ? 0 : -1 : 1; + } + if (caller != other.caller) { + return caller < other.caller ? -1 : 1; + } + return 0; +} + StackFrame::StackFrame(KInstIterator _caller, KFunction *_kf) : caller(_caller), kf(_kf), callPathNode(0), minDistToUncoveredOnReturn(0), varargs(0) { @@ -71,24 +90,30 @@ StackFrame::~StackFrame() { delete[] locals; } /***/ ExecutionState::ExecutionState() : initPC(nullptr), pc(nullptr), prevPC(nullptr), incomingBBIndex(-1), - depth(0), ptreeNode(nullptr), steppedInstructions(0), - steppedMemoryInstructions(0), instsSinceCovNew(0), + depth(0), prevHistory(TargetsHistory::create()), + history(TargetsHistory::create()), ptreeNode(nullptr), + steppedInstructions(0), steppedMemoryInstructions(0), instsSinceCovNew(0), roundingMode(llvm::APFloat::rmNearestTiesToEven), coveredNew(false), forkDisabled(false) { 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), prevHistory(TargetsHistory::create()), + history(TargetsHistory::create()), ptreeNode(nullptr), + steppedInstructions(0), steppedMemoryInstructions(0), instsSinceCovNew(0), + roundingMode(llvm::APFloat::rmNearestTiesToEven), coveredNew(false), + forkDisabled(false) { pushFrame(nullptr, kf); setID(); } ExecutionState::ExecutionState(KFunction *kf, KBlock *kb) : initPC(kb->instructions), pc(initPC), prevPC(pc), incomingBBIndex(-1), - depth(0), ptreeNode(nullptr), steppedInstructions(0), - steppedMemoryInstructions(0), instsSinceCovNew(0), + depth(0), prevHistory(TargetsHistory::create()), + history(TargetsHistory::create()), ptreeNode(nullptr), + steppedInstructions(0), steppedMemoryInstructions(0), instsSinceCovNew(0), roundingMode(llvm::APFloat::rmNearestTiesToEven), coveredNew(false), forkDisabled(false) { pushFrame(nullptr, kf); @@ -102,14 +127,16 @@ 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), callStack(state.callStack), frames(state.frames), + stackBalance(state.stackBalance), incomingBBIndex(state.incomingBBIndex), + depth(state.depth), multilevel(state.multilevel), level(state.level), transitionLevel(state.transitionLevel), addressSpace(state.addressSpace), constraints(state.constraints), targetForest(state.targetForest), - pathOS(state.pathOS), symPathOS(state.symPathOS), - coveredLines(state.coveredLines), symbolics(state.symbolics), - resolvedPointers(state.resolvedPointers), + prevTargets(state.prevTargets), targets(state.targets), + prevHistory(state.prevHistory), history(state.history), + isTargeted(state.isTargeted), pathOS(state.pathOS), + symPathOS(state.symPathOS), coveredLines(state.coveredLines), + symbolics(state.symbolics), resolvedPointers(state.resolvedPointers), cexPreferences(state.cexPreferences), arrayNames(state.arrayNames), steppedInstructions(state.steppedInstructions), steppedMemoryInstructions(state.steppedMemoryInstructions), @@ -179,6 +206,11 @@ ExecutionState *ExecutionState::copy() const { void ExecutionState::pushFrame(KInstIterator caller, KFunction *kf) { stack.emplace_back(StackFrame(caller, kf)); + if (std::find(callStack.begin(), callStack.end(), + CallStackFrame(caller, kf)) == callStack.end()) { + frames.emplace_back(CallStackFrame(caller, kf)); + } + callStack.emplace_back(CallStackFrame(caller, kf)); ++stackBalance; } @@ -191,6 +223,12 @@ void ExecutionState::popFrame() { addressSpace.unbindObject(memoryObject); } stack.pop_back(); + callStack.pop_back(); + auto it = std::find(callStack.begin(), callStack.end(), + CallStackFrame(sf.caller, sf.kf)); + if (it == callStack.end()) { + frames.pop_back(); + } --stackBalance; } @@ -404,7 +442,7 @@ void ExecutionState::increaseLevel() { KModule *kmodule = kf->parent; if (prevPC->inst->isTerminator() && kmodule->inMainModule(kf->function)) { - multilevel[srcbb]++; + ++multilevel[srcbb]; level.insert(srcbb); } if (srcbb != dstbb) { @@ -432,3 +470,9 @@ bool ExecutionState::reachedTarget(Target target) const { return pc == target.getBlock()->getFirstInstruction(); } } + +bool ExecutionState::isStuck() { + KInstruction *prevKI = prevPC; + return (prevKI->inst->isTerminator() && + multilevel[getPCBlock()] > MaxCyclesBeforeStuck - 1); +} diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index d15170bbd5..71f596fc03 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -52,6 +52,25 @@ struct TranstionHash; llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const MemoryMap &mm); +struct CallStackFrame { + KInstruction *caller; + KFunction *kf; + + CallStackFrame(KInstruction *caller_, KFunction *kf_) + : caller(caller_), kf(kf_) {} + ~CallStackFrame() = default; + + int compare(const CallStackFrame &other) const; + + bool operator<(const CallStackFrame &other) const { + return compare(other) == -1; + } + + bool operator==(const CallStackFrame &other) const { + return compare(other) == 0; + } +}; + struct StackFrame { KInstIterator caller; KFunction *kf; @@ -204,8 +223,8 @@ class ExecutionState { public: using stack_ty = std::vector; - using TargetHashSet = - std::unordered_set, RefTargetHash, RefTargetCmp>; + using call_stack_ty = std::vector; + using frames_ty = std::vector; // Execution - Control Flow specific @@ -221,6 +240,8 @@ class ExecutionState { /// @brief Stack representing the current instruction stream stack_ty stack; + call_stack_ty callStack; + frames_ty frames; int stackBalance = 0; @@ -247,6 +268,12 @@ class ExecutionState { /// @brief Key points which should be visited through execution TargetForest targetForest; + TargetHashSet prevTargets; + TargetHashSet targets; + ref prevHistory; + ref history; + bool isTargeted = false; + bool areTargetsChanged = false; /// @brief Velocity and acceleration of this state investigating new blocks long long progressVelocity = 0; @@ -388,6 +415,7 @@ class ExecutionState { bool isGEPExpr(ref expr) const; bool reachedTarget(Target target) const; + bool isStuck(); }; struct ExecutionStateIDCompare { diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index c2780d603d..04867671bc 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -27,6 +27,7 @@ #include "SpecialFunctionHandler.h" #include "StatsTracker.h" #include "TargetCalculator.h" +#include "TargetManager.h" #include "TargetedExecutionManager.h" #include "TimingSolver.h" #include "TypeManager.h" @@ -459,10 +460,9 @@ Executor::Executor(LLVMContext &ctx, const InterpreterOptions &opts, concretizationManager(new ConcretizationManager(EqualitySubstitution)), codeGraphDistance(new CodeGraphDistance()), distanceCalculator(new DistanceCalculator(*codeGraphDistance)), - targetedExecutionManager(*codeGraphDistance), replayKTest(0), - replayPath(0), usingSeeds(0), atMemoryLimit(false), inhibitForking(false), - haltExecution(HaltExecution::NotHalt), ivcEnabled(false), - debugLogBuffer(debugBufferString) { + replayKTest(0), replayPath(0), usingSeeds(0), atMemoryLimit(false), + inhibitForking(false), haltExecution(HaltExecution::NotHalt), + ivcEnabled(false), debugLogBuffer(debugBufferString) { guidanceKind = opts.Guidance; @@ -603,6 +603,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(); } @@ -1083,12 +1090,12 @@ bool Executor::canReachSomeTargetFromBlock(ExecutionState &es, KBlock *block) { if (interpreterOpts.Guidance != GuidanceKind::ErrorGuidance) return true; auto nextInstr = block->getFirstInstruction(); - for (const auto &p : *es.targetForest.getTargets()) { + for (const auto &p : *es.targetForest.getTopLayer()) { auto target = p.first; if (target->mustVisitForkBranches(es.prevPC)) return true; - auto dist = distanceCalculator->getDistance(nextInstr, es.prevPC, es.initPC, - es.stack, es.error, target); + auto dist = distanceCalculator->getDistance(es.prevPC, nextInstr, es.frames, + es.error, target); if (dist.result != WeightResult::Miss) return true; } @@ -1569,7 +1576,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); @@ -3884,10 +3897,29 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { } void Executor::updateStates(ExecutionState *current) { + if (targetManager) { + targetManager->update(current, addedStates, removedStates); + } + if (guidanceKind == GuidanceKind::ErrorGuidance && targetedExecutionManager) { + targetedExecutionManager->update(current, addedStates, removedStates); + } + if (searcher) { searcher->update(current, addedStates, removedStates); } + if (current && (std::find(removedStates.begin(), removedStates.end(), + current) == removedStates.end())) { + current->prevHistory = current->history; + current->prevTargets = current->targets; + current->areTargetsChanged = false; + } + for (const auto state : addedStates) { + state->prevHistory = state->history; + state->prevTargets = state->targets; + state->areTargetsChanged = false; + } + states.insert(addedStates.begin(), addedStates.end()); addedStates.clear(); @@ -4030,9 +4062,12 @@ bool Executor::checkMemoryUsage() { } bool Executor::decreaseConfidenceFromStoppedStates( - SetOfStates &left_states, HaltExecution::Reason reason) { + SetOfStates &leftStates, HaltExecution::Reason reason) { + if (targets.size() == 0) { + return false; + } bool hasStateWhichCanReachSomeTarget = false; - for (auto state : left_states) { + for (auto state : leftStates) { if (state->targetForest.empty()) continue; hasStateWhichCanReachSomeTarget = true; @@ -4156,7 +4191,7 @@ void Executor::reportProgressTowardsTargets(std::string prefix, klee_message("%zu %sstates remaining", states.size(), prefix.c_str()); TargetHashMap distancesTowardsTargets; for (auto &state : states) { - for (auto &p : *state->targetForest.getTargets()) { + for (auto &p : *state->targetForest.getTopLayer()) { auto target = p.first; auto distance = distanceCalculator->getDistance(*state, target); auto it = distancesTowardsTargets.find(target); @@ -4202,7 +4237,14 @@ void Executor::run(std::vector initialStates) { searcher = constructUserSearcher(*this); std::vector newStates(states.begin(), states.end()); + targetManager->update(0, newStates, std::vector()); + targetedExecutionManager->update(0, newStates, + std::vector()); searcher->update(0, newStates, std::vector()); + for (auto state : newStates) { + state->prevHistory = state->history; + state->prevTargets = state->targets; + } // main interpreter loop while (!states.empty() && !haltExecution) { @@ -4213,6 +4255,13 @@ void Executor::run(std::vector initialStates) { if (prevKI->inst->isTerminator() && kmodule->inMainModule(kf->function)) { targetCalculator->update(state); + auto target = Target::create(state.prevPC->parent); + if (guidanceKind == GuidanceKind::CoverageGuidance) { + if (!target->atReturn() || + state.prevPC == target->getBlock()->getLastInstruction()) { + targetManager->setReached(target); + } + } } executeStep(state); @@ -4223,20 +4272,12 @@ void Executor::run(std::vector initialStates) { } if (guidanceKind == GuidanceKind::ErrorGuidance) { - SetOfStates leftState = states; - for (auto state : pausedStates) { - leftState.erase(state); - } - reportProgressTowardsTargets(); - bool canReachNew1 = decreaseConfidenceFromStoppedStates( - pausedStates, HaltExecution::MaxCycles); - bool canReachNew2 = - decreaseConfidenceFromStoppedStates(leftState, haltExecution); + bool canReachNew = + decreaseConfidenceFromStoppedStates(states, haltExecution); for (auto &startBlockAndWhiteList : targets) { - startBlockAndWhiteList.second.reportFalsePositives(canReachNew1 || - canReachNew2); + startBlockAndWhiteList.second.reportFalsePositives(canReachNew); } if (searcher->empty()) @@ -4284,10 +4325,20 @@ void Executor::initializeTypeManager() { } void Executor::executeStep(ExecutionState &state) { - KInstruction *ki = state.pc; - stepInstruction(state); + KInstruction *prevKI = state.prevPC; + if (targetManager->isTargeted(state) && state.targets.empty()) { + terminateStateEarly(state, "State missed all it's targets.", + StateTerminationType::MissedAllTargets); + } else if (prevKI->inst->isTerminator() && + state.multilevel[state.getPCBlock()] > MaxCycles - 1) { + terminateStateEarly(state, "max-cycles exceeded.", + StateTerminationType::MaxCycles); + } else { + KInstruction *ki = state.pc; + stepInstruction(state); + executeInstruction(state, ki); + } - executeInstruction(state, ki); timers.invoke(); if (::dumpStates) dumpStates(); @@ -4416,6 +4467,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; } @@ -4424,6 +4479,11 @@ 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."); @@ -4478,7 +4538,9 @@ void Executor::terminateStateEarly(ExecutionState &state, const Twine &message, (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); } @@ -4550,9 +4612,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); } } } @@ -4639,7 +4701,8 @@ 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); @@ -4658,8 +4721,6 @@ void Executor::terminateStateOnExecError(ExecutionState &state, void Executor::terminateStateOnSolverError(ExecutionState &state, const llvm::Twine &message) { terminateStateOnError(state, message, StateTerminationType::Solver, ""); - SetOfStates states = {&state}; - decreaseConfidenceFromStoppedStates(states, HaltExecution::MaxSolverTime); } // XXX shoot me @@ -6434,7 +6495,7 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv, } auto &paths = interpreterOpts.Paths.value(); - auto prepTargets = targetedExecutionManager.prepareTargets( + auto prepTargets = targetedExecutionManager->prepareTargets( kmodule.get(), std::move(paths)); if (prepTargets.empty()) { klee_warning( @@ -6519,6 +6580,11 @@ ExecutionState *Executor::prepareStateForPOSIX(KInstIterator &caller, 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) { @@ -6542,6 +6608,11 @@ ExecutionState *Executor::prepareStateForPOSIX(KInstIterator &caller, void Executor::prepareTargetedExecution(ExecutionState *initialState, ref whitelist) { initialState->targetForest = *whitelist->deepCopy(); + initialState->isTargeted = true; + initialState->history = initialState->targetForest.getHistory(); + initialState->targets = initialState->targetForest.getTargets(); + initialState->prevHistory = initialState->history; + initialState->prevTargets = initialState->targets; } bool isReturnValueFromInitBlock(const ExecutionState &state, @@ -6753,14 +6824,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; } @@ -6793,7 +6864,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); diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index ac9165f9f4..8c565d1080 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -91,6 +91,7 @@ class SpecialFunctionHandler; struct StackFrame; class SymbolicSource; class TargetCalculator; +class TargetManager; class StatsTracker; class TimingSolver; class TreeStreamWriter; @@ -145,6 +146,7 @@ class Executor : public Interpreter { std::unique_ptr codeGraphDistance; std::unique_ptr distanceCalculator; std::unique_ptr targetCalculator; + std::unique_ptr targetManager; /// Used to track states that have been added during the current /// instructions step. @@ -178,7 +180,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. diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp index 91e9f67e0c..7271c5352e 100644 --- a/lib/Core/Searcher.cpp +++ b/lib/Core/Searcher.cpp @@ -148,6 +148,14 @@ static unsigned int ulog2(unsigned int val) { /// +TargetedSearcher::~TargetedSearcher() {} + +bool TargetedSearcher::empty() { return states->empty(); } + +void TargetedSearcher::printName(llvm::raw_ostream &os) { + os << "TargetedSearcher"; +} + TargetedSearcher::TargetedSearcher(ref target, DistanceCalculator &_distanceCalculator) : states(std::make_unique< @@ -156,555 +164,222 @@ TargetedSearcher::TargetedSearcher(ref target, ExecutionState &TargetedSearcher::selectState() { return *states->choose(0); } -WeightResult TargetedSearcher::tryGetWeight(ExecutionState *es, - weight_type &weight) { - 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; - } - - auto distRes = distanceCalculator.getDistance(*es, target); - weight = ulog2(distRes.weight + es->steppedMemoryInstructions); // [0, 32] - if (!distRes.isInsideFunction) { - weight += 32; // [32, 64] - } - - return distRes.result; -} - 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( - DistanceCalculator &distanceCalculator, TargetCalculator &stateHistory, - std::set &pausedStates, - unsigned long long bound, RNG &rng, Searcher *baseSearcher) - : baseSearcher(baseSearcher), distanceCalculator(distanceCalculator), - 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[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 (ErrorGuidance == guidance) { - if (!removedStates.empty()) { - std::vector alt = removedStates; - 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); - } - } - } - } - } - 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(); + if (!isThereTarget(history, target)) { + addTarget(history, target); } - } - 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); + } + for (auto target : targets) { + localHistoryTargets.insert({history, target}); + addedTStates[{history, target}].push_back(state); + } + } else { + for (auto target : prevTargets) { + if (!targets.count(target)) { + removedTargets.insert(target); } } - } - - 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) { + if (!prevTargets.count(target)) { + addedTargets.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(); +bool GuidedSearcher::isThereTarget(ref history, + ref target) { + return targetedSearchers.count({history, target}) != 0; } -void GuidedSearcher::printName(llvm::raw_ostream &os) { - os << "GuidedSearcher\n"; -} - -bool GuidedSearcher::isReached(ref history, +void GuidedSearcher::addTarget(ref history, ref target) { - return reachedTargets.count(target) != 0; -} - -bool GuidedSearcher::tryAddTarget(ref history, - ref target) { - if (isReached(history, target)) { - return false; - } - assert(targetedSearchers.count(history) == 0 || - targetedSearchers.at(history).count(target) == 0); - targetedSearchers[history][target] = + assert(targetedSearchers.count({history, target}) == 0); + targetedSearchers[{history, target}] = std::make_unique(target, distanceCalculator); - 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()); + 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}); - return true; } -GuidedSearcher::TargetForestHisoryTargetVector::iterator -GuidedSearcher::removeTarget(ref history, - ref target) { - targetedSearchers.at(history).erase(target); +void GuidedSearcher::removeTarget(ref history, + ref target) { + targetedSearchers.erase({history, target}); auto it = std::find_if( historiesAndTargets.begin(), historiesAndTargets.end(), - [&history, &target]( - const std::pair, ref> &element) { + [&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); + historiesAndTargets.erase(it); +} + +bool GuidedSearcher::empty() { return baseSearcher->empty(); } + +void GuidedSearcher::printName(llvm::raw_ostream &os) { + os << "GuidedSearcher\n"; } /// diff --git a/lib/Core/Searcher.h b/lib/Core/Searcher.h index 2951b62dd8..0c7bee33f5 100644 --- a/lib/Core/Searcher.h +++ b/lib/Core/Searcher.h @@ -14,6 +14,8 @@ #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" @@ -41,7 +43,6 @@ template class WeightedQueue; class ExecutionState; class TargetCalculator; class TargetForest; -class TargetReachability; /// A Searcher implements an exploration strategy for the Executor by selecting /// states for further exploration using different strategies or heuristics. @@ -128,16 +129,14 @@ class RandomSearcher final : public Searcher { void printName(llvm::raw_ostream &os) override; }; -/// TargetedSearcher picks a state /*COMMENT*/. class TargetedSearcher final : public Searcher { private: std::unique_ptr> states; ref target; DistanceCalculator &distanceCalculator; - std::set reachedOnLastUpdate; - WeightResult tryGetWeight(ExecutionState *es, weight_type &weight); + weight_type getWeight(ExecutionState *es); public: TargetedSearcher(ref target, DistanceCalculator &distanceCalculator); @@ -147,99 +146,62 @@ class TargetedSearcher final : public Searcher { 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 { public: - using TargetToStateUnorderedSetMap = - TargetHashMap>; - - 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>>; + 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: - enum Guidance { CoverageGuidance, ErrorGuidance }; - - Guidance guidance; std::unique_ptr baseSearcher; - TargetForestHistoryToSearcherMap targetedSearchers; + TargetHistoryTargetPairToSearcherMap targetedSearchers; DistanceCalculator &distanceCalculator; - TargetCalculator &stateHistory; - TargetHashSet reachedTargets; - std::set &pausedStates; - unsigned long long bound; 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( - DistanceCalculator &distanceCalculator, TargetCalculator &stateHistory, - std::set &pausedStates, - unsigned long long 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/TargetManager.cpp b/lib/Core/TargetManager.cpp new file mode 100644 index 0000000000..14f3af4e76 --- /dev/null +++ b/lib/Core/TargetManager.cpp @@ -0,0 +1,134 @@ +//===-- 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()); + state.areTargetsChanged = true; + if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { + state.isTargeted = 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()); + state.areTargetsChanged = true; + if (guidance == Interpreter::GuidanceKind::CoverageGuidance || + target->shouldFailOnThisTarget()) { + reachedTargets.insert(target); + for (auto es : states) { + if (isTargeted(*es)) { + auto &esTargetForest = targetForest(*es); + esTargetForest.block(target); + es->areTargetsChanged = true; + if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { + if (targets(*es).size() == 0) { + es->isTargeted = false; + } + } + setTargets(*es, esTargetForest.getTargets()); + } + } + } + if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { + state.isTargeted = false; + } +} + +void TargetManager::updateTargets(ExecutionState &state) { + if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { + if (targets(state).empty() && state.isStuck()) { + state.isTargeted = true; + } + if (isTargeted(state) && !targets(state).empty()) { + ref target(targetCalculator.calculate(state)); + if (target) { + state.targetForest.add(target); + state.areTargetsChanged = true; + 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) { + updateTargets(*state); + } + + localStates.clear(); +} \ No newline at end of file diff --git a/lib/Core/TargetManager.h b/lib/Core/TargetManager.h new file mode 100644 index 0000000000..cfc173a145 --- /dev/null +++ b/lib/Core/TargetManager.h @@ -0,0 +1,105 @@ +//===-- 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 "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 { +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.targets = targets; + } + + void setHistory(ExecutionState &state, ref history) { + state.history = history; + } + + void setPrevHistory(ExecutionState &state, ref history) { + state.prevHistory = history; + } + + void setPrevTargets(ExecutionState &state, const TargetHashSet &targets) { + state.prevTargets = targets; + } + + 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); + + 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 2aa1d615ee..7a775a1427 100644 --- a/lib/Core/TargetedExecutionManager.cpp +++ b/lib/Core/TargetedExecutionManager.cpp @@ -9,6 +9,9 @@ #include "TargetedExecutionManager.h" +#include "DistanceCalculator.h" +#include "TargetManager.h" + #include "ExecutionState.h" #include "klee/Core/TerminationTypes.h" #include "klee/Module/CodeGraphDistance.h" @@ -154,6 +157,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) { @@ -460,23 +469,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; } @@ -499,10 +509,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 @@ -536,7 +545,48 @@ 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(); +} \ No newline at end of file diff --git a/lib/Core/TargetedExecutionManager.h b/lib/Core/TargetedExecutionManager.h index 7488f5cc55..c16ae8e029 100644 --- a/lib/Core/TargetedExecutionManager.h +++ b/lib/Core/TargetedExecutionManager.h @@ -22,6 +22,9 @@ #include namespace klee { +class DistanceCalculator; +class TargetManager; + extern llvm::cl::OptionCategory TerminationCat; /*** Termination criteria options ***/ @@ -56,6 +59,8 @@ extern llvm::cl::opt MaxStaticPctCheckDelay; extern llvm::cl::opt TimerInterval; +extern llvm::cl::opt MaxCycles; + class CodeGraphDistance; class LocatedEventManager { @@ -78,8 +83,8 @@ class TargetedHaltsOnTraces { using TraceToHaltTypeToConfidence = std::unordered_map, HaltTypeToConfidence, - TargetForest::RefUnorderedTargetsSetHash, - TargetForest::RefUnorderedTargetsSetCmp>; + TargetForest::UnorderedTargetsSetHash, + TargetForest::UnorderedTargetsSetCmp>; TraceToHaltTypeToConfidence traceToHaltTypeToConfidence; static void totalConfidenceAndTopContributor( @@ -103,14 +108,17 @@ class TargetedExecutionManager { 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 +132,35 @@ 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); }; } // 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 72800087b9..07eb2ba2b5 100644 --- a/lib/Core/UserSearcher.cpp +++ b/lib/Core/UserSearcher.cpp @@ -76,11 +76,6 @@ cl::opt BatchTime( "--use-batching-search. Set to 0s to disable (default=5s)"), 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)); } // namespace void klee::initializeSearchOptions() { @@ -178,13 +173,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.distanceCalculator, *executor.targetCalculator, - executor.pausedStates, MaxCycles - 1, executor.theRNG, searcher); + searcher = new GuidedSearcher(searcher, *executor.distanceCalculator, + executor.theRNG); } llvm::raw_ostream &os = executor.getHandler().getInfoStream(); diff --git a/lib/Expr/Expr.cpp b/lib/Expr/Expr.cpp index 348034153d..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()); diff --git a/lib/Module/SarifReport.cpp b/lib/Module/SarifReport.cpp index 219866d601..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") { @@ -116,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 { @@ -169,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 99673cfcdb..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; } @@ -89,9 +87,6 @@ bool Target::mustVisitForkBranches(KInstruction *instr) const { } 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; } @@ -101,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; @@ -113,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 bc54d1949e..f512bf7cc5 100644 --- a/lib/Module/TargetForest.cpp +++ b/lib/Module/TargetForest.cpp @@ -20,10 +20,8 @@ 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) { @@ -32,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 = seed; + hashValue = res; + return res; +} + +int TargetForest::UnorderedTargetsSet::compare( + const TargetForest::UnorderedTargetsSet &other) const { + if (targetsVec != other.targetsVec) { + return targetsVec < other.targetsVec ? -1 : 1; + } + 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 @@ -56,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); } } @@ -92,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) { @@ -205,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) { @@ -218,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; } } @@ -345,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); @@ -373,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"; @@ -398,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 { @@ -465,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); } } @@ -487,7 +497,7 @@ void TargetForest::stepTo(ref loc) { loc->isReported = true; } if (forest->empty() && !loc->shouldFailOnThisTarget()) { - history = History::create(); + history = TargetsHistory::create(); } } @@ -521,16 +531,12 @@ void TargetForest::block(const ref &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 { @@ -607,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 f5bfd1a9ea..f95c09d8df 100644 --- a/lib/Runner/run_klee.cpp +++ b/lib/Runner/run_klee.cpp @@ -378,8 +378,8 @@ class KleeHandler : public InterpreterHandler { 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 @@ -540,8 +540,8 @@ 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; @@ -578,10 +578,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) { @@ -666,9 +666,9 @@ void KleeHandler::processTestCase(const ExecutionState &state, } } - if (errorMessage && OptExitOnError) { + if (isError && OptExitOnError) { m_interpreter->prepareForEarlyExit(); - klee_error("EXITING ON ERROR:\n%s\n", errorMessage); + klee_error("EXITING ON ERROR:\n%s\n", message); } } diff --git a/test/Feature/MemoryLimit.c b/test/Feature/MemoryLimit.c index f0522c4da8..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 --max-cycles=0 %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 --max-cycles=0 %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..feb01bce62 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/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/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 3e4990abbe..30ca2c7d88 100644 --- a/test/Industry/if2.c +++ b/test/Industry/if2.c @@ -11,7 +11,7 @@ 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 // RUN: FileCheck -input-file=%t.klee-out/messages.txt %s -check-prefix=CHECK-DISTANCE diff --git a/test/Industry/while_true.c b/test/Industry/while_true.c index 4b6cc204dc..e0adb538e7 100644 --- a/test/Industry/while_true.c +++ b/test/Industry/while_true.c @@ -11,14 +11,14 @@ 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 // RUN: FileCheck -input-file=%t.klee-out/messages.txt %s -check-prefix=CHECK-REACH-2 From 957bc6f2fb640bb5afd978936989cafa22b7f061 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Fri, 14 Jul 2023 12:54:45 +0400 Subject: [PATCH 37/45] [fix] Create a whole symbolic array for `MockMutableGlobalsPolicy::All` --- lib/Core/Executor.cpp | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 04867671bc..6fd4ae5c29 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -907,9 +907,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(); } @@ -5729,10 +5736,9 @@ void Executor::collectReads( ref result = os->read(mo->getOffsetExpr(address), type); - if (MockMutableGlobals != MockMutableGlobalsPolicy::None && mo->isGlobal && - !os->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); @@ -5890,10 +5896,9 @@ void Executor::executeMemoryOperation( if (interpreterOpts.MakeConcreteSymbolic) result = replaceReadWithSymbolic(*state, result); - if (MockMutableGlobals != MockMutableGlobalsPolicy::None && + if (MockMutableGlobals == MockMutableGlobalsPolicy::PrimitiveFields && mo->isGlobal && !os->readOnly && isa(result) && - (MockMutableGlobals != MockMutableGlobalsPolicy::PrimitiveFields || - !targetType->getRawType()->isPointerTy())) { + !targetType->getRawType()->isPointerTy()) { result = makeMockValue(*state, "mockGlobalValue", result->getWidth()); ObjectState *wos = state->addressSpace.getWriteable(mo, os); wos->write(mo->getOffsetExpr(address), result); @@ -6072,10 +6077,9 @@ void Executor::executeMemoryOperation( } else { ref result = os->read(mo->getOffsetExpr(address), type); - if (MockMutableGlobals != MockMutableGlobalsPolicy::None && + if (MockMutableGlobals == MockMutableGlobalsPolicy::PrimitiveFields && mo->isGlobal && !os->readOnly && isa(result) && - (MockMutableGlobals != MockMutableGlobalsPolicy::PrimitiveFields || - !targetType->getRawType()->isPointerTy())) { + !targetType->getRawType()->isPointerTy()) { result = makeMockValue(*bound, "mockGlobalValue", result->getWidth()); ObjectState *wos = bound->addressSpace.getWriteable(mo, os); wos->write(mo->getOffsetExpr(address), result); From 8c51eecdfd929817ca0593baa2075a029b169c84 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Fri, 14 Jul 2023 12:56:05 +0400 Subject: [PATCH 38/45] [clean] Remove `ConcretizationManager` as unnecessary --- include/klee/Expr/Assignment.h | 1 + include/klee/Solver/Common.h | 2 - include/klee/Solver/ConcretizationManager.h | 74 --------------------- include/klee/Solver/Solver.h | 2 - lib/Core/Executor.cpp | 72 ++++++++++++-------- lib/Core/Executor.h | 2 - lib/Core/TargetManager.cpp | 2 +- lib/Solver/CMakeLists.txt | 1 - lib/Solver/ConcretizationManager.cpp | 44 ------------ lib/Solver/ConcretizingSolver.cpp | 52 +-------------- lib/Solver/ConstructSolverChain.cpp | 7 +- tools/kleaver/main.cpp | 2 +- 12 files changed, 51 insertions(+), 210 deletions(-) delete mode 100644 include/klee/Solver/ConcretizationManager.h delete mode 100644 lib/Solver/ConcretizationManager.cpp 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/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..b8a7deff69 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" @@ -259,7 +258,6 @@ Solver *createDummySolver(); Solver *createCoreSolver(CoreSolverType cst); Solver *createConcretizingSolver(Solver *s, - ConcretizationManager *concretizationManager, AddressGenerator *addressGenerator); } // namespace klee diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 6fd4ae5c29..6ea48d6da9 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -56,7 +56,6 @@ #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" @@ -457,7 +456,6 @@ Executor::Executor(LLVMContext &ctx, const InterpreterOptions &opts, externalDispatcher(new ExternalDispatcher(ctx)), statsTracker(0), pathWriter(0), symPathWriter(0), specialFunctionHandler(0), timers{time::Span(TimerInterval)}, - concretizationManager(new ConcretizationManager(EqualitySubstitution)), codeGraphDistance(new CodeGraphDistance()), distanceCalculator(new DistanceCalculator(*codeGraphDistance)), replayKTest(0), replayPath(0), usingSeeds(0), atMemoryLimit(false), @@ -491,7 +489,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); @@ -1392,28 +1390,25 @@ void Executor::addConstraint(ExecutionState &state, ref condition) { klee_warning("seeds patched for violating constraint"); } - std::pair symcretization = - concretizationManager->get(state.constraints.cs(), condition); - - if (!symcretization.second && - Query(state.constraints.cs(), condition).containsSymcretes()) { - bool mayBeInBounds; + Assignment concretization; + if (Query(state.constraints.cs(), condition).containsSymcretes()) { + ref response; solver->setTimeout(coreSolverTimeout); - bool success = solver->mayBeTrue(state.constraints.cs(), condition, - mayBeInBounds, state.queryMetaData); + bool success = solver->getResponse(state.constraints.cs(), + Expr::createIsZero(condition), response, + state.queryMetaData); solver->setTimeout(time::Span()); assert(success); - assert(mayBeInBounds); - symcretization = - concretizationManager->get(state.constraints.cs(), condition); - assert(symcretization.second); + assert(isa(response)); + concretization = cast(response)->initialValuesFor( + state.constraints.cs().gatherSymcretizedArrays()); } - 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, {}); @@ -5700,14 +5695,21 @@ 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 symcretization; + if (Query(state.constraints.cs(), resolveConditions.at(i)) + .containsSymcretes()) { + ref response; + solver->setTimeout(coreSolverTimeout); + bool success = solver->getResponse( + state.constraints.cs(), Expr::createIsZero(resolveConditions.at(i)), + response, state.queryMetaData); + solver->setTimeout(time::Span()); + assert(success); + assert(isa(response)); + symcretization = cast(response)->initialValuesFor( + state.constraints.cs().gatherSymcretizedArrays()); } + resolveConcretizations.push_back(symcretization); } return true; } @@ -6912,10 +6914,22 @@ 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 symcretization; + if (Query(extendedConstraints.cs(), pi).containsSymcretes()) { + ref response; + solver->setTimeout(coreSolverTimeout); + bool success = solver->getResponse(extendedConstraints.cs(), + Expr::createIsZero(pi), response, + state.queryMetaData); + solver->setTimeout(time::Span()); + assert(success); + assert(isa(response)); + symcretization = cast(response)->initialValuesFor( + state.constraints.cs().gatherSymcretizedArrays()); + } + + if (!symcretization.isEmpty()) { + extendedConstraints.addConstraint(pi, symcretization); } else { extendedConstraints.addConstraint(pi, {}); } diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index 8c565d1080..56cea728ce 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -31,7 +31,6 @@ #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" @@ -141,7 +140,6 @@ class Executor : public Interpreter { TreeStreamWriter *pathWriter, *symPathWriter; SpecialFunctionHandler *specialFunctionHandler; TimerGroup timers; - std::unique_ptr concretizationManager; std::unique_ptr processForest; std::unique_ptr codeGraphDistance; std::unique_ptr distanceCalculator; diff --git a/lib/Core/TargetManager.cpp b/lib/Core/TargetManager.cpp index 14f3af4e76..bc52fa1d9c 100644 --- a/lib/Core/TargetManager.cpp +++ b/lib/Core/TargetManager.cpp @@ -66,7 +66,7 @@ void TargetManager::updateTargets(ExecutionState &state) { if (targets(state).empty() && state.isStuck()) { state.isTargeted = true; } - if (isTargeted(state) && !targets(state).empty()) { + if (isTargeted(state) && targets(state).empty()) { ref target(targetCalculator.calculate(state)); if (target) { state.targetForest.add(target); 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 f48cb2c1f7..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; } @@ -398,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; @@ -453,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; } @@ -470,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; @@ -510,10 +474,6 @@ bool ConcretizingSolver::computeTruth(const Query &query, bool &isValid) { } } - if (!isValid) { - concretizationManager->add(query.negateExpr(), assign); - } - return true; } @@ -560,7 +520,6 @@ bool ConcretizingSolver::computeValidityCore(const Query &query, if (!isValid) { validityCore = ValidityCore(); - concretizationManager->add(query.negateExpr(), assign); } return true; @@ -614,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; @@ -637,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/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(); From a40fed14afad745555ff004e769accd4051cf9cc Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Fri, 14 Jul 2023 12:56:20 +0400 Subject: [PATCH 39/45] [fix] Update tests --- test/Feature/MultipleReallocResolution.c | 2 +- test/Solver/CexCacheValidityCoresCheck.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/test/Feature/MultipleReallocResolution.c b/test/Feature/MultipleReallocResolution.c index feb01bce62..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 --use-guided-search=none--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/Solver/CexCacheValidityCoresCheck.c b/test/Solver/CexCacheValidityCoresCheck.c index a0d68efe75..82b1faa2f4 100644 --- a/test/Solver/CexCacheValidityCoresCheck.c +++ b/test/Solver/CexCacheValidityCoresCheck.c @@ -2,8 +2,8 @@ // 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 --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 @@ -30,6 +30,6 @@ int main(int argc, char **argv) { } } // CHECK-CACHE-ON: QCexCHits,Queries -// CHECK-CACHE-ON: 277,124 +// CHECK-CACHE-ON: 1461,202 // CHECK-CACHE-OFF: QCexCHits,Queries -// CHECK-CACHE-OFF: 226,175 +// CHECK-CACHE-OFF: 1011,652 From 8319e6b81377495627ad7f25af186b586acf7b22 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Sat, 15 Jul 2023 02:26:12 +0400 Subject: [PATCH 40/45] [refactor] Replace `stack_ty` --- lib/Core/DistanceCalculator.cpp | 5 +- lib/Core/DistanceCalculator.h | 2 +- lib/Core/ExecutionState.cpp | 104 ++++++++++++++------------ lib/Core/ExecutionState.h | 77 ++++++++++++------- lib/Core/Executor.cpp | 64 ++++++++-------- lib/Core/Executor.h | 4 +- lib/Core/Searcher.cpp | 4 +- lib/Core/SpecialFunctionHandler.cpp | 10 ++- lib/Core/StatsTracker.cpp | 36 ++++----- lib/Core/StatsTracker.h | 4 +- lib/Core/TargetCalculator.cpp | 5 +- lib/Core/TargetedExecutionManager.cpp | 2 +- 12 files changed, 181 insertions(+), 136 deletions(-) diff --git a/lib/Core/DistanceCalculator.cpp b/lib/Core/DistanceCalculator.cpp index cf0cd6a869..963a923181 100644 --- a/lib/Core/DistanceCalculator.cpp +++ b/lib/Core/DistanceCalculator.cpp @@ -44,7 +44,8 @@ unsigned DistanceCalculator::SpeculativeState::computeHash() { DistanceResult DistanceCalculator::getDistance(const ExecutionState &state, ref target) { - return getDistance(state.prevPC, state.pc, state.frames, state.error, target); + return getDistance(state.prevPC, state.pc, state.stack.uniqueFrames(), + state.error, target); } DistanceResult DistanceCalculator::getDistance(const KInstruction *pc, @@ -99,7 +100,7 @@ DistanceResult DistanceCalculator::computeDistance(const KInstruction *pc, DistanceResult DistanceCalculator::getDistance(const KInstruction *prevPC, const KInstruction *pc, - const ExecutionState::frames_ty &frames, + const ExecutionStack::call_stack_ty &frames, ReachWithError error, ref target) { weight_type weight = 0; diff --git a/lib/Core/DistanceCalculator.h b/lib/Core/DistanceCalculator.h index 7058860792..0e94bf8b30 100644 --- a/lib/Core/DistanceCalculator.h +++ b/lib/Core/DistanceCalculator.h @@ -51,7 +51,7 @@ class DistanceCalculator { DistanceResult getDistance(const ExecutionState &es, ref target); DistanceResult getDistance(const KInstruction *prevPC, const KInstruction *pc, - const ExecutionState::frames_ty &frames, + const ExecutionStack::call_stack_ty &frames, ReachWithError error, ref target); private: diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index c9ec981820..576434b86c 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -56,37 +56,62 @@ std::uint32_t ExecutionState::nextID = 1; /***/ -int CallStackFrame::compare(const CallStackFrame &other) const { - if (kf != other.kf) { - return kf < other.kf ? -1 : 1; +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)); } - if (!caller || !other.caller) { - return !caller ? !other.caller ? 0 : -1 : 1; - } - if (caller != other.caller) { - return caller < other.caller ? -1 : 1; + 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(); } - return 0; + --stackBalance; + assert(valueStack_.size() == callStack_.size()); + assert(valueStack_.size() == infoStack_.size()); } -StackFrame::StackFrame(KInstIterator _caller, KFunction *_kf) - : caller(_caller), kf(_kf), callPathNode(0), minDistToUncoveredOnReturn(0), - varargs(0) { +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), @@ -127,9 +152,9 @@ ExecutionState::~ExecutionState() { ExecutionState::ExecutionState(const ExecutionState &state) : initPC(state.initPC), pc(state.pc), prevPC(state.prevPC), - stack(state.stack), callStack(state.callStack), frames(state.frames), - stackBalance(state.stackBalance), incomingBBIndex(state.incomingBBIndex), - depth(state.depth), multilevel(state.multilevel), level(state.level), + stack(state.stack), stackBalance(state.stackBalance), + incomingBBIndex(state.incomingBBIndex), depth(state.depth), + multilevel(state.multilevel), level(state.level), transitionLevel(state.transitionLevel), addressSpace(state.addressSpace), constraints(state.constraints), targetForest(state.targetForest), prevTargets(state.prevTargets), targets(state.targets), @@ -205,31 +230,18 @@ ExecutionState *ExecutionState::copy() const { } void ExecutionState::pushFrame(KInstIterator caller, KFunction *kf) { - stack.emplace_back(StackFrame(caller, kf)); - if (std::find(callStack.begin(), callStack.end(), - CallStackFrame(caller, kf)) == callStack.end()) { - frames.emplace_back(CallStackFrame(caller, kf)); - } - callStack.emplace_back(CallStackFrame(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(); - callStack.pop_back(); - auto it = std::find(callStack.begin(), callStack.end(), - CallStackFrame(sf.caller, sf.kf)); - if (it == callStack.end()) { - frames.pop_back(); - } - --stackBalance; + stack.popFrame(); } void ExecutionState::addSymbolic(const MemoryObject *mo, const Array *array, @@ -380,15 +392,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') @@ -405,7 +417,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; } @@ -413,7 +425,7 @@ 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; } } diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index 71f596fc03..2719c03857 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -53,39 +53,24 @@ struct TranstionHash; llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const MemoryMap &mm); struct CallStackFrame { - KInstruction *caller; + KInstIterator caller; KFunction *kf; - CallStackFrame(KInstruction *caller_, KFunction *kf_) + CallStackFrame(KInstIterator caller_, KFunction *kf_) : caller(caller_), kf(kf_) {} ~CallStackFrame() = default; + CallStackFrame(const CallStackFrame &s); - int compare(const CallStackFrame &other) const; - - bool operator<(const CallStackFrame &other) const { - return compare(other) == -1; - } + bool equals(const CallStackFrame &other) const; - bool operator==(const CallStackFrame &other) const { - return compare(other) == 0; - } + bool operator==(const CallStackFrame &other) const { return equals(other); } }; struct StackFrame { - KInstIterator caller; KFunction *kf; - CallPathNode *callPathNode; - 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 @@ -93,11 +78,53 @@ 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_; + unsigned stackBalance = 0; + +public: + void pushFrame(KInstIterator caller, KFunction *kf); + void popFrame(); + value_stack_ty &valueStack() { return valueStack_; } + const value_stack_ty &valueStack() const { return valueStack_; } + const call_stack_ty &callStack() const { return callStack_; } + info_stack_ty &infoStack() { return infoStack_; } + const call_stack_ty &uniqueFrames() const { return uniqueFrames_; } + + unsigned size() const { return callStack_.size(); } + bool empty() const { return callStack_.empty(); } +}; + /// Contains information related to unwinding (Itanium ABI/2-Phase unwinding) class UnwindingInformation { public: @@ -222,9 +249,7 @@ class ExecutionState { ExecutionState(const ExecutionState &state); public: - using stack_ty = std::vector; - using call_stack_ty = std::vector; - using frames_ty = std::vector; + using stack_ty = ExecutionStack; // Execution - Control Flow specific @@ -238,10 +263,8 @@ class ExecutionState { /// @brief Pointer to instruction which is currently executed KInstIterator prevPC; - /// @brief Stack representing the current instruction stream + /// @brief Execution stack representing the current instruction stream stack_ty stack; - call_stack_ty callStack; - frames_ty frames; int stackBalance = 0; diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 6ea48d6da9..97118c99dc 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -1050,7 +1050,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. && @@ -1099,8 +1099,8 @@ bool Executor::canReachSomeTargetFromBlock(ExecutionState &es, KBlock *block) { auto target = p.first; if (target->mustVisitForkBranches(es.prevPC)) return true; - auto dist = distanceCalculator->getDistance(es.prevPC, nextInstr, es.frames, - es.error, target); + auto dist = distanceCalculator->getDistance( + es.prevPC, nextInstr, es.stack.uniqueFrames(), es.error, target); if (dist.result != WeightResult::Miss) return true; } @@ -1444,7 +1444,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, @@ -1753,7 +1753,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 { @@ -1761,7 +1761,7 @@ 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; @@ -1815,8 +1815,8 @@ void Executor::unwindToNextLandingpad(ExecutionState &state) { bindArgument(kf, 2, state, clauses_mo->getBaseExpr()); if (statsTracker) { - statsTracker->framePushed(state, - &state.stack[state.stack.size() - 2]); + statsTracker->framePushed( + state, &state.stack.infoStack()[state.stack.size() - 2]); } // make sure we remember our search progress afterwards @@ -2073,7 +2073,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) @@ -2159,7 +2159,8 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f, state); if (statsTracker) - statsTracker->framePushed(state, &state.stack[state.stack.size() - 2]); + statsTracker->framePushed( + state, &state.stack.infoStack()[state.stack.size() - 2]); // TODO: support zeroext, signext, sret attributes @@ -2244,7 +2245,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)); @@ -2338,7 +2339,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); } @@ -2387,7 +2388,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); @@ -2507,7 +2508,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { cond = optimizer.optimizeExpr(cond, false); - KFunction *kf = state.stack.back().kf; + KFunction *kf = state.stack.callStack().back().kf; auto ifTrueBlock = kf->blockMap[bi->getSuccessor(0)]; auto ifFalseBlock = kf->blockMap[bi->getSuccessor(1)]; Executor::StatePair branches = @@ -2518,7 +2519,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { // 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.stack.callStack().back().kf->trackCoverage) statsTracker->markBranchVisited(branches.first, branches.second); if (branches.first) @@ -2557,7 +2558,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { ref errorCase = ConstantExpr::alloc(1, Expr::Bool); SmallPtrSet destinations; - KFunction *kf = state.stack.back().kf; + KFunction *kf = state.stack.callStack().back().kf; // collect and check destinations from label list for (unsigned k = 0; k < numDestinations; ++k) { // filter duplicates @@ -2652,7 +2653,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { // Track default branch values ref defaultValue = ConstantExpr::alloc(1, Expr::Bool); - KFunction *kf = state.stack.back().kf; + KFunction *kf = state.stack.callStack().back().kf; // iterate through all non-default cases but in order of the expressions for (std::map, BasicBlock *>::iterator @@ -4565,8 +4566,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--; @@ -4693,7 +4695,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(); @@ -4988,7 +4990,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; } @@ -6223,7 +6225,8 @@ IDType Executor::lazyInitializeLocalObject(ExecutionState &state, 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, @@ -6594,7 +6597,7 @@ ExecutionState *Executor::prepareStateForPOSIX(KInstIterator &caller, targetedRun(*state, target, &initialState); state = initialState; if (state) { - auto frame = state->stack.back(); + auto frame = state->stack.callStack().back(); caller = frame.caller; state->popFrame(); delete original; @@ -6668,12 +6671,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, @@ -6703,7 +6706,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) { @@ -7137,12 +7140,13 @@ void Executor::dumpStates() { for (ExecutionState *es : states) { *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 << "), "; @@ -7151,7 +7155,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 56cea728ce..c920b1bd45 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -488,11 +488,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, diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp index 7271c5352e..54a1a1ce35 100644 --- a/lib/Core/Searcher.cpp +++ b/lib/Core/Searcher.cpp @@ -424,7 +424,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; @@ -436,7 +436,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/SpecialFunctionHandler.cpp b/lib/Core/SpecialFunctionHandler.cpp index 0ecb4e2fa2..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( diff --git a/lib/Core/StatsTracker.cpp b/lib/Core/StatsTracker.cpp index d69b85a028..2d319ac073 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); @@ -441,28 +441,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; } } @@ -652,8 +655,8 @@ void StatsTracker::updateStateStatistics(uint64_t addend) { 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); } } @@ -1089,20 +1092,19 @@ void StatsTracker::computeReachableUncovered() { 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; + for (unsigned i = 0; i < es->stack.size(); ++i) { + unsigned ri = es->stack.size() - 1 - i; KInstIterator kii; - if (next == es->stack.end()) { + if (ri + 1 == es->stack.size()) { kii = es->pc; } else { - kii = next->caller; + kii = es->stack.callStack().at(ri + 1).caller; ++kii; } - sfIt->minDistToUncoveredOnReturn = currentFrameMinDist; + es->stack.infoStack().at(ri).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/TargetedExecutionManager.cpp b/lib/Core/TargetedExecutionManager.cpp index 7a775a1427..8b6bd6e8ce 100644 --- a/lib/Core/TargetedExecutionManager.cpp +++ b/lib/Core/TargetedExecutionManager.cpp @@ -528,7 +528,7 @@ bool TargetedExecutionManager::reportTruePositive(ExecutionState &state, found = false; break; } - possibleInstruction = state.stack[i].caller; + possibleInstruction = state.stack.callStack().at(i).caller; i--; } if (!found) From 6d78dc0879cd59a3b6d73c2a727835a3f57fb73a Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Sun, 16 Jul 2023 13:55:03 +0400 Subject: [PATCH 41/45] [reafctor] Refine the target's system --- include/klee/Module/Target.h | 8 ++- include/klee/Module/TargetForest.h | 12 +++-- include/klee/Solver/Solver.h | 3 +- lib/Core/ExecutionState.cpp | 71 ++++++++++++++++++++------- lib/Core/ExecutionState.h | 25 +++++++--- lib/Core/Executor.cpp | 39 ++++++--------- lib/Core/Executor.h | 2 +- lib/Core/Searcher.cpp | 42 ++++++++-------- lib/Core/Searcher.h | 9 ++-- lib/Core/TargetManager.cpp | 12 ++--- lib/Core/TargetManager.h | 26 ++++------ lib/Core/TargetedExecutionManager.cpp | 2 +- 12 files changed, 143 insertions(+), 108 deletions(-) diff --git a/include/klee/Module/Target.h b/include/klee/Module/Target.h index b06f40379d..b6ba0ba01d 100644 --- a/include/klee/Module/Target.h +++ b/include/klee/Module/Target.h @@ -56,6 +56,9 @@ struct Target { static ref createCachedTarget(ref target); protected: + friend class ref; + friend class ref; + struct TargetHash { unsigned operator()(Target *const t) const { return t->hash(); } }; @@ -83,10 +86,11 @@ struct Target { 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::vector &_errors, unsigned _id, optional _loc, diff --git a/include/klee/Module/TargetForest.h b/include/klee/Module/TargetForest.h index 78ec719a6a..3a03b7cb84 100644 --- a/include/klee/Module/TargetForest.h +++ b/include/klee/Module/TargetForest.h @@ -64,6 +64,9 @@ class TargetsHistory { } protected: + friend class ref; + friend class ref; + struct TargetsHistoryHash { unsigned operator()(TargetsHistory *const t) const { return t->hash(); } }; @@ -93,6 +96,9 @@ class TargetsHistory { bool isCached = false; bool toBeCleared = false; + /// @brief Required by klee::ref-managed objects + mutable class ReferenceCounter _refCount; + public: const ref target; const ref visitedTargets; @@ -115,12 +121,10 @@ class TargetsHistory { void dump() const; ~TargetsHistory(); - - /// @brief Required by klee::ref-managed objects - class ReferenceCounter _refCount; }; -using TargetHistoryTargetPair = std::pair, ref>; +using TargetHistoryTargetPair = + std::pair, ref>; class TargetForest { public: diff --git a/include/klee/Solver/Solver.h b/include/klee/Solver/Solver.h index b8a7deff69..e9f21b7c89 100644 --- a/include/klee/Solver/Solver.h +++ b/include/klee/Solver/Solver.h @@ -257,8 +257,7 @@ Solver *createDummySolver(); // Create a solver based on the supplied ``CoreSolverType``. Solver *createCoreSolver(CoreSolverType cst); -Solver *createConcretizingSolver(Solver *s, - AddressGenerator *addressGenerator); +Solver *createConcretizingSolver(Solver *s, AddressGenerator *addressGenerator); } // namespace klee #endif /* KLEE_SOLVER_H */ diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index 576434b86c..d038f053e3 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -115,32 +115,32 @@ InfoStackFrame::InfoStackFrame(const InfoStackFrame &s) /***/ ExecutionState::ExecutionState() : initPC(nullptr), pc(nullptr), prevPC(nullptr), incomingBBIndex(-1), - depth(0), prevHistory(TargetsHistory::create()), - history(TargetsHistory::create()), ptreeNode(nullptr), - steppedInstructions(0), steppedMemoryInstructions(0), instsSinceCovNew(0), + 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), incomingBBIndex(-1), - depth(0), prevHistory(TargetsHistory::create()), - history(TargetsHistory::create()), ptreeNode(nullptr), - steppedInstructions(0), steppedMemoryInstructions(0), instsSinceCovNew(0), + 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(); } ExecutionState::ExecutionState(KFunction *kf, KBlock *kb) : initPC(kb->instructions), pc(initPC), prevPC(pc), incomingBBIndex(-1), - depth(0), prevHistory(TargetsHistory::create()), - history(TargetsHistory::create()), ptreeNode(nullptr), - steppedInstructions(0), steppedMemoryInstructions(0), instsSinceCovNew(0), + 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(); } @@ -157,11 +157,10 @@ ExecutionState::ExecutionState(const ExecutionState &state) multilevel(state.multilevel), level(state.level), transitionLevel(state.transitionLevel), addressSpace(state.addressSpace), constraints(state.constraints), targetForest(state.targetForest), - prevTargets(state.prevTargets), targets(state.targets), - prevHistory(state.prevHistory), history(state.history), - isTargeted(state.isTargeted), pathOS(state.pathOS), - symPathOS(state.symPathOS), coveredLines(state.coveredLines), - symbolics(state.symbolics), resolvedPointers(state.resolvedPointers), + prevTargets_(state.prevTargets_), targets_(state.targets_), + pathOS(state.pathOS), symPathOS(state.symPathOS), + coveredLines(state.coveredLines), symbolics(state.symbolics), + resolvedPointers(state.resolvedPointers), cexPreferences(state.cexPreferences), arrayNames(state.arrayNames), steppedInstructions(state.steppedInstructions), steppedMemoryInstructions(state.steppedMemoryInstructions), @@ -171,7 +170,9 @@ ExecutionState::ExecutionState(const ExecutionState &state) ? state.unwindingInformation->clone() : nullptr), coveredNew(state.coveredNew), forkDisabled(state.forkDisabled), - returnValue(state.returnValue), gepExprBases(state.gepExprBases) {} + returnValue(state.returnValue), gepExprBases(state.gepExprBases), + prevHistory_(state.prevHistory_), history_(state.history_), + isTargeted_(state.isTargeted_) {} ExecutionState *ExecutionState::branch() { depth++; @@ -472,6 +473,40 @@ bool ExecutionState::visited(KBlock *block) const { return level.find(block->basicBlock) != level.end(); } +const TargetHashSet &ExecutionState::prevTargets() const { + return prevTargets_; +} + +const TargetHashSet &ExecutionState::targets() const { return targets_; } + +ref ExecutionState::prevHistory() const { + return prevHistory_; +} + +ref ExecutionState::history() const { return history_; } + +bool ExecutionState::isTargeted() const { return isTargeted_; } + +bool ExecutionState::areTargetsChanged() const { return areTargetsChanged_; } + +void ExecutionState::stepTargetsAndHistory() { + prevHistory_ = history_; + prevTargets_ = targets_; + areTargetsChanged_ = false; +} + +void ExecutionState::setTargeted(bool targeted) { isTargeted_ = targeted; } + +void ExecutionState::setTargets(const TargetHashSet &targets) { + targets_ = targets; + areTargetsChanged_ = true; +} + +void ExecutionState::setHistory(ref history) { + history_ = history; + areTargetsChanged_ = true; +} + bool ExecutionState::reachedTarget(Target target) const { if (constraints.path().KBlockSize() == 0) { return false; diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index 2719c03857..be918dbf88 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -291,12 +291,6 @@ class ExecutionState { /// @brief Key points which should be visited through execution TargetForest targetForest; - TargetHashSet prevTargets; - TargetHashSet targets; - ref prevHistory; - ref history; - bool isTargeted = false; - bool areTargetsChanged = false; /// @brief Velocity and acceleration of this state investigating new blocks long long progressVelocity = 0; @@ -379,6 +373,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(); @@ -437,6 +439,17 @@ class ExecutionState { bool isTransfered(); bool isGEPExpr(ref expr) const; + const TargetHashSet &prevTargets() const; + const TargetHashSet &targets() const; + ref prevHistory() const; + ref history() const; + bool isTargeted() const; + bool areTargetsChanged() const; + void stepTargetsAndHistory(); + void setTargeted(bool targeted); + void setTargets(const TargetHashSet &targets); + void setHistory(ref history); + bool reachedTarget(Target target) const; bool isStuck(); }; diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 97118c99dc..91a3ce4d2b 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -1580,7 +1580,7 @@ void Executor::printDebugInstructions(ExecutionState &state) { } (*stream) << state.getID() << ":"; (*stream) << "["; - for (auto target : state.targets) { + for (auto target : state.targets()) { (*stream) << target->toString() << ","; } (*stream) << "]"; @@ -3913,14 +3913,10 @@ void Executor::updateStates(ExecutionState *current) { if (current && (std::find(removedStates.begin(), removedStates.end(), current) == removedStates.end())) { - current->prevHistory = current->history; - current->prevTargets = current->targets; - current->areTargetsChanged = false; + current->stepTargetsAndHistory(); } for (const auto state : addedStates) { - state->prevHistory = state->history; - state->prevTargets = state->targets; - state->areTargetsChanged = false; + state->stepTargetsAndHistory(); } states.insert(addedStates.begin(), addedStates.end()); @@ -4245,8 +4241,7 @@ void Executor::run(std::vector initialStates) { std::vector()); searcher->update(0, newStates, std::vector()); for (auto state : newStates) { - state->prevHistory = state->history; - state->prevTargets = state->targets; + state->stepTargetsAndHistory(); } // main interpreter loop @@ -4329,7 +4324,7 @@ void Executor::initializeTypeManager() { void Executor::executeStep(ExecutionState &state) { KInstruction *prevKI = state.prevPC; - if (targetManager->isTargeted(state) && state.targets.empty()) { + if (targetManager->isTargeted(state) && state.targets().empty()) { terminateStateEarly(state, "State missed all it's targets.", StateTerminationType::MissedAllTargets); } else if (prevKI->inst->isTerminator() && @@ -6524,7 +6519,7 @@ 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); } delete state; @@ -6588,12 +6583,9 @@ ExecutionState *Executor::prepareStateForPOSIX(KInstIterator &caller, 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; + ref targets(new TargetForest()); + targets->add(Target::create(target)); + prepareTargetedExecution(*state, targets); targetedRun(*state, target, &initialState); state = initialState; if (state) { @@ -6614,14 +6606,13 @@ ExecutionState *Executor::prepareStateForPOSIX(KInstIterator &caller, return state; } -void Executor::prepareTargetedExecution(ExecutionState *initialState, +void Executor::prepareTargetedExecution(ExecutionState &initialState, ref whitelist) { - initialState->targetForest = *whitelist->deepCopy(); - initialState->isTargeted = true; - initialState->history = initialState->targetForest.getHistory(); - initialState->targets = initialState->targetForest.getTargets(); - initialState->prevHistory = initialState->history; - initialState->prevTargets = initialState->targets; + initialState.targetForest = *whitelist->deepCopy(); + initialState.setTargeted(true); + initialState.setHistory(initialState.targetForest.getHistory()); + initialState.setTargets(initialState.targetForest.getTargets()); + initialState.stepTargetsAndHistory(); } bool isReturnValueFromInitBlock(const ExecutionState &state, diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index c920b1bd45..b8689083b6 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -615,7 +615,7 @@ class Executor : public Interpreter { ExecutionState *prepareStateForPOSIX(KInstIterator &caller, ExecutionState *state); - void prepareTargetedExecution(ExecutionState *initialState, + void prepareTargetedExecution(ExecutionState &initialState, ref whitelist); void increaseProgressVelocity(ExecutionState &state, KBlock *block); diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp index 54a1a1ce35..63ac6cef29 100644 --- a/lib/Core/Searcher.cpp +++ b/lib/Core/Searcher.cpp @@ -210,7 +210,7 @@ ExecutionState &GuidedSearcher::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, target}) != targetedSearchers.end() && @@ -227,8 +227,8 @@ void GuidedSearcher::update( if (current) { updateTargets(current); - ref history = current->history; - const TargetHashSet &targets = current->targets; + ref history = current->history(); + const TargetHashSet &targets = current->targets(); for (auto target : targets) { localHistoryTargets.insert({history, target}); currTargets.insert({history, target}); @@ -236,8 +236,8 @@ void GuidedSearcher::update( } for (const auto state : addedStates) { - ref history = state->history; - const TargetHashSet &targets = state->targets; + ref history = state->history(); + const TargetHashSet &targets = state->targets(); for (auto target : targets) { localHistoryTargets.insert({history, target}); addedTStates[{history, target}].push_back(state); @@ -245,8 +245,8 @@ void GuidedSearcher::update( } for (const auto state : removedStates) { - ref history = state->history; - const TargetHashSet &targets = state->targets; + ref history = state->history(); + const TargetHashSet &targets = state->targets(); for (auto target : targets) { localHistoryTargets.insert({history, target}); removedTStates[{history, target}].push_back(state); @@ -254,7 +254,7 @@ void GuidedSearcher::update( } for (auto historyTarget : localHistoryTargets) { - ref history = historyTarget.first; + ref history = historyTarget.first; ref target = historyTarget.second; ExecutionState *currTState = @@ -282,14 +282,14 @@ void GuidedSearcher::update( } void GuidedSearcher::updateTargets(ExecutionState *state) { - if (!state->areTargetsChanged) { + if (!state->areTargetsChanged()) { return; } - ref prevHistory = state->prevHistory; - ref history = state->history; - const TargetHashSet &prevTargets = state->prevTargets; - const TargetHashSet &targets = state->targets; + 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}); @@ -323,7 +323,7 @@ void GuidedSearcher::updateTargets(ExecutionState *state) { } for (auto historyTarget : localHistoryTargets) { - ref history = historyTarget.first; + ref history = historyTarget.first; ref target = historyTarget.second; if (!isThereTarget(history, target)) { @@ -342,33 +342,33 @@ void GuidedSearcher::updateTargets(ExecutionState *state) { localHistoryTargets.clear(); } -bool GuidedSearcher::isThereTarget(ref history, +bool GuidedSearcher::isThereTarget(ref history, ref target) { return targetedSearchers.count({history, target}) != 0; } -void GuidedSearcher::addTarget(ref history, +void GuidedSearcher::addTarget(ref history, ref target) { 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) { + [&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}); } -void GuidedSearcher::removeTarget(ref history, +void GuidedSearcher::removeTarget(ref history, ref target) { targetedSearchers.erase({history, target}); auto it = std::find_if( historiesAndTargets.begin(), historiesAndTargets.end(), - [&history, - &target](const std::pair, ref> &element) { + [&history, &target]( + const std::pair, ref> &element) { return element.first.get() == history.get() && element.second.get() == target.get(); }); diff --git a/lib/Core/Searcher.h b/lib/Core/Searcher.h index 0c7bee33f5..09eeb86210 100644 --- a/lib/Core/Searcher.h +++ b/lib/Core/Searcher.h @@ -157,7 +157,8 @@ class GuidedSearcher final : public Searcher { std::unordered_map; - using TargetHistoryTargetPair = std::pair, ref>; + using TargetHistoryTargetPair = + std::pair, ref>; using TargetHistoryTargetPairToSearcherMap = std::unordered_map, @@ -187,9 +188,9 @@ class GuidedSearcher final : public Searcher { TargetForestHistoryTargetSet currTargets; TargetForestHisoryTargetVector historiesAndTargets; - bool isThereTarget(ref history, ref target); - void addTarget(ref history, ref target); - void removeTarget(ref history, ref target); + bool isThereTarget(ref history, ref target); + void addTarget(ref history, ref target); + void removeTarget(ref history, ref target); public: GuidedSearcher(Searcher *baseSearcher, DistanceCalculator &distanceCalculator, diff --git a/lib/Core/TargetManager.cpp b/lib/Core/TargetManager.cpp index bc52fa1d9c..9d59758e42 100644 --- a/lib/Core/TargetManager.cpp +++ b/lib/Core/TargetManager.cpp @@ -24,9 +24,8 @@ void TargetManager::updateMiss(ExecutionState &state, ref target) { auto &stateTargetForest = targetForest(state); stateTargetForest.remove(target); setTargets(state, stateTargetForest.getTargets()); - state.areTargetsChanged = true; if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { - state.isTargeted = false; + state.setTargeted(false); } } @@ -38,7 +37,6 @@ void TargetManager::updateDone(ExecutionState &state, ref target) { stateTargetForest.stepTo(target); setTargets(state, stateTargetForest.getTargets()); setHistory(state, stateTargetForest.getHistory()); - state.areTargetsChanged = true; if (guidance == Interpreter::GuidanceKind::CoverageGuidance || target->shouldFailOnThisTarget()) { reachedTargets.insert(target); @@ -46,10 +44,9 @@ void TargetManager::updateDone(ExecutionState &state, ref target) { if (isTargeted(*es)) { auto &esTargetForest = targetForest(*es); esTargetForest.block(target); - es->areTargetsChanged = true; if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { if (targets(*es).size() == 0) { - es->isTargeted = false; + es->setTargeted(false); } } setTargets(*es, esTargetForest.getTargets()); @@ -57,20 +54,19 @@ void TargetManager::updateDone(ExecutionState &state, ref target) { } } if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { - state.isTargeted = false; + state.setTargeted(false); } } void TargetManager::updateTargets(ExecutionState &state) { if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { if (targets(state).empty() && state.isStuck()) { - state.isTargeted = true; + state.setTargeted(true); } if (isTargeted(state) && targets(state).empty()) { ref target(targetCalculator.calculate(state)); if (target) { state.targetForest.add(target); - state.areTargetsChanged = true; setTargets(state, state.targetForest.getTargets()); } } diff --git a/lib/Core/TargetManager.h b/lib/Core/TargetManager.h index cfc173a145..5975539e13 100644 --- a/lib/Core/TargetManager.h +++ b/lib/Core/TargetManager.h @@ -37,19 +37,11 @@ class TargetManager { StatesSet localStates; void setTargets(ExecutionState &state, const TargetHashSet &targets) { - state.targets = targets; + state.setTargets(targets); } void setHistory(ExecutionState &state, ref history) { - state.history = history; - } - - void setPrevHistory(ExecutionState &state, ref history) { - state.prevHistory = history; - } - - void setPrevTargets(ExecutionState &state, const TargetHashSet &targets) { - state.prevTargets = targets; + state.setHistory(history); } void updateMiss(ExecutionState &state, ref target); @@ -76,26 +68,26 @@ class TargetManager { } const TargetHashSet &targets(const ExecutionState &state) { - return state.targets; + return state.targets(); } - ref history(const ExecutionState &state) { - return state.history; + ref history(const ExecutionState &state) { + return state.history(); } - ref prevHistory(const ExecutionState &state) { - return state.prevHistory; + ref prevHistory(const ExecutionState &state) { + return state.prevHistory(); } const TargetHashSet &prevTargets(const ExecutionState &state) { - return state.prevTargets; + return state.prevTargets(); } TargetForest &targetForest(ExecutionState &state) { return state.targetForest; } - bool isTargeted(const ExecutionState &state) { return state.isTargeted; } + bool isTargeted(const ExecutionState &state) { return state.isTargeted(); } void setReached(ref target) { reachedTargets.insert(target); } }; diff --git a/lib/Core/TargetedExecutionManager.cpp b/lib/Core/TargetedExecutionManager.cpp index 8b6bd6e8ce..0495089249 100644 --- a/lib/Core/TargetedExecutionManager.cpp +++ b/lib/Core/TargetedExecutionManager.cpp @@ -567,7 +567,7 @@ void TargetedExecutionManager::update( TargetToStateUnorderedSetMap reachableStatesOfTarget; for (auto state : localStates) { - auto &stateTargets = state->targets; + auto &stateTargets = state->targets(); for (auto target : stateTargets) { DistanceResult stateDistance = targetManager.distance(*state, target); From 6fd7ab9a79047bcc51eab0f4ec453a5bdf9c35da Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Mon, 17 Jul 2023 23:13:15 +0400 Subject: [PATCH 42/45] [refactor] Add `computeConcretization` --- lib/Core/Executor.cpp | 71 +++++++++++++++++-------------------------- lib/Core/Executor.h | 4 +++ 2 files changed, 32 insertions(+), 43 deletions(-) diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 91a3ce4d2b..dfe198957e 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -1390,19 +1390,8 @@ void Executor::addConstraint(ExecutionState &state, ref condition) { klee_warning("seeds patched for violating constraint"); } - Assignment concretization; - if (Query(state.constraints.cs(), condition).containsSymcretes()) { - ref response; - solver->setTimeout(coreSolverTimeout); - bool success = solver->getResponse(state.constraints.cs(), - Expr::createIsZero(condition), response, - state.queryMetaData); - solver->setTimeout(time::Span()); - assert(success); - assert(isa(response)); - concretization = cast(response)->initialValuesFor( - state.constraints.cs().gatherSymcretizedArrays()); - } + Assignment concretization = computeConcretization( + state.constraints.cs(), condition, state.queryMetaData); if (!concretization.isEmpty()) { // Update memory objects if arrays have affected them. @@ -5692,22 +5681,11 @@ bool Executor::collectConcretizations( } for (unsigned int i = 0; i < resolvedMemoryObjects.size(); ++i) { - Assignment symcretization; - if (Query(state.constraints.cs(), resolveConditions.at(i)) - .containsSymcretes()) { - ref response; - solver->setTimeout(coreSolverTimeout); - bool success = solver->getResponse( - state.constraints.cs(), Expr::createIsZero(resolveConditions.at(i)), - response, state.queryMetaData); - solver->setTimeout(time::Span()); - assert(success); - assert(isa(response)); - symcretization = cast(response)->initialValuesFor( - state.constraints.cs().gatherSymcretizedArrays()); - } - resolveConcretizations.push_back(symcretization); + Assignment concretization = computeConcretization( + state.constraints.cs(), resolveConditions.at(i), state.queryMetaData); + resolveConcretizations.push_back(concretization); } + return true; } @@ -6883,6 +6861,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); @@ -6908,22 +6904,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) { - Assignment symcretization; - if (Query(extendedConstraints.cs(), pi).containsSymcretes()) { - ref response; - solver->setTimeout(coreSolverTimeout); - bool success = solver->getResponse(extendedConstraints.cs(), - Expr::createIsZero(pi), response, - state.queryMetaData); - solver->setTimeout(time::Span()); - assert(success); - assert(isa(response)); - symcretization = cast(response)->initialValuesFor( - state.constraints.cs().gatherSymcretizedArrays()); - } + Assignment concretization = computeConcretization( + extendedConstraints.cs(), pi, state.queryMetaData); - if (!symcretization.isEmpty()) { - extendedConstraints.addConstraint(pi, symcretization); + if (!concretization.isEmpty()) { + extendedConstraints.addConstraint(pi, concretization); } else { extendedConstraints.addConstraint(pi, {}); } diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index b8689083b6..b7bc72132c 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -438,6 +438,10 @@ class Executor : public Interpreter { // 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, From 63778b2003688fc685d686b05faf2699a47920cc Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Mon, 17 Jul 2023 23:41:01 +0400 Subject: [PATCH 43/45] [fixup] Replace `stack_ty` --- lib/Core/ExecutionState.h | 16 ++++++++-------- lib/Core/StatsTracker.cpp | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index be918dbf88..0319074d0c 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -115,14 +115,14 @@ struct ExecutionStack { public: void pushFrame(KInstIterator caller, KFunction *kf); void popFrame(); - value_stack_ty &valueStack() { return valueStack_; } - const value_stack_ty &valueStack() const { return valueStack_; } - const call_stack_ty &callStack() const { return callStack_; } - info_stack_ty &infoStack() { return infoStack_; } - const call_stack_ty &uniqueFrames() const { return uniqueFrames_; } - - unsigned size() const { return callStack_.size(); } - bool empty() const { return callStack_.empty(); } + 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) diff --git a/lib/Core/StatsTracker.cpp b/lib/Core/StatsTracker.cpp index 2d319ac073..d0bef8787a 100644 --- a/lib/Core/StatsTracker.cpp +++ b/lib/Core/StatsTracker.cpp @@ -1092,19 +1092,25 @@ void StatsTracker::computeReachableUncovered() { it != ie; ++it) { ExecutionState *es = *it; uint64_t currentFrameMinDist = 0; - for (unsigned i = 0; i < es->stack.size(); ++i) { - unsigned ri = es->stack.size() - 1 - i; + 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 (ri + 1 == es->stack.size()) { + if (next == sf_ie) { kii = es->pc; } else { - kii = es->stack.callStack().at(ri + 1).caller; + kii = next->caller; ++kii; } - es->stack.infoStack().at(ri).minDistToUncoveredOnReturn = - currentFrameMinDist; + isfIt->minDistToUncoveredOnReturn = currentFrameMinDist; currentFrameMinDist = computeMinDistToUncovered(kii, currentFrameMinDist); } From dbafb8affaeed2090105a99a6915b431e965e018 Mon Sep 17 00:00:00 2001 From: Aleksandr Misonizhnik Date: Mon, 17 Jul 2023 23:42:18 +0400 Subject: [PATCH 44/45] [fixup] Add `TargetManager` --- lib/Core/DistanceCalculator.cpp | 55 ++++++++++++++++--------------- lib/Core/DistanceCalculator.h | 24 +++++++------- lib/Core/ExecutionState.cpp | 57 ++++++--------------------------- lib/Core/ExecutionState.h | 53 +++++++++++++++++++++++------- lib/Core/Executor.cpp | 12 +++---- lib/Core/Executor.h | 6 ++-- lib/Core/Searcher.cpp | 12 +++---- lib/Core/StatsTracker.cpp | 8 +++-- lib/Core/TargetManager.cpp | 2 +- 9 files changed, 107 insertions(+), 122 deletions(-) diff --git a/lib/Core/DistanceCalculator.cpp b/lib/Core/DistanceCalculator.cpp index 963a923181..8702ee57b0 100644 --- a/lib/Core/DistanceCalculator.cpp +++ b/lib/Core/DistanceCalculator.cpp @@ -35,7 +35,7 @@ std::string DistanceResult::toString() const { unsigned DistanceCalculator::SpeculativeState::computeHash() { unsigned res = - (reinterpret_cast(pc) * SymbolicSource::MAGIC_HASH_CONSTANT) + + (reinterpret_cast(kb) * SymbolicSource::MAGIC_HASH_CONSTANT) + kind; res = (res * SymbolicSource::MAGIC_HASH_CONSTANT) + error; hashValue = res; @@ -48,22 +48,19 @@ DistanceResult DistanceCalculator::getDistance(const ExecutionState &state, state.error, target); } -DistanceResult DistanceCalculator::getDistance(const KInstruction *pc, - TargetKind kind, +DistanceResult DistanceCalculator::getDistance(KBlock *kb, TargetKind kind, ReachWithError error, ref target) { - const KInstruction *ki = pc; - SpeculativeState specState(pc, kind, error); + SpeculativeState specState(kb, kind, error); if (distanceResultCache.count(target) == 0 || distanceResultCache.at(target).count(specState) == 0) { - auto result = computeDistance(pc, kind, error, target); + auto result = computeDistance(kb, kind, error, target); distanceResultCache[target][specState] = result; } return distanceResultCache.at(target).at(specState); } -DistanceResult DistanceCalculator::computeDistance(const KInstruction *pc, - TargetKind kind, +DistanceResult DistanceCalculator::computeDistance(KBlock *kb, TargetKind kind, ReachWithError error, ref target) const { const auto &distanceToTargetFunction = @@ -73,16 +70,16 @@ DistanceResult DistanceCalculator::computeDistance(const KInstruction *pc, bool isInsideFunction = true; switch (kind) { case LocalTarget: - res = tryGetTargetWeight(pc, weight, target); + res = tryGetTargetWeight(kb, weight, target); break; case PreTarget: - res = tryGetPreTargetWeight(pc, weight, distanceToTargetFunction, target); + res = tryGetPreTargetWeight(kb, weight, distanceToTargetFunction, target); isInsideFunction = false; break; case PostTarget: - res = tryGetPostTargetWeight(pc, weight, target); + res = tryGetPostTargetWeight(kb, weight, target); isInsideFunction = false; break; @@ -129,7 +126,7 @@ DistanceCalculator::getDistance(const KInstruction *prevPC, callWeight *= 2; callWeight += sfNum; - if (callWeight < minCallWeight) { + if (callWeight < UINT_MAX) { minCallWeight = callWeight; minSfNum = sfNum; } @@ -140,7 +137,7 @@ DistanceCalculator::getDistance(const KInstruction *prevPC, } sfNum++; - if (minCallWeight < sfNum) + if (minCallWeight < UINT_MAX) break; } @@ -153,7 +150,7 @@ DistanceCalculator::getDistance(const KInstruction *prevPC, kind = PostTarget; } - return getDistance(pc, kind, error, target); + return getDistance(pc->parent, kind, error, target); } bool DistanceCalculator::distanceInCallGraph( @@ -186,11 +183,12 @@ bool DistanceCalculator::distanceInCallGraph( return distance != UINT_MAX; } -WeightResult DistanceCalculator::tryGetLocalWeight( - const KInstruction *pc, weight_type &weight, - const std::vector &localTargets, ref target) const { - KFunction *currentKF = pc->parent->parent; - KBlock *currentKB = pc->parent; +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; @@ -211,11 +209,11 @@ WeightResult DistanceCalculator::tryGetLocalWeight( } WeightResult DistanceCalculator::tryGetPreTargetWeight( - const KInstruction *pc, weight_type &weight, + KBlock *kb, weight_type &weight, const std::unordered_map &distanceToTargetFunction, ref target) const { - KFunction *currentKF = pc->parent->parent; + KFunction *currentKF = kb->parent; std::vector localTargets; for (auto &kCallBlock : currentKF->kCallBlocks) { for (auto &calledFunction : kCallBlock->calledFunctions) { @@ -230,26 +228,27 @@ WeightResult DistanceCalculator::tryGetPreTargetWeight( if (localTargets.empty()) return Miss; - WeightResult res = tryGetLocalWeight(pc, weight, localTargets, target); + WeightResult res = tryGetLocalWeight(kb, weight, localTargets, target); return res == Done ? Continue : res; } -WeightResult DistanceCalculator::tryGetPostTargetWeight( - const KInstruction *pc, weight_type &weight, ref target) const { - KFunction *currentKF = pc->parent->parent; +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(pc, weight, localTargets, target); + WeightResult res = tryGetLocalWeight(kb, weight, localTargets, target); return res == Done ? Continue : res; } -WeightResult DistanceCalculator::tryGetTargetWeight(const KInstruction *pc, +WeightResult DistanceCalculator::tryGetTargetWeight(KBlock *kb, weight_type &weight, ref target) const { std::vector localTargets = {target->getBlock()}; - WeightResult res = tryGetLocalWeight(pc, weight, localTargets, target); + WeightResult res = tryGetLocalWeight(kb, weight, localTargets, target); return res; } diff --git a/lib/Core/DistanceCalculator.h b/lib/Core/DistanceCalculator.h index 0e94bf8b30..37830c967e 100644 --- a/lib/Core/DistanceCalculator.h +++ b/lib/Core/DistanceCalculator.h @@ -69,12 +69,11 @@ class DistanceCalculator { unsigned computeHash(); public: - const KInstruction *pc; + KBlock *kb; TargetKind kind; ReachWithError error; - SpeculativeState(const KInstruction *pc_, TargetKind kind_, - ReachWithError error_) - : pc(pc_), kind(kind_), error(error_) { + SpeculativeState(KBlock *kb_, TargetKind kind_, ReachWithError error_) + : kb(kb_), kind(kind_), error(error_) { computeHash(); } ~SpeculativeState() = default; @@ -88,7 +87,7 @@ class DistanceCalculator { struct SpeculativeStateCompare { bool operator()(const SpeculativeState &a, const SpeculativeState &b) const { - return a.pc == b.pc && a.error == b.error && a.kind == b.kind; + return a.kb == b.kb && a.error == b.error && a.kind == b.kind; } }; @@ -105,10 +104,10 @@ class DistanceCalculator { TargetToSpeculativeStateToDistanceResultMap distanceResultCache; StatesSet localStates; - DistanceResult getDistance(const KInstruction *pc, TargetKind kind, - ReachWithError error, ref target); + DistanceResult getDistance(KBlock *kb, TargetKind kind, ReachWithError error, + ref target); - DistanceResult computeDistance(const KInstruction *pc, TargetKind kind, + DistanceResult computeDistance(KBlock *kb, TargetKind kind, ReachWithError error, ref target) const; @@ -116,18 +115,17 @@ class DistanceCalculator { const std::unordered_map &distanceToTargetFunction, ref target) const; - WeightResult tryGetLocalWeight(const KInstruction *pc, weight_type &weight, + WeightResult tryGetLocalWeight(KBlock *kb, weight_type &weight, const std::vector &localTargets, ref target) const; WeightResult - tryGetPreTargetWeight(const KInstruction *pc, weight_type &weight, + tryGetPreTargetWeight(KBlock *kb, weight_type &weight, const std::unordered_map &distanceToTargetFunction, ref target) const; - WeightResult tryGetTargetWeight(const KInstruction *pc, weight_type &weight, + WeightResult tryGetTargetWeight(KBlock *kb, weight_type &weight, ref target) const; - WeightResult tryGetPostTargetWeight(const KInstruction *pc, - weight_type &weight, + WeightResult tryGetPostTargetWeight(KBlock *kb, weight_type &weight, ref target) const; }; } // namespace klee diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index d038f053e3..58f89d4dfb 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -36,6 +36,14 @@ 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), @@ -43,11 +51,6 @@ cl::opt UseGEPOptimization( "instead of only the referenced parts (default=true)"), cl::cat(ExecCat)); -cl::opt MaxCyclesBeforeStuck( - "max-cycles-before-stuck", - cl::desc("Set target after after state visiting some basic block this " - "amount of times (default=1)."), - cl::init(1), cl::cat(TerminationCat)); } // namespace /***/ @@ -157,7 +160,6 @@ ExecutionState::ExecutionState(const ExecutionState &state) multilevel(state.multilevel), level(state.level), transitionLevel(state.transitionLevel), addressSpace(state.addressSpace), constraints(state.constraints), targetForest(state.targetForest), - prevTargets_(state.prevTargets_), targets_(state.targets_), pathOS(state.pathOS), symPathOS(state.symPathOS), coveredLines(state.coveredLines), symbolics(state.symbolics), resolvedPointers(state.resolvedPointers), @@ -171,6 +173,7 @@ ExecutionState::ExecutionState(const ExecutionState &state) : nullptr), coveredNew(state.coveredNew), forkDisabled(state.forkDisabled), returnValue(state.returnValue), gepExprBases(state.gepExprBases), + prevTargets_(state.prevTargets_), targets_(state.targets_), prevHistory_(state.prevHistory_), history_(state.history_), isTargeted_(state.isTargeted_) {} @@ -463,8 +466,6 @@ void ExecutionState::increaseLevel() { } } -bool ExecutionState::isTransfered() { return getPrevPCBlock() != getPCBlock(); } - bool ExecutionState::isGEPExpr(ref expr) const { return UseGEPOptimization && gepExprBases.find(expr) != gepExprBases.end(); } @@ -473,40 +474,6 @@ bool ExecutionState::visited(KBlock *block) const { return level.find(block->basicBlock) != level.end(); } -const TargetHashSet &ExecutionState::prevTargets() const { - return prevTargets_; -} - -const TargetHashSet &ExecutionState::targets() const { return targets_; } - -ref ExecutionState::prevHistory() const { - return prevHistory_; -} - -ref ExecutionState::history() const { return history_; } - -bool ExecutionState::isTargeted() const { return isTargeted_; } - -bool ExecutionState::areTargetsChanged() const { return areTargetsChanged_; } - -void ExecutionState::stepTargetsAndHistory() { - prevHistory_ = history_; - prevTargets_ = targets_; - areTargetsChanged_ = false; -} - -void ExecutionState::setTargeted(bool targeted) { isTargeted_ = targeted; } - -void ExecutionState::setTargets(const TargetHashSet &targets) { - targets_ = targets; - areTargetsChanged_ = true; -} - -void ExecutionState::setHistory(ref history) { - history_ = history; - areTargetsChanged_ = true; -} - bool ExecutionState::reachedTarget(Target target) const { if (constraints.path().KBlockSize() == 0) { return false; @@ -517,9 +484,3 @@ bool ExecutionState::reachedTarget(Target target) const { return pc == target.getBlock()->getFirstInstruction(); } } - -bool ExecutionState::isStuck() { - KInstruction *prevKI = prevPC; - return (prevKI->inst->isTerminator() && - multilevel[getPCBlock()] > MaxCyclesBeforeStuck - 1); -} diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index 0319074d0c..5d63a5e937 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" @@ -52,6 +53,8 @@ struct TranstionHash; llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const MemoryMap &mm); +extern llvm::cl::opt MaxCyclesBeforeStuck; + struct CallStackFrame { KInstIterator caller; KFunction *kf; @@ -436,22 +439,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; - const TargetHashSet &prevTargets() const; - const TargetHashSet &targets() const; - ref prevHistory() const; - ref history() const; - bool isTargeted() const; - bool areTargetsChanged() const; - void stepTargetsAndHistory(); - void setTargeted(bool targeted); - void setTargets(const TargetHashSet &targets); - void setHistory(ref history); + 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(Target target) const; - bool isStuck(); + + 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 dfe198957e..1b0f246135 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -4049,12 +4049,11 @@ bool Executor::checkMemoryUsage() { return false; } -bool Executor::decreaseConfidenceFromStoppedStates( +void Executor::decreaseConfidenceFromStoppedStates( SetOfStates &leftStates, HaltExecution::Reason reason) { if (targets.size() == 0) { - return false; + return; } - bool hasStateWhichCanReachSomeTarget = false; for (auto state : leftStates) { if (state->targetForest.empty()) continue; @@ -4066,7 +4065,6 @@ bool Executor::decreaseConfidenceFromStoppedStates( .subtractConfidencesFrom(state->targetForest, realReason); } } - return hasStateWhichCanReachSomeTarget; } void Executor::doDumpStates() { @@ -4260,11 +4258,11 @@ void Executor::run(std::vector initialStates) { if (guidanceKind == GuidanceKind::ErrorGuidance) { reportProgressTowardsTargets(); - bool canReachNew = - decreaseConfidenceFromStoppedStates(states, haltExecution); + decreaseConfidenceFromStoppedStates(states, haltExecution); for (auto &startBlockAndWhiteList : targets) { - startBlockAndWhiteList.second.reportFalsePositives(canReachNew); + startBlockAndWhiteList.second.reportFalsePositives( + hasStateWhichCanReachSomeTarget); } if (searcher->empty()) diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index b7bc72132c..ce983b0b86 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -239,6 +239,8 @@ class Executor : public Interpreter { GuidanceKind guidanceKind; + bool hasStateWhichCanReachSomeTarget = false; + /// Return the typeid corresponding to a certain `type_info` ref getEhTypeidFor(ref type_info); @@ -624,8 +626,8 @@ class Executor : public Interpreter { void increaseProgressVelocity(ExecutionState &state, KBlock *block); - bool decreaseConfidenceFromStoppedStates( - SetOfStates &left_states, + void decreaseConfidenceFromStoppedStates( + SetOfStates &leftStates, HaltExecution::Reason reason = HaltExecution::NotHalt); void checkNullCheckAfterDeref(ref cond, ExecutionState &state, diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp index 63ac6cef29..db056a2013 100644 --- a/lib/Core/Searcher.cpp +++ b/lib/Core/Searcher.cpp @@ -192,9 +192,9 @@ weight_type TargetedSearcher::getWeight(ExecutionState *es) { return weight; } auto distRes = distanceCalculator.getDistance(*es, target); - weight = ulog2(distRes.weight + es->steppedMemoryInstructions + 1); // [0, 32] + weight = ulog2(distRes.weight + es->steppedMemoryInstructions + 1); // [0, 32) if (!distRes.isInsideFunction) { - weight += 32; // [32, 64] + weight += 32; // [32, 64) } return weight; } @@ -300,16 +300,12 @@ void GuidedSearcher::updateTargets(ExecutionState *state) { addedTStates[{history, target}].push_back(state); } } else { + addedTargets = targets; for (auto target : prevTargets) { - if (!targets.count(target)) { + if (addedTargets.erase(target) == 0) { removedTargets.insert(target); } } - for (auto target : targets) { - if (!prevTargets.count(target)) { - addedTargets.insert(target); - } - } for (auto target : removedTargets) { localHistoryTargets.insert({history, target}); removedTStates[{history, target}].push_back(state); diff --git a/lib/Core/StatsTracker.cpp b/lib/Core/StatsTracker.cpp index d0bef8787a..3ef6013971 100644 --- a/lib/Core/StatsTracker.cpp +++ b/lib/Core/StatsTracker.cpp @@ -820,11 +820,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(); diff --git a/lib/Core/TargetManager.cpp b/lib/Core/TargetManager.cpp index 9d59758e42..17d19ad720 100644 --- a/lib/Core/TargetManager.cpp +++ b/lib/Core/TargetManager.cpp @@ -60,7 +60,7 @@ void TargetManager::updateDone(ExecutionState &state, ref target) { void TargetManager::updateTargets(ExecutionState &state) { if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { - if (targets(state).empty() && state.isStuck()) { + if (targets(state).empty() && state.isStuck(MaxCyclesBeforeStuck)) { state.setTargeted(true); } if (isTargeted(state) && targets(state).empty()) { From faebfae8364743ee4247b4cec1eb9347b9826ee9 Mon Sep 17 00:00:00 2001 From: Alex Babushkin Date: Mon, 17 Jul 2023 17:57:22 +0300 Subject: [PATCH 45/45] save --- include/klee/ADT/Ticker.h | 33 + include/klee/Core/Interpreter.h | 6 + include/klee/Expr/Constraints.h | 9 +- include/klee/Expr/Expr.h | 2 +- include/klee/Expr/ExprPPrinter.h | 3 + include/klee/Expr/Lemma.h | 63 ++ include/klee/Expr/Parser/Lexer.h | 1 + include/klee/Expr/Parser/Parser.h | 23 +- include/klee/Expr/Symcrete.h | 13 +- include/klee/Module/CodeGraphDistance.h | 6 + include/klee/Support/DebugFlags.h | 27 + lib/Core/BackwardSearcher.cpp | 46 + lib/Core/BackwardSearcher.h | 41 + lib/Core/BidirectionalSearcher.cpp | 162 ++++ lib/Core/BidirectionalSearcher.h | 68 ++ lib/Core/CMakeLists.txt | 7 + lib/Core/Composer.cpp | 383 ++++++++ lib/Core/Composer.h | 224 +++++ lib/Core/ExecutionState.cpp | 33 +- lib/Core/ExecutionState.h | 17 +- lib/Core/Executor.cpp | 1148 +++++++++++++++++------ lib/Core/Executor.h | 96 +- lib/Core/Initializer.cpp | 173 ++++ lib/Core/Initializer.h | 63 ++ lib/Core/Memory.cpp | 9 + lib/Core/Memory.h | 1 + lib/Core/ObjectManager.cpp | 311 ++++++ lib/Core/ObjectManager.h | 183 ++++ lib/Core/ProofObligation.cpp | 7 + lib/Core/ProofObligation.h | 104 ++ lib/Core/SearcherUtil.h | 103 ++ lib/Core/SeedMap.cpp | 62 ++ lib/Core/SeedMap.h | 38 + lib/Core/StatsTracker.cpp | 21 +- lib/Core/TargetManager.cpp | 25 +- lib/Core/TargetManager.h | 5 +- lib/Core/TargetedExecutionManager.cpp | 11 +- lib/Core/TargetedExecutionManager.h | 6 +- lib/Core/UserSearcher.cpp | 11 + lib/Core/UserSearcher.h | 3 + lib/Expr/CMakeLists.txt | 1 + lib/Expr/Constraints.cpp | 6 +- lib/Expr/ExprPPrinter.cpp | 49 + lib/Expr/Lemma.cpp | 113 +++ lib/Expr/Lexer.cpp | 4 + lib/Expr/Parser.cpp | 73 ++ lib/Module/CodeGraphDistance.cpp | 74 ++ lib/Runner/run_klee.cpp | 11 +- lib/Support/CMakeLists.txt | 1 + lib/Support/DebugFlags.cpp | 29 + test/Bidirectional/fib-sum-dec.c | 38 + test/Bidirectional/fib-sum-dec.c.good | 280 ++++++ 52 files changed, 3834 insertions(+), 392 deletions(-) create mode 100644 include/klee/ADT/Ticker.h create mode 100644 include/klee/Expr/Lemma.h create mode 100644 include/klee/Support/DebugFlags.h create mode 100644 lib/Core/BackwardSearcher.cpp create mode 100644 lib/Core/BackwardSearcher.h create mode 100644 lib/Core/BidirectionalSearcher.cpp create mode 100644 lib/Core/BidirectionalSearcher.h create mode 100644 lib/Core/Composer.cpp create mode 100644 lib/Core/Composer.h create mode 100644 lib/Core/Initializer.cpp create mode 100644 lib/Core/Initializer.h create mode 100644 lib/Core/ObjectManager.cpp create mode 100644 lib/Core/ObjectManager.h create mode 100644 lib/Core/ProofObligation.cpp create mode 100644 lib/Core/ProofObligation.h create mode 100644 lib/Core/SearcherUtil.h create mode 100644 lib/Core/SeedMap.cpp create mode 100644 lib/Core/SeedMap.h create mode 100644 lib/Expr/Lemma.cpp create mode 100644 lib/Support/DebugFlags.cpp create mode 100644 test/Bidirectional/fib-sum-dec.c create mode 100644 test/Bidirectional/fib-sum-dec.c.good 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 2a21d29256..9b3a91c7f2 100644 --- a/include/klee/Core/Interpreter.h +++ b/include/klee/Core/Interpreter.h @@ -56,11 +56,17 @@ 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 *message, const char *suffix, bool isError = false) = 0; }; +enum class ExecutionKind { + Forward, + Bidirectional +}; + class Interpreter { public: enum class GuidanceKind { 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 cc4c67cdfa..188c2cb7c9 100644 --- a/include/klee/Expr/Expr.h +++ b/include/klee/Expr/Expr.h @@ -394,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) { 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/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/Expr/Symcrete.h b/include/klee/Expr/Symcrete.h index c8ebab70c0..aacfa44b83 100644 --- a/include/klee/Expr/Symcrete.h +++ b/include/klee/Expr/Symcrete.h @@ -84,10 +84,7 @@ typedef std::set, SymcreteLess> SymcreteOrderedSet; class AddressSymcrete : public Symcrete { public: - const Array *addressArray; - - AddressSymcrete(const Array *_addressArray, ref s, SymcreteKind kind) - : Symcrete(s, kind), addressArray(_addressArray) { + AddressSymcrete(ref s, SymcreteKind kind) : Symcrete(s, kind) { assert((kind == SymcreteKind::SK_ALLOC_ADDRESS || kind == SymcreteKind::SK_LI_ADDRESS) && "wrong kind"); @@ -101,8 +98,8 @@ class AddressSymcrete : public Symcrete { class AllocAddressSymcrete : public AddressSymcrete { public: - AllocAddressSymcrete(const Array *addressArray, ref s) - : AddressSymcrete(addressArray, s, SymcreteKind::SK_ALLOC_ADDRESS) {} + AllocAddressSymcrete(ref s) + : AddressSymcrete(s, SymcreteKind::SK_ALLOC_ADDRESS) {} static bool classof(const Symcrete *symcrete) { return symcrete->getKind() == SymcreteKind::SK_ALLOC_ADDRESS; @@ -111,8 +108,8 @@ class AllocAddressSymcrete : public AddressSymcrete { class LazyInitializedAddressSymcrete : public AddressSymcrete { public: - LazyInitializedAddressSymcrete(const Array *addressArray, ref s) - : AddressSymcrete(addressArray, s, SymcreteKind::SK_LI_ADDRESS) {} + LazyInitializedAddressSymcrete(ref s) + : AddressSymcrete(s, SymcreteKind::SK_LI_ADDRESS) {} static bool classof(const Symcrete *symcrete) { return symcrete->getKind() == SymcreteKind::SK_LI_ADDRESS; 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/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/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 f6a7144b5f..9c46608319 100644 --- a/lib/Core/CMakeLists.txt +++ b/lib/Core/CMakeLists.txt @@ -9,7 +9,10 @@ klee_add_component(kleeCore AddressManager.cpp AddressSpace.cpp + BackwardSearcher.cpp + BidirectionalSearcher.cpp CallPathManager.cpp + Composer.cpp Context.cpp CoreStats.cpp CXXTypeSystem/CXXTypeManager.cpp @@ -19,12 +22,16 @@ klee_add_component(kleeCore 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 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/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index 58f89d4dfb..daecb3286f 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -67,7 +67,7 @@ void ExecutionStack::pushFrame(KInstIterator caller, KFunction *kf) { } callStack_.emplace_back(CallStackFrame(caller, kf)); infoStack_.emplace_back(InfoStackFrame(kf)); - ++stackBalance; + ++stackBalance_; assert(valueStack_.size() == callStack_.size()); assert(valueStack_.size() == infoStack_.size()); } @@ -84,7 +84,7 @@ void ExecutionStack::popFrame() { if (it == callStack_.end()) { uniqueFrames_.pop_back(); } - --stackBalance; + --stackBalance_; assert(valueStack_.size() == callStack_.size()); assert(valueStack_.size() == infoStack_.size()); } @@ -155,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), @@ -172,10 +172,10 @@ ExecutionState::ExecutionState(const ExecutionState &state) ? state.unwindingInformation->clone() : nullptr), coveredNew(state.coveredNew), forkDisabled(state.forkDisabled), - returnValue(state.returnValue), gepExprBases(state.gepExprBases), - prevTargets_(state.prevTargets_), targets_(state.targets_), - prevHistory_(state.prevHistory_), history_(state.history_), - isTargeted_(state.isTargeted_) {} + 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++; @@ -213,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; @@ -433,6 +433,10 @@ void ExecutionState::dumpStack(llvm::raw_ostream &out) const { } } +std::string ExecutionState::pathAndPCToString() const { + return constraints.path().toString() + " at " + pc->toString(); +} + void ExecutionState::addConstraint(ref e, const Assignment &delta) { constraints.addConstraint(e, delta); } @@ -459,6 +463,7 @@ void ExecutionState::increaseLevel() { if (prevPC->inst->isTerminator() && kmodule->inMainModule(kf->function)) { ++multilevel[srcbb]; + multilevelCount++; level.insert(srcbb); } if (srcbb != dstbb) { @@ -474,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 5d63a5e937..3f2466621c 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -113,11 +113,13 @@ struct ExecutionStack { call_stack_ty callStack_; info_stack_ty infoStack_; call_stack_ty uniqueFrames_; - unsigned stackBalance = 0; + 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_; } @@ -252,8 +254,6 @@ class ExecutionState { ExecutionState(const ExecutionState &state); public: - using stack_ty = ExecutionStack; - // Execution - Control Flow specific /// @brief Pointer to initial instruction @@ -267,9 +267,7 @@ class ExecutionState { KInstIterator prevPC; /// @brief Execution stack representing the current instruction stream - stack_ty stack; - - int stackBalance = 0; + ExecutionStack stack; /// @brief Remember from which Basic Block control flow arrived /// (i.e. to select the right phi values) @@ -283,6 +281,7 @@ class ExecutionState { /// @brief Exploration level, i.e., number of times KLEE cycled for this state std::unordered_map multilevel; + unsigned long multilevelCount = 0; std::unordered_set level; std::unordered_set transitionLevel; @@ -367,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; @@ -431,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; }; @@ -474,7 +477,7 @@ class ExecutionState { areTargetsChanged_ = true; } - bool reachedTarget(Target target) const; + bool reachedTarget(ref target) const; inline bool isStuck(unsigned long long bound) { KInstruction *prevKI = prevPC; diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 1b0f246135..b89c8eb483 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -11,6 +11,7 @@ #include "AddressManager.h" #include "CXXTypeSystem/CXXTypeManager.h" +#include "Composer.h" #include "Context.h" #include "CoreStats.h" #include "DistanceCalculator.h" @@ -38,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" @@ -47,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" @@ -60,6 +63,7 @@ #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" @@ -154,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 " @@ -219,6 +229,15 @@ llvm::cl::opt MinNumberElementsLazyInit( "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 { @@ -453,8 +472,9 @@ 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)}, codeGraphDistance(new CodeGraphDistance()), distanceCalculator(new DistanceCalculator(*codeGraphDistance)), @@ -464,6 +484,10 @@ Executor::Executor(LLVMContext &ctx, const InterpreterOptions &opts, 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, [&] { @@ -932,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)"); @@ -968,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); } } @@ -986,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 @@ -1016,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; @@ -1112,8 +1137,8 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, 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); @@ -1272,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) { @@ -1302,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); @@ -1315,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. @@ -1367,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(); @@ -1499,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 = @@ -1592,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; @@ -1756,7 +1783,7 @@ void Executor::unwindToNextLandingpad(ExecutionState &state) { if (popFrames) { state.popFrame(); - if (statsTracker != nullptr) { + if (statsTracker != nullptr && !state.isolated) { statsTracker->framePopped(state); } } @@ -1803,7 +1830,7 @@ void Executor::unwindToNextLandingpad(ExecutionState &state) { bindArgument(kf, 1, state, clauses_mo->getSizeExpr()); bindArgument(kf, 2, state, clauses_mo->getBaseExpr()); - if (statsTracker) { + if (statsTracker && !state.isolated) { statsTracker->framePushed( state, &state.stack.infoStack()[state.stack.size() - 2]); } @@ -2147,7 +2174,7 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f, transferToBasicBlock(&*kf->function->begin(), state.getPrevPCBlock(), state); - if (statsTracker) + if (statsTracker && !state.isolated) statsTracker->framePushed( state, &state.stack.infoStack()[state.stack.size() - 2]); @@ -2302,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 @@ -2390,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); @@ -2504,12 +2549,56 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { 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.callStack().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(), @@ -3888,46 +3977,6 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { } } -void Executor::updateStates(ExecutionState *current) { - if (targetManager) { - targetManager->update(current, addedStates, removedStates); - } - if (guidanceKind == GuidanceKind::ErrorGuidance && targetedExecutionManager) { - targetedExecutionManager->update(current, addedStates, removedStates); - } - - if (searcher) { - searcher->update(current, addedStates, removedStates); - } - - if (current && (std::find(removedStates.begin(), removedStates.end(), - current) == removedStates.end())) { - current->stepTargetsAndHistory(); - } - for (const auto state : addedStates) { - state->stepTargetsAndHistory(); - } - - 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, @@ -4026,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, @@ -4050,7 +4100,7 @@ bool Executor::checkMemoryUsage() { } void Executor::decreaseConfidenceFromStoppedStates( - SetOfStates &leftStates, HaltExecution::Reason reason) { + const SetOfStates &leftStates, HaltExecution::Reason reason) { if (targets.size() == 0) { return; } @@ -4068,20 +4118,485 @@ void Executor::decreaseConfidenceFromStoppedStates( } 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 { @@ -4107,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(); @@ -4117,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); @@ -4138,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++; @@ -4164,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(); @@ -4202,8 +4719,7 @@ void Executor::reportProgressTowardsTargets(std::string prefix, } void Executor::reportProgressTowardsTargets() const { - reportProgressTowardsTargets("paused ", pausedStates); - reportProgressTowardsTargets("", states); + reportProgressTowardsTargets("", objectManager->getStates()); } void Executor::run(std::vector initialStates) { @@ -4212,88 +4728,73 @@ void Executor::run(std::vector initialStates) { 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()); - targetManager->update(0, newStates, std::vector()); - targetedExecutionManager->update(0, newStates, - std::vector()); - searcher->update(0, newStates, std::vector()); - for (auto state : newStates) { - state->stepTargetsAndHistory(); + 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); } - // 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); - auto target = Target::create(state.prevPC->parent); - if (guidanceKind == GuidanceKind::CoverageGuidance) { - if (!target->atReturn() || - state.prevPC == target->getBlock()->getLastInstruction()) { - targetManager->setReached(target); - } - } - } - - 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) { - reportProgressTowardsTargets(); - decreaseConfidenceFromStoppedStates(states, haltExecution); + objectManager->initialUpdate(); - for (auto &startBlockAndWhiteList : targets) { - startBlockAndWhiteList.second.reportFalsePositives( - hasStateWhichCanReachSomeTarget); - } + // 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) { @@ -4309,76 +4810,48 @@ void Executor::initializeTypeManager() { typeSystemManager->initModule(); } -void Executor::executeStep(ExecutionState &state) { - KInstruction *prevKI = state.prevPC; - if (targetManager->isTargeted(state) && state.targets().empty()) { - terminateStateEarly(state, "State missed all it's targets.", - StateTerminationType::MissedAllTargets); - } else if (prevKI->inst->isTerminator() && - state.multilevel[state.getPCBlock()] > MaxCycles - 1) { - terminateStateEarly(state, "max-cycles exceeded.", - StateTerminationType::MaxCycles); - } else { - 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(); +// 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); + +// states.insert(&initialState); - TargetedSearcher *targetedSearcher = - new TargetedSearcher(Target::create(target), *distanceCalculator); - searcher = targetedSearcher; +// 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(); +// 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; +// KInstruction *ki = state.pc; - if (ki == terminator) { - *resultState = state.copy(); - terminateStateOnTerminator(state); - updateStates(&state); - haltExecution = HaltExecution::ReachedTarget; - break; - } +// if (ki == terminator) { +// *resultState = state.copy(); +// terminateStateOnTerminator(state); +// updateStates(&state); +// haltExecution = HaltExecution::ReachedTarget; +// break; +// } - executeStep(state); - } +// executeStep(state); +// } - delete searcher; - searcher = nullptr; +// delete searcher; +// searcher = nullptr; - doDumpStates(); - if (*resultState) - haltExecution = HaltExecution::NotHalt; -} +// doDumpStates(); +// if (*resultState) +// haltExecution = HaltExecution::NotHalt; +// } std::string Executor::getAddressInfo(ExecutionState &state, ref address, unsigned size, @@ -4474,22 +4947,15 @@ void Executor::terminateState(ExecutionState &state, "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) { @@ -4507,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); } @@ -4520,7 +4989,7 @@ 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(), @@ -4531,7 +5000,7 @@ void Executor::terminateStateEarly(ExecutionState &state, const Twine &message, } 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); @@ -4656,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, @@ -4691,7 +5161,9 @@ void Executor::terminateStateOnError(ExecutionState &state, true); } - targetCalculator->update(state); + if (!state.isolated) { + targetCalculator->update(state); + } terminateState(state, terminationType); if (shouldExitOn(terminationType)) @@ -4986,7 +5458,7 @@ void Executor::executeAlloc(ExecutionState &state, ref size, bool isLocal, allocationAlignment = getAllocationAlignment(allocSite); } - if (!isa(size) && !UseSymbolicSizeAllocation) { + if (!isa(size) && !UseSymbolicSizeAllocation && !state.isolated) { size = toConstant(state, size, "symbolic size allocation"); } @@ -5264,9 +5736,9 @@ MemoryObject *Executor::allocate(ExecutionState &state, ref size, ref addressSymcrete = lazyInitializationSource ? cast( - new LazyInitializedAddressSymcrete(addressArray, addressExpr)) + new LazyInitializedAddressSymcrete(addressExpr)) : cast( - new AllocAddressSymcrete(addressArray, addressExpr)); + new AllocAddressSymcrete(addressExpr)); ref sizeSymcrete = lazyInitializationSource ? cast(new LazyInitializedSizeSymcrete( @@ -5435,8 +5907,7 @@ bool Executor::resolveMemoryObjects( uint64_t minObjectSize = MinNumberElementsLazyInit * size; if (!lazyInitializeObject(state, base, target, baseTargetType, minObjectSize, false, idLazyInitialization, - /*state.isolated || UseSymbolicSizeLazyInit*/ - UseSymbolicSizeLazyInit)) { + state.isolated || UseSymbolicSizeLazyInit)) { return false; } // Lazy initialization might fail if we've got unappropriate address @@ -5488,7 +5959,7 @@ bool Executor::checkResolvedMemoryObjects( AndExpr::create(baseInBounds, mo->getBoundsCheckPointer(base, size)); } - if (hasLazyInitialized) { + if (hasLazyInitialized && !state.isolated) { baseInBounds = AndExpr::create( baseInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); } @@ -5530,9 +6001,17 @@ bool Executor::checkResolvedMemoryObjects( } else { resolveConditions.push_back(inBounds); unboundConditions.push_back(notInBounds); - if (hasLazyInitialized /*&& !state.isolated*/) { - notInBounds = AndExpr::create( - notInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); + 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; } @@ -5540,9 +6019,17 @@ bool Executor::checkResolvedMemoryObjects( if (mustBeOutOfBound) { checkOutOfBounds = Expr::createTrue(); } else { - if (hasLazyInitialized /*&& !state.isolated*/) { - notInBounds = AndExpr::create( - notInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); + 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; } @@ -5566,7 +6053,7 @@ bool Executor::checkResolvedMemoryObjects( baseInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); } - if (hasLazyInitialized && i == mayBeResolvedMemoryObjects.size() - 1) { + if (hasLazyInitialized && i == mayBeResolvedMemoryObjects.size() - 1 && !state.isolated) { baseInBounds = AndExpr::create( baseInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); } @@ -5597,10 +6084,17 @@ bool Executor::checkResolvedMemoryObjects( resolvedMemoryObjects.push_back(mo->id); unboundConditions.push_back(notInBounds); - if (hasLazyInitialized && - i == mayBeResolvedMemoryObjects.size() - 1 /*&& !state.isolated*/) { - notInBounds = AndExpr::create( - notInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); + 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) { @@ -5723,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 */, @@ -5823,8 +6341,6 @@ void Executor::executeMemoryOperation( if (base != address || size != bytes) { baseInBounds = AndExpr::create(baseInBounds, mo->getBoundsCheckPointer(base, size)); - baseInBounds = AndExpr::create( - baseInBounds, Expr::createIsZero(mo->getOffsetExpr(base))); } inBounds = AndExpr::create(inBounds, baseInBounds); @@ -5838,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); @@ -6178,7 +6694,7 @@ IDType Executor::lazyInitializeLocalObject(ExecutionState &state, bool success = lazyInitializeObject( state, address, target, typeSystemManager->getWrappedType(ai->getType()), elementSize, true, id, - /*state.isolated || UseSymbolicSizeLazyInit*/ UseSymbolicSizeLazyInit); + state.isolated || UseSymbolicSizeLazyInit); assert(success); assert(id); auto op = state.addressSpace.findObject(id); @@ -6281,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(); @@ -6462,17 +6978,21 @@ 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( @@ -6514,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); @@ -6523,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(); @@ -6540,47 +7059,50 @@ 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; - ref targets(new TargetForest()); - targets->add(Target::create(target)); - prepareTargetedExecution(*state, targets); - targetedRun(*state, target, &initialState); - state = initialState; - if (state) { - auto frame = state->stack.callStack().back(); - caller = frame.caller; - state->popFrame(); - delete original; - } else { - state = original; - state->popFrame(); - } - - processForest = nullptr; - - if (statsTracker) - statsTracker->done(); - - return state; -} +// 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) { @@ -7111,7 +7633,7 @@ 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.callStack().begin(); diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index ce983b0b86..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,6 +28,7 @@ #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" @@ -95,8 +98,6 @@ 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 @@ -104,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; @@ -111,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; @@ -126,7 +134,7 @@ class Executor : public Interpreter { std::unique_ptr kmodule; InterpreterHandler *interpreterHandler; - Searcher *searcher; + std::unique_ptr searcher; ExternalDispatcher *externalDispatcher; TimingSolver *solver; @@ -134,8 +142,8 @@ 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; @@ -146,17 +154,6 @@ class Executor : public Interpreter { std::unique_ptr targetCalculator; std::unique_ptr targetManager; - /// 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; - /// When non-empty the Executor is running in "seed" mode. The /// states in this map will be executed in an arbitrary order /// (outside the normal search interface) until they terminate. When @@ -164,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; @@ -237,6 +234,13 @@ 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; @@ -248,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(); @@ -271,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, @@ -384,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, @@ -618,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, ref whitelist); void increaseProgressVelocity(ExecutionState &state, KBlock *block); + void updateConfidenceRates(); + void decreaseConfidenceFromStoppedStates( - SetOfStates &leftStates, + const SetOfStates &leftStates, HaltExecution::Reason reason = HaltExecution::NotHalt); void checkNullCheckAfterDeref(ref cond, ExecutionState &state, @@ -663,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; @@ -780,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 8a1997dc9a..fac197b113 100644 --- a/lib/Core/Memory.h +++ b/lib/Core/Memory.h @@ -242,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/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/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/StatsTracker.cpp b/lib/Core/StatsTracker.cpp index 3ef6013971..5099207302 100644 --- a/lib/Core/StatsTracker.cpp +++ b/lib/Core/StatsTracker.cpp @@ -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; + } } } } @@ -606,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()); @@ -648,8 +651,10 @@ 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; @@ -1089,8 +1094,10 @@ 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; diff --git a/lib/Core/TargetManager.cpp b/lib/Core/TargetManager.cpp index 17d19ad720..8d5e123573 100644 --- a/lib/Core/TargetManager.cpp +++ b/lib/Core/TargetManager.cpp @@ -59,7 +59,7 @@ void TargetManager::updateDone(ExecutionState &state, ref target) { } void TargetManager::updateTargets(ExecutionState &state) { - if (guidance == Interpreter::GuidanceKind::CoverageGuidance) { + if (!state.isolated && guidance == Interpreter::GuidanceKind::CoverageGuidance) { if (targets(state).empty() && state.isStuck(MaxCyclesBeforeStuck)) { state.setTargeted(true); } @@ -123,8 +123,29 @@ void TargetManager::update(ExecutionState *current, } 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(); -} \ No newline at end of file +} + +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 index 5975539e13..ae2a0051ce 100644 --- a/lib/Core/TargetManager.h +++ b/lib/Core/TargetManager.h @@ -13,6 +13,7 @@ #include "DistanceCalculator.h" +#include "ObjectManager.h" #include "klee/Core/Interpreter.h" #include "klee/Module/TargetHash.h" @@ -25,7 +26,7 @@ namespace klee { class TargetCalculator; -class TargetManager { +class TargetManager final : public Subscriber { private: using StatesSet = std::unordered_set; @@ -63,6 +64,8 @@ class TargetManager { 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); } diff --git a/lib/Core/TargetedExecutionManager.cpp b/lib/Core/TargetedExecutionManager.cpp index 0495089249..5498aa1907 100644 --- a/lib/Core/TargetedExecutionManager.cpp +++ b/lib/Core/TargetedExecutionManager.cpp @@ -16,6 +16,7 @@ #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 @@ -589,4 +590,12 @@ void TargetedExecutionManager::update( } localStates.clear(); -} \ No newline at end of file +} + +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 c16ae8e029..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" @@ -101,7 +102,7 @@ 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, @@ -150,6 +151,7 @@ class TargetedExecutionManager { distanceCalculator(distanceCalculator_), targetManager(targetManager_) { } ~TargetedExecutionManager() = default; + std::map, KFunctionLess> prepareTargets(KModule *kmodule, SarifReport paths); @@ -161,6 +163,8 @@ class TargetedExecutionManager { void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates); + + void update(ref e) override; }; } // namespace klee diff --git a/lib/Core/UserSearcher.cpp b/lib/Core/UserSearcher.cpp index 07eb2ba2b5..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" @@ -76,6 +77,12 @@ cl::opt BatchTime( "--use-batching-search. Set to 0s to disable (default=5s)"), cl::init("5s"), cl::cat(SearchCat)); +cl::opt + 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() { @@ -185,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/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 a52db8dd2d..0c5b35c58f 100644 --- a/lib/Expr/Constraints.cpp +++ b/lib/Expr/Constraints.cpp @@ -260,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; } @@ -289,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); } } @@ -335,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, {}); diff --git a/lib/Expr/ExprPPrinter.cpp b/lib/Expr/ExprPPrinter.cpp index b8fc326ef1..4684f3f403 100644 --- a/lib/Expr/ExprPPrinter.cpp +++ b/lib/Expr/ExprPPrinter.cpp @@ -642,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/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/Runner/run_klee.cpp b/lib/Runner/run_klee.cpp index f95c09d8df..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,7 +374,9 @@ 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); @@ -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 != ""; @@ -1906,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/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/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