diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 58ad51bafa..be44b5b4bd 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -52,7 +52,7 @@ jobs: "Latest klee-uclibc", "Asserts enabled", "No TCMalloc, optimised runtime", - ] + ] include: - name: "LLVM 13" env: @@ -148,6 +148,8 @@ jobs: steps: - name: Checkout KLEE source code uses: actions/checkout@v2 + with: + submodules: recursive - name: Build KLEE env: ${{ matrix.env }} run: scripts/build/build.sh klee --docker --create-final-image @@ -167,6 +169,8 @@ jobs: run: brew install bash - name: Checkout KLEE source code uses: actions/checkout@v2 + with: + submodules: recursive - name: Build KLEE run: scripts/build/build.sh klee --debug --install-system-deps - name: Run tests @@ -177,6 +181,8 @@ jobs: steps: - name: Checkout KLEE Code uses: actions/checkout@v2 + with: + submodules: recursive - name: Build Docker image run: docker build . @@ -189,6 +195,8 @@ jobs: steps: - name: Checkout KLEE source code uses: actions/checkout@v2 + with: + submodules: recursive - name: Build KLEE run: scripts/build/build.sh klee --docker --create-final-image - name: Run tests diff --git a/.gitignore b/.gitignore index 9dc7e8df5d..e27fc5e755 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,5 @@ autoconf/aclocal.m4 autoconf/autom4te.cache/ autom4te.cache/ -.vscode/launch.json -.vscode/settings.json +.vscode/ .cache/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..d21b6002fc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "json"] + path = json + url = https://github.com/nlohmann/json.git +[submodule "optional"] + path = optional + url = https://github.com/martinmoene/optional-lite.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 110155b610..c8174ff7a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -724,6 +724,8 @@ configure_file(${CMAKE_SOURCE_DIR}/include/klee/Config/CompileTimeInfo.h.cmin ################################################################################ include_directories("${CMAKE_BINARY_DIR}/include") include_directories("${CMAKE_SOURCE_DIR}/include") +include_directories("${CMAKE_SOURCE_DIR}/json/include") +include_directories("${CMAKE_SOURCE_DIR}/optional/include") ################################################################################ # Set default location for targets in the build directory diff --git a/include/klee/Core/Interpreter.h b/include/klee/Core/Interpreter.h index dde7f8fa43..42b7aaf08f 100644 --- a/include/klee/Core/Interpreter.h +++ b/include/klee/Core/Interpreter.h @@ -9,15 +9,25 @@ #ifndef KLEE_INTERPRETER_H #define KLEE_INTERPRETER_H +#include "TerminationTypes.h" + +#include "klee/Module/SarifReport.h" + #include #include #include #include +#include #include +#include + +using nonstd::optional; + struct KTest; namespace llvm { +class BasicBlock; class Function; class LLVMContext; class Module; @@ -27,8 +37,10 @@ class raw_fd_ostream; namespace klee { class ExecutionState; +struct SarifReport; class Interpreter; class TreeStreamWriter; +class InstructionInfoTable; class InterpreterHandler { public: @@ -50,6 +62,14 @@ class InterpreterHandler { class Interpreter { public: + enum class GuidanceKind { + NoGuidance, // Default symbolic execution + CoverageGuidance, // Use GuidedSearcher and guidedRun to maximize full code + // coverage + ErrorGuidance // Use GuidedSearcher and guidedRun to maximize specified + // targets coverage + }; + /// ModuleOptions - Module level options which can be set when /// registering a module with the interpreter. struct ModuleOptions { @@ -57,18 +77,21 @@ class Interpreter { std::string EntryPoint; std::string OptSuffix; bool Optimize; + bool Simplify; bool CheckDivZero; bool CheckOvershift; bool WithFPRuntime; + bool WithPOSIXRuntime; ModuleOptions(const std::string &_LibraryDir, const std::string &_EntryPoint, const std::string &_OptSuffix, - bool _Optimize, bool _CheckDivZero, bool _CheckOvershift, - bool _WithFPRuntime) + bool _Optimize, bool _Simplify, bool _CheckDivZero, + bool _CheckOvershift, bool _WithFPRuntime, + bool _WithPOSIXRuntime) : LibraryDir(_LibraryDir), EntryPoint(_EntryPoint), - OptSuffix(_OptSuffix), Optimize(_Optimize), + OptSuffix(_OptSuffix), Optimize(_Optimize), Simplify(_Simplify), CheckDivZero(_CheckDivZero), CheckOvershift(_CheckOvershift), - WithFPRuntime(_WithFPRuntime) {} + WithFPRuntime(_WithFPRuntime), WithPOSIXRuntime(_WithPOSIXRuntime) {} }; enum LogType { @@ -84,12 +107,16 @@ class Interpreter { /// symbolic values. This is used to test the correctness of the /// symbolic execution on concrete programs. unsigned MakeConcreteSymbolic; + GuidanceKind Guidance; + nonstd::optional Paths; - InterpreterOptions() : MakeConcreteSymbolic(false) {} + InterpreterOptions(nonstd::optional Paths) + : MakeConcreteSymbolic(false), Guidance(GuidanceKind::NoGuidance), + Paths(std::move(Paths)) {} }; protected: - const InterpreterOptions interpreterOpts; + const InterpreterOptions &interpreterOpts; Interpreter(const InterpreterOptions &_interpreterOpts) : interpreterOpts(_interpreterOpts) {} @@ -112,7 +139,8 @@ class Interpreter { setModule(std::vector> &userModules, std::vector> &libsModules, const ModuleOptions &opts, - const std::vector &mainModuleFunctions) = 0; + const std::vector &mainModuleFunctions, + std::unique_ptr origInfos) = 0; // supply a tree stream writer which the interpreter will use // to record the concrete path (as a stream of '0' and '1' bytes). @@ -140,12 +168,14 @@ class Interpreter { /*** Runtime options ***/ - virtual void setHaltExecution(bool value) = 0; + virtual void setHaltExecution(HaltExecution::Reason value) = 0; virtual void setInhibitForking(bool value) = 0; virtual void prepareForEarlyExit() = 0; + virtual bool hasTargetForest() const = 0; + /*** State accessor methods ***/ virtual unsigned getPathStreamID(const ExecutionState &state) = 0; diff --git a/include/klee/Core/TargetedExecutionReporter.h b/include/klee/Core/TargetedExecutionReporter.h new file mode 100644 index 0000000000..790388df5e --- /dev/null +++ b/include/klee/Core/TargetedExecutionReporter.h @@ -0,0 +1,38 @@ +//===-- TargetedExecutionReporter.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_TARGETEDEXECUTIONREPORTER_H +#define KLEE_TARGETEDEXECUTIONREPORTER_H + +#include "klee/Module/SarifReport.h" + +#include + +namespace klee { + +namespace confidence { +using ty = double; +static ty MinConfidence = 0.0; +static ty MaxConfidence = 100.0; +static ty Confident = 90.0; +static ty VeryConfident = 99.0; +bool isConfident(ty conf); +bool isVeryConfident(ty conf); +bool isNormal(ty conf); +std::string toString(ty conf); +ty min(ty left, ty right); +}; // namespace confidence + +void reportFalsePositive(confidence::ty confidence, + const std::unordered_set &errors, + unsigned id, std::string whatToIncrease); + +} // namespace klee + +#endif diff --git a/include/klee/Core/TerminationTypes.h b/include/klee/Core/TerminationTypes.h index 2c77c05cf1..2735c4eb3c 100644 --- a/include/klee/Core/TerminationTypes.h +++ b/include/klee/Core/TerminationTypes.h @@ -33,7 +33,8 @@ TTYPE(ReadOnly, 17U, "read_only.err") \ TTYPE(ReportError, 18U, "report_error.err") \ TTYPE(UndefinedBehavior, 19U, "undefined_behavior.err") \ - MARK(PROGERR, 19U) \ + TTYPE(InternalOutOfMemory, 20U, "out_of_memory.er") \ + MARK(PROGERR, 20U) \ TTYPE(User, 23U, "user.err") \ MARK(USERERR, 23U) \ TTYPE(Execution, 25U, "exec.err") \ @@ -42,7 +43,8 @@ TTYPE(Replay, 27U, "") \ TTYPE(Merge, 28U, "") \ TTYPE(SilentExit, 29U, "") \ - MARK(END, 29U) + TTYPE(Paused, 30U, "") \ + MARK(END, 30U) ///@brief Reason an ExecutionState got terminated. enum class StateTerminationType : std::uint8_t { @@ -53,4 +55,23 @@ enum class StateTerminationType : std::uint8_t { #undef MARK }; +namespace HaltExecution { +enum Reason { + NotHalt = 0, + MaxTests, + MaxInstructions, + MaxSteppedInstructions, + MaxTime, + CovCheck, + NoMoreStates, + ReachedTarget, + ErrorOnWhichShouldExit, + Interrupt, + MaxDepth, + MaxStackFrames, + MaxSolverTime, + Unspecified +}; +}; + #endif diff --git a/include/klee/Expr/Assignment.h b/include/klee/Expr/Assignment.h index 7e2e7b7cf7..5caf6b2cf9 100644 --- a/include/klee/Expr/Assignment.h +++ b/include/klee/Expr/Assignment.h @@ -36,17 +36,14 @@ class Assignment { Assignment(const bindings_ty &_bindings, bool _allowFreeValues = false) : allowFreeValues(_allowFreeValues), bindings(_bindings) {} Assignment(const std::vector &objects, - std::vector> &values, + const std::vector> &values, bool _allowFreeValues = false) : allowFreeValues(_allowFreeValues) { - std::vector>::iterator valIt = values.begin(); - for (std::vector::const_iterator it = objects.begin(), - ie = objects.end(); - it != ie; ++it) { - const Array *os = *it; - SparseStorage &arr = *valIt; + assert(objects.size() == values.size()); + for (unsigned i = 0; i < values.size(); ++i) { + const Array *os = objects.at(i); + const SparseStorage &arr = values.at(i); bindings.insert(std::make_pair(os, arr)); - ++valIt; } } diff --git a/include/klee/Expr/SourceBuilder.h b/include/klee/Expr/SourceBuilder.h index 94b25b02e9..99349558fa 100644 --- a/include/klee/Expr/SourceBuilder.h +++ b/include/klee/Expr/SourceBuilder.h @@ -10,7 +10,10 @@ class SourceBuilder { private: static ref constantSource; static ref makeSymbolicSource; - static ref lazyInitializationMakeSymbolicSource; + static ref symbolicAddressSource; + static ref lazyInitializationSymbolicSource; + static ref irreproducibleSource; + static ref symbolicValueSource; public: SourceBuilder() = delete; @@ -20,7 +23,9 @@ class SourceBuilder { static ref makeSymbolic(); static ref symbolicAddress(); static ref symbolicSize(); - static ref lazyInitializationMakeSymbolic(); + static ref lazyInitializationSymbolic(); + static ref irreproducible(); + static ref symbolicValue(); }; }; // namespace klee diff --git a/include/klee/Expr/SymbolicSource.h b/include/klee/Expr/SymbolicSource.h index dc53e4db3e..1b70205192 100644 --- a/include/klee/Expr/SymbolicSource.h +++ b/include/klee/Expr/SymbolicSource.h @@ -19,7 +19,9 @@ class SymbolicSource { LazyInitializationSymbolic, MakeSymbolic, SymbolicAddress, - SymbolicSize + SymbolicSize, + Irreproducible, + SymbolicValue }; public: @@ -120,7 +122,7 @@ class LazyInitializationSymbolicSource : public SymbolicSource { public: Kind getKind() const override { return Kind::LazyInitializationSymbolic; } virtual std::string getName() const override { - return "lazyInitializationMakeSymbolic"; + return "lazyInitializationSymbolic"; } virtual bool isSymcrete() const override { return false; } @@ -129,6 +131,30 @@ class LazyInitializationSymbolicSource : public SymbolicSource { } }; +class IrreproducibleSource : public SymbolicSource { +public: + Kind getKind() const override { return Kind::Irreproducible; } + virtual std::string getName() const override { return "irreproducible"; } + virtual bool isSymcrete() const override { return false; } + + static bool classof(const SymbolicSource *S) { + return S->getKind() == Kind::Irreproducible; + } + static bool classof(const IrreproducibleSource *) { return true; } +}; + +class SymbolicValueSource : public SymbolicSource { +public: + Kind getKind() const override { return Kind::SymbolicValue; } + virtual std::string getName() const override { return "symbolicValue"; } + virtual bool isSymcrete() const override { return false; } + + static bool classof(const SymbolicSource *S) { + return S->getKind() == Kind::SymbolicValue; + } + static bool classof(const SymbolicValueSource *) { return true; } +}; + } // namespace klee #endif /* KLEE_SYMBOLICSOURCE_H */ diff --git a/include/klee/Module/InstructionInfoTable.h b/include/klee/Module/InstructionInfoTable.h index 06ae59f2f3..09575bf29b 100644 --- a/include/klee/Module/InstructionInfoTable.h +++ b/include/klee/Module/InstructionInfoTable.h @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace llvm { @@ -68,20 +69,31 @@ struct FunctionInfo { }; class InstructionInfoTable { +public: + using Instructions = std::unordered_map< + std::string, + std::unordered_map< + unsigned int, + std::unordered_map>>>; + +private: std::unordered_map> infos; std::unordered_map> functionInfos; std::vector> internedStrings; + Instructions insts; public: explicit InstructionInfoTable( - const llvm::Module &m, std::unique_ptr assemblyFS); + const llvm::Module &m, std::unique_ptr assemblyFS, + bool withInstructions = false); unsigned getMaxID() const; const InstructionInfo &getInfo(const llvm::Instruction &) const; const FunctionInfo &getFunctionInfo(const llvm::Function &) const; + Instructions getInstructions(); }; } // namespace klee diff --git a/include/klee/Module/KModule.h b/include/klee/Module/KModule.h index 6b496f3377..849fed39a3 100644 --- a/include/klee/Module/KModule.h +++ b/include/klee/Module/KModule.h @@ -12,13 +12,17 @@ #include "klee/Config/Version.h" #include "klee/Core/Interpreter.h" +#include "klee/Module/InstructionInfoTable.h" #include "klee/Module/KCallable.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/CFG.h" +#include #include #include #include +#include #include #include #include @@ -42,7 +46,6 @@ struct Cell; class Executor; class Expr; class InterpreterHandler; -class InstructionInfoTable; struct KInstruction; class KModule; struct KFunction; @@ -86,13 +89,13 @@ struct KBlock { struct KCallBlock : KBlock { KInstruction *kcallInstruction; - llvm::Function *calledFunction; + std::set calledFunctions; public: KCallBlock(KFunction *, llvm::BasicBlock *, KModule *, std::unordered_map &, - std::unordered_map &, llvm::Function *, - KInstruction **); + std::unordered_map &, + std::set, KInstruction **); static bool classof(const KCallBlock *) { return true; } static bool classof(const KBlock *E) { return E->getKBlockType() == KBlockType::Call; @@ -145,7 +148,9 @@ struct KFunction : public KCallable { unsigned getArgRegister(unsigned index) const { return index; } - llvm::StringRef getName() const override { return function->getName(); } + llvm::StringRef getName() const override { + return function ? function->getName() : ""; + } llvm::FunctionType *getFunctionType() const override { return function->getFunctionType(); @@ -174,6 +179,9 @@ class KConstant { }; class KModule { +private: + bool withPosixRuntime; + public: std::unique_ptr module; std::unique_ptr targetData; @@ -189,6 +197,8 @@ class KModule { std::vector mainModuleFunctions; + InstructionInfoTable::Instructions origInfos; + std::unique_ptr infos; std::vector constants; @@ -201,6 +211,7 @@ class KModule { // Functions which are part of KLEE runtime std::set internalFunctions; + // Mark function with functionName as part of the KLEE runtime void addInternalFunction(const char *functionName); // Replace std functions with KLEE intrinsics void replaceFunction(const std::unique_ptr &m, @@ -248,6 +259,8 @@ class KModule { KBlock *getKBlock(llvm::BasicBlock *bb); bool inMainModule(llvm::Function *f); + + bool WithPOSIXRuntime() { return withPosixRuntime; } }; } // namespace klee diff --git a/include/klee/Module/SarifReport.h b/include/klee/Module/SarifReport.h new file mode 100644 index 0000000000..1bbac92eef --- /dev/null +++ b/include/klee/Module/SarifReport.h @@ -0,0 +1,272 @@ +//===-- SarifReport.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_SARIF_REPORT_H +#define KLEE_SARIF_REPORT_H + +#include +#include +#include + +#include "klee/ADT/Ref.h" +#include +#include + +using json = nlohmann::json; +using nonstd::optional; + +namespace nlohmann { +template struct adl_serializer> { + static void to_json(json &j, const nonstd::optional &opt) { + if (opt == nonstd::nullopt) { + j = nullptr; + } else { + j = *opt; + } + } + + static void from_json(const json &j, nonstd::optional &opt) { + if (j.is_null()) { + opt = nonstd::nullopt; + } else { + opt = j.get(); + } + } +}; +} // namespace nlohmann + +namespace klee { +enum ReachWithError { + DoubleFree = 0, + UseAfterFree, + NullPointerException, + NullCheckAfterDerefException, + Reachable, + None, +}; + +static const char *ReachWithErrorNames[] = { + "DoubleFree", + "UseAfterFree", + "NullPointerException", + "NullCheckAfterDerefException", + "Reachable", + "None", +}; + +const char *getErrorString(ReachWithError error); +std::string getErrorsString(const std::unordered_set &errors); + +struct FunctionInfo; +struct KBlock; + +struct ArtifactLocationJson { + optional uri; +}; + +struct RegionJson { + optional startLine; + optional endLine; + optional startColumn; + optional endColumn; +}; + +struct PhysicalLocationJson { + optional artifactLocation; + optional region; +}; + +struct LocationJson { + optional physicalLocation; +}; + +struct ThreadFlowLocationJson { + optional location; + optional metadata; +}; + +struct ThreadFlowJson { + std::vector locations; +}; + +struct CodeFlowJson { + std::vector threadFlows; +}; + +struct Message { + std::string text; +}; + +struct ResultJson { + optional ruleId; + optional message; + std::vector locations; + std::vector codeFlows; +}; + +struct DriverJson { + std::string name; +}; + +struct ToolJson { + DriverJson driver; +}; + +struct RunJson { + std::vector results; + ToolJson tool; +}; + +struct SarifReportJson { + std::vector runs; +}; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ArtifactLocationJson, uri) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(RegionJson, startLine, endLine, + startColumn, endColumn) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(PhysicalLocationJson, + artifactLocation, region) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(LocationJson, physicalLocation) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ThreadFlowLocationJson, + location, metadata) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ThreadFlowJson, locations) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(CodeFlowJson, threadFlows) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Message, text) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ResultJson, ruleId, message, + codeFlows, locations) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(DriverJson, name) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ToolJson, driver) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(RunJson, results, tool) + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(SarifReportJson, runs) + +struct Location { + struct LocationHash { + std::size_t operator()(const Location *l) const { return l->hash(); } + }; + + struct LocationCmp { + bool operator()(const Location *a, const Location *b) const { + return a == b; + } + }; + + struct EquivLocationCmp { + bool operator()(const Location *a, const Location *b) const { + if (a == NULL || b == NULL) + return false; + return *a == *b; + } + }; + std::string filename; + unsigned int startLine; + unsigned int endLine; + optional startColumn; + optional endColumn; + + static ref create(std::string filename_, unsigned int startLine_, + optional endLine_, + optional startColumn_, + optional endColumn_); + + ~Location(); + std::size_t hash() const { return hashValue; } + + /// @brief Required by klee::ref-managed objects + class ReferenceCounter _refCount; + + bool operator==(const Location &other) const { + return filename == other.filename && startLine == other.startLine && + endLine == other.endLine && startColumn == other.startColumn && + endColumn == other.endColumn; + } + + bool isInside(const FunctionInfo &info) const; + + using Instructions = std::unordered_map< + unsigned int, + std::unordered_map>>; + + bool isInside(KBlock *block, const Instructions &origInsts) const; + + std::string toString() const; + +private: + typedef std::unordered_set + EquivLocationHashSet; + typedef std::unordered_set + LocationHashSet; + + static EquivLocationHashSet cachedLocations; + static LocationHashSet locations; + + size_t hashValue = 0; + void computeHash() { + hash_combine(hashValue, filename); + hash_combine(hashValue, startLine); + hash_combine(hashValue, endLine); + hash_combine(hashValue, startColumn); + hash_combine(hashValue, endColumn); + } + + template inline void hash_combine(std::size_t &s, const T &v) { + std::hash h; + s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2); + } + + Location(std::string filename_, unsigned int startLine_, + optional endLine_, optional startColumn_, + optional endColumn_) + : filename(filename_), startLine(startLine_), + endLine(endLine_.has_value() ? *endLine_ : startLine_), + startColumn(startColumn_), + endColumn(endColumn_.has_value() ? endColumn_ : startColumn_) { + computeHash(); + } +}; + +struct RefLocationHash { + unsigned operator()(const ref &t) const { return t->hash(); } +}; + +struct RefLocationCmp { + bool operator()(const ref &a, const ref &b) const { + return a.get() == b.get(); + } +}; + +struct Result { + std::vector> locations; + std::vector> metadatas; + unsigned id; + std::unordered_set errors; +}; + +struct SarifReport { + std::vector results; + + bool empty() const { return results.empty(); } +}; + +SarifReport convertAndFilterSarifJson(const SarifReportJson &reportJson); + +} // namespace klee + +#endif /* KLEE_SARIF_REPORT_H */ diff --git a/include/klee/Module/Target.h b/include/klee/Module/Target.h new file mode 100644 index 0000000000..086b52c697 --- /dev/null +++ b/include/klee/Module/Target.h @@ -0,0 +1,106 @@ +//===-- Target.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_TARGET_H +#define KLEE_TARGET_H + +#include "klee/ADT/RNG.h" +#include "klee/ADT/Ref.h" +#include "klee/Module/KModule.h" +#include "klee/Module/SarifReport.h" +#include "klee/System/Time.h" + +#include "klee/Support/OptionCategories.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include +#include + +namespace klee { +struct EquivTargetCmp; +struct TargetHash; +struct TargetCmp; + +using nonstd::optional; + +struct ErrorLocation { + unsigned int startLine; + unsigned int endLine; + optional startColumn; + optional endColumn; +}; + +struct Target { +private: + typedef std::unordered_set + EquivTargetHashSet; + typedef std::unordered_set TargetHashSet; + static EquivTargetHashSet cachedTargets; + static TargetHashSet targets; + KBlock *block; + std::unordered_set + 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::unordered_set &_errors, + unsigned _id, optional _loc, KBlock *_block) + : block(_block), errors(_errors), id(_id), loc(_loc) {} + + static ref getFromCacheOrReturn(Target *target); + +public: + bool isReported = false; + /// @brief Required by klee::ref-managed objects + class ReferenceCounter _refCount; + + static ref create(const std::unordered_set &_errors, + unsigned _id, optional _loc, + KBlock *_block); + static ref create(KBlock *_block); + + int compare(const Target &other) const; + + bool equals(const Target &other) const; + + bool operator<(const Target &other) const; + + bool operator==(const Target &other) const; + + bool atReturn() const { return isa(block); } + + KBlock *getBlock() const { return block; } + + bool isNull() const { return block == nullptr; } + + explicit operator bool() const noexcept { return !isNull(); } + + unsigned hash() const { return reinterpret_cast(block); } + + const std::unordered_set &getErrors() const { return errors; } + bool isThatError(ReachWithError err) const { return errors.count(err) != 0; } + bool shouldFailOnThisTarget() const { + return errors.count(ReachWithError::None) == 0; + } + + bool isTheSameAsIn(KInstruction *instr) const; + + unsigned getId() const { return id; } + + std::string toString() const; + ~Target(); +}; +} // namespace klee + +#endif /* KLEE_TARGET_H */ diff --git a/include/klee/Module/TargetForest.h b/include/klee/Module/TargetForest.h new file mode 100644 index 0000000000..91cd347309 --- /dev/null +++ b/include/klee/Module/TargetForest.h @@ -0,0 +1,382 @@ +//===-- TargetForest.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 represent prefix tree of Targets +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_TARGETFOREST_H +#define KLEE_TARGETFOREST_H + +#include "klee/ADT/Ref.h" +#include "klee/Core/TargetedExecutionReporter.h" +#include "klee/Expr/Expr.h" +#include "klee/Module/KModule.h" +#include "klee/Module/Target.h" +#include "klee/Module/TargetHash.h" + +#include +#include +#include + +namespace klee { +struct RefTargetHash; +struct RefTargetCmp; +struct TargetsHistoryHash; +struct EquivTargetsHistoryCmp; +struct TargetsHistoryCmp; +struct UnorderedTargetsSetHash; +struct UnorderedTargetsSetCmp; +struct EquivUnorderedTargetsSetCmp; +struct RefUnorderedTargetsSetHash; +struct RefUnorderedTargetsSetCmp; + +class TargetForest { +public: + using TargetsSet = + std::unordered_set, RefTargetHash, RefTargetCmp>; + + class UnorderedTargetsSet { + public: + static ref create(const ref &target); + static ref create(const TargetsSet &targets); + + ~UnorderedTargetsSet(); + std::size_t hash() const { return hashValue; } + bool operator==(const UnorderedTargetsSet &other) const { + return targetsVec == other.targetsVec; + } + + const std::vector> &getTargets() const { return targetsVec; } + + std::string toString() const { + std::stringstream ss; + for (const auto &target : getTargets()) { + ss << target->toString() << '\n'; + } + return ss.str(); + } + + /// @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); + + typedef std::unordered_set + EquivUnorderedTargetsSetHashSet; + typedef std::unordered_set + UnorderedTargetsSetHashSet; + + static EquivUnorderedTargetsSetHashSet cachedVectors; + static UnorderedTargetsSetHashSet vectors; + std::vector> targetsVec; + std::size_t hashValue; + + void sortAndComputeHash(); + }; + + struct RefUnorderedTargetsSetHash { + unsigned operator()(const ref &t) const { + return t->hash(); + } + }; + + struct RefUnorderedTargetsSetCmp { + bool operator()(const ref &a, + const ref &b) const { + return a.get() == b.get(); + } + }; + +private: + using TargetToStateSetMap = + std::unordered_map, std::unordered_set, + RefTargetHash, RefTargetCmp>; + class Layer { + using InternalLayer = + std::unordered_map, ref, + RefUnorderedTargetsSetHash, + RefUnorderedTargetsSetCmp>; + InternalLayer forest; + using TargetsToVector = std::unordered_map< + ref, + std::unordered_set, RefUnorderedTargetsSetHash, + RefUnorderedTargetsSetCmp>, + RefTargetHash, RefTargetCmp>; + TargetsToVector targetsToVector; + + /// @brief Confidence in % that this layer (i.e., parent target node) can be + /// reached + confidence::ty confidence; + + Layer(const InternalLayer &forest, const TargetsToVector &targetsToVector, + 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()) {} + void unionWith(Layer *other); + void block(ref target); + void removeTarget(ref target); + Layer *removeChild(ref child) const; + + confidence::ty getConfidence(confidence::ty parentConfidence) const { + return confidence::min(parentConfidence, confidence); + } + + void collectHowManyEventsInTracesWereReached( + std::unordered_map> + &traceToEventCount, + unsigned reached, unsigned total) const; + + public: + using iterator = TargetsToVector::const_iterator; + + /// @brief Required by klee::ref-managed objects + class ReferenceCounter _refCount; + + explicit Layer() : confidence(confidence::MaxConfidence) {} + + iterator find(ref b) const { return targetsToVector.find(b); } + iterator begin() const { return targetsToVector.begin(); } + iterator end() const { return targetsToVector.end(); } + void insert(ref loc, ref nextLayer) { + forest[loc] = nextLayer; + } + void insertTargetsToVec(ref target, + ref targetsVec) { + targetsToVector[target].insert(targetsVec); + } + bool empty() const { return forest.empty(); } + bool deepFind(ref target) const; + bool deepFindIn(ref child, ref target) const; + size_t size() const { return forest.size(); } + Layer *replaceChildWith( + ref child, + const std::unordered_set, + RefUnorderedTargetsSetHash, + RefUnorderedTargetsSetCmp> &other) const; + Layer *replaceChildWith(ref child, Layer *other) const; + Layer *removeChild(ref child) const; + Layer *addChild(ref child) const; + Layer *blockLeafInChild(ref child, + ref leaf) const; + Layer *blockLeafInChild(ref child, ref leaf) const; + Layer *blockLeaf(ref leaf) const; + bool allNodesRefCountOne() const; + 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); + Layer * + divideConfidenceBy(const TargetToStateSetMap &reachableStatesOfTarget); + confidence::ty getConfidence() const { + return getConfidence(confidence::MaxConfidence); + } + void collectHowManyEventsInTracesWereReached( + std::unordered_map> + &traceToEventCount) const { + collectHowManyEventsInTracesWereReached(traceToEventCount, 0, 0); + } + + void addTrace( + const Result &result, + const std::unordered_map, std::unordered_set, + RefLocationHash, RefLocationCmp> &locToBlocks); + }; + + ref forest; + + bool allNodesRefCountOne() const; + +public: + class History { + private: + typedef std::unordered_set + EquivTargetsHistoryHashSet; + typedef std::unordered_set + TargetsHistoryHashSet; + + static EquivTargetsHistoryHashSet cachedHistories; + static TargetsHistoryHashSet histories; + unsigned hashValue; + + explicit History(ref _target, ref _visitedTargets) + : target(_target), visitedTargets(_visitedTargets) { + computeHash(); + } + + public: + const ref target; + const ref visitedTargets; + + static ref create(ref _target, + ref _visitedTargets); + static ref create(ref _target); + static ref create(); + + ref add(ref _target) { + return History::create(_target, this); + } + + unsigned hash() const { return hashValue; } + + int compare(const History &h) const; + bool equals(const History &h) const; + + void computeHash() { + unsigned res = 0; + if (target) { + res = target->hash() * Expr::MAGIC_HASH_CONSTANT; + } + if (visitedTargets) { + res ^= visitedTargets->hash() * Expr::MAGIC_HASH_CONSTANT; + } + hashValue = res; + } + + void dump() const; + + ~History(); + + /// @brief Required by klee::ref-managed objects + class ReferenceCounter _refCount; + }; + +private: + ref history; + 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( + const Result &result, + const std::unordered_map, std::unordered_set, + RefLocationHash, RefLocationCmp> &locToBlocks) { + forest->addTrace(result, locToBlocks); + } + + TargetForest(ref layer, KFunction *entryFunction) + : forest(layer), history(History::create()), + entryFunction(entryFunction) {} + TargetForest() : TargetForest(new Layer(), nullptr) {} + TargetForest(KFunction *entryFunction) + : TargetForest(new Layer(), entryFunction) {} + + bool empty() const { return forest.isNull() || forest->empty(); } + Layer::iterator begin() const { return forest->begin(); } + Layer::iterator end() const { return forest->end(); } + bool deepContains(ref b) { return forest->deepFind(b); } + bool contains(ref b) { return forest->find(b) != forest->end(); } + + /// @brief Number of children of this layer (immediate successors) + size_t successorCount() const { return forest->size(); } + + void stepTo(ref); + void add(ref); + void remove(ref); + void blockIn(ref, ref); + const ref getHistory() { return history; }; + const ref getTargets() { return forest; }; + 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); + } + 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; + } +}; + +struct TargetsHistoryCmp { + bool operator()(const TargetForest::History *a, + const TargetForest::History *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 RefTargetsHistoryCmp { + bool operator()(const ref &a, + const ref &b) const { + return a.get() == b.get(); + } +}; + +struct UnorderedTargetsSetHash { + std::size_t operator()(const TargetForest::UnorderedTargetsSet *t) const { + return t->hash(); + } +}; + +struct UnorderedTargetsSetCmp { + bool operator()(const TargetForest::UnorderedTargetsSet *a, + const TargetForest::UnorderedTargetsSet *b) const { + return a == b; + } +}; + +struct EquivUnorderedTargetsSetCmp { + bool operator()(const TargetForest::UnorderedTargetsSet *a, + const TargetForest::UnorderedTargetsSet *b) const { + if (a == NULL || b == NULL) + return false; + return *a == *b; + } +}; + +} // namespace klee + +#endif /* KLEE_TARGETFOREST_H */ diff --git a/include/klee/Module/TargetHash.h b/include/klee/Module/TargetHash.h new file mode 100644 index 0000000000..eccac7db77 --- /dev/null +++ b/include/klee/Module/TargetHash.h @@ -0,0 +1,58 @@ +//===-- TargetHash.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_TARGETHASH_H +#define KLEE_TARGETHASH_H + +#include "klee/ADT/Ref.h" + +#include +#include + +namespace llvm { +class BasicBlock; +} + +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 { + bool operator()(const ref &a, const ref &b) const; +}; + +typedef std::pair Transition; + +struct TransitionHash { + std::size_t operator()(const Transition &p) const; +}; + +struct RefTargetLess { + bool operator()(const ref &a, const ref &b) const { + return a.get() < b.get(); + } +}; + +} // namespace klee +#endif /* KLEE_TARGETHASH_H */ diff --git a/include/klee/Runner/run_klee.h b/include/klee/Runner/run_klee.h index 14a4ce9925..cec79a242d 100644 --- a/include/klee/Runner/run_klee.h +++ b/include/klee/Runner/run_klee.h @@ -1,6 +1,10 @@ #ifndef KLEE_RUN_KLEE_H #define KLEE_RUN_KLEE_H +#include "klee/Module/SarifReport.h" + +klee::SarifReport parseInputPathTree(const std::string &inputPathTreePath); + int run_klee(int argc, char **argv, char **envp); #endif // KLEE_RUN_KLEE_H diff --git a/include/klee/Solver/Solver.h b/include/klee/Solver/Solver.h index 4ee955b6a4..b3cd796512 100644 --- a/include/klee/Solver/Solver.h +++ b/include/klee/Solver/Solver.h @@ -280,6 +280,28 @@ class Solver { public: enum Validity { True = 1, False = -1, Unknown = 0 }; + enum PartialValidity { + /// The query is provably true. + MustBeTrue = 1, + + /// The query is provably false. + MustBeFalse = -1, + + /// The query is not provably false (a true assignment is known to + /// exist). + MayBeTrue = 2, + + /// The query is not provably true (a false assignment is known to + /// exist). + MayBeFalse = -2, + + /// The query is known to have both true and false assignments. + TrueOrFalse = 0, + + /// The validity of the query is unknown. + None = 3 + }; + public: /// validity_to_str - Return the name of given Validity enum value. static const char *validity_to_str(Validity v); @@ -308,6 +330,8 @@ class Solver { bool evaluate(const Query &, ref &queryResult, ref &negateQueryResult); + Solver::PartialValidity evaluate(const Query &query); + /// mustBeTrue - Determine if the expression is provably true. /// /// This evaluates the following logical formula: diff --git a/include/klee/Solver/SolverImpl.h b/include/klee/Solver/SolverImpl.h index a579cc6fca..8d3271304e 100644 --- a/include/klee/Solver/SolverImpl.h +++ b/include/klee/Solver/SolverImpl.h @@ -68,6 +68,8 @@ class SolverImpl { ref &queryResult, ref &negatedQueryResult); + virtual Solver::PartialValidity computePartialValidity(const Query &query); + /// computeTruth - Determine whether the given query expression is provably /// true given the constraints. /// @@ -117,6 +119,19 @@ class SolverImpl { return nullptr; } + virtual Solver::PartialValidity validityToPartial(Solver::Validity v) { + switch (v) { + case Solver::Validity::True: + return Solver::PartialValidity::MustBeTrue; + case Solver::Validity::False: + return Solver::PartialValidity::MustBeFalse; + case Solver::Validity::Unknown: + return Solver::PartialValidity::TrueOrFalse; + default: // Silence compiler warning + return Solver::PartialValidity::None; + } + } + virtual void setCoreSolverTimeout(time::Span timeout){}; }; diff --git a/include/klee/Support/OptionCategories.h b/include/klee/Support/OptionCategories.h index be37ba06e3..879249fa2c 100644 --- a/include/klee/Support/OptionCategories.h +++ b/include/klee/Support/OptionCategories.h @@ -17,6 +17,7 @@ #include "llvm/Support/CommandLine.h" namespace klee { +extern llvm::cl::OptionCategory ExecCat; extern llvm::cl::OptionCategory DebugCat; extern llvm::cl::OptionCategory ExecCat; extern llvm::cl::OptionCategory MergeCat; diff --git a/json b/json new file mode 160000 index 0000000000..6af826d0bd --- /dev/null +++ b/json @@ -0,0 +1 @@ +Subproject commit 6af826d0bdb55e4b69e3ad817576745335f243ca diff --git a/lib/Core/AddressSpace.cpp b/lib/Core/AddressSpace.cpp index 1aa4a060a1..fac4dc0fc9 100644 --- a/lib/Core/AddressSpace.cpp +++ b/lib/Core/AddressSpace.cpp @@ -145,7 +145,8 @@ bool AddressSpace::resolveOneIfUnique(ExecutionState &state, bool AddressSpace::resolveOne(ExecutionState &state, TimingSolver *solver, ref address, KType *objectType, IDType &result, MOPredicate predicate, - bool &success) const { + bool &success, + const std::atomic_bool &haltExecution) const { if (ref CE = dyn_cast(address)) { if (resolveOne(CE, objectType, result)) { success = true; @@ -197,6 +198,10 @@ bool AddressSpace::resolveOne(ExecutionState &state, TimingSolver *solver, continue; } + if (haltExecution) { + break; + } + bool mayBeTrue; if (!solver->mayBeTrue(state.constraints, mo->getBoundsCheckPointer(address), mayBeTrue, @@ -215,7 +220,8 @@ bool AddressSpace::resolveOne(ExecutionState &state, TimingSolver *solver, bool AddressSpace::resolveOne(ExecutionState &state, TimingSolver *solver, ref address, KType *objectType, - IDType &result, bool &success) const { + IDType &result, bool &success, + const std::atomic_bool &haltExecution) const { MOPredicate predicate([](const MemoryObject *mo) { return true; }); if (UseTimestamps) { ref base = @@ -243,7 +249,7 @@ bool AddressSpace::resolveOne(ExecutionState &state, TimingSolver *solver, } return resolveOne(state, solver, address, objectType, result, predicate, - success); + success, haltExecution); } int AddressSpace::checkPointerInObject(ExecutionState &state, diff --git a/lib/Core/AddressSpace.h b/lib/Core/AddressSpace.h index 25095dd89b..01d907cdb8 100644 --- a/lib/Core/AddressSpace.h +++ b/lib/Core/AddressSpace.h @@ -95,10 +95,11 @@ class AddressSpace { /// \return true iff an object was found at \a address. bool resolveOne(ExecutionState &state, TimingSolver *solver, ref address, KType *objectType, IDType &result, - bool &success) const; + bool &success, const std::atomic_bool &haltExecution) const; bool resolveOne(ExecutionState &state, TimingSolver *solver, ref address, KType *objectType, IDType &result, - MOPredicate predicate, bool &success) const; + MOPredicate predicate, bool &success, + const std::atomic_bool &haltExecution) const; /// @brief Tries to resolve the pointer in the concrete object /// if it value is unique. diff --git a/lib/Core/CMakeLists.txt b/lib/Core/CMakeLists.txt index 2369a10ae2..beb732b2f8 100644 --- a/lib/Core/CMakeLists.txt +++ b/lib/Core/CMakeLists.txt @@ -21,12 +21,15 @@ klee_add_component(kleeCore ImpliedValue.cpp Memory.cpp MemoryManager.cpp + PForest.cpp PTree.cpp Searcher.cpp SeedInfo.cpp SpecialFunctionHandler.cpp StatsTracker.cpp - Target.cpp + TargetCalculator.cpp + TargetedExecutionReporter.cpp + TargetedExecutionManager.cpp TimingSolver.cpp TypeManager.cpp UserSearcher.cpp diff --git a/lib/Core/ExecutionState.cpp b/lib/Core/ExecutionState.cpp index 5b8cebccf7..8dcf8b7554 100644 --- a/lib/Core/ExecutionState.cpp +++ b/lib/Core/ExecutionState.cpp @@ -25,11 +25,13 @@ #include "llvm/Support/raw_ostream.h" #include +#include #include #include #include #include #include +#include using namespace llvm; using namespace klee; @@ -72,6 +74,14 @@ StackFrame::StackFrame(const StackFrame &s) StackFrame::~StackFrame() { delete[] locals; } /***/ +ExecutionState::ExecutionState() + : initPC(nullptr), pc(nullptr), prevPC(nullptr), incomingBBIndex(-1), + depth(0), ptreeNode(nullptr), steppedInstructions(0), + steppedMemoryInstructions(0), instsSinceCovNew(0), + roundingMode(llvm::APFloat::rmNearestTiesToEven), coveredNew(false), + forkDisabled(false) { + setID(); +} ExecutionState::ExecutionState(KFunction *kf) : initPC(kf->instructions), pc(initPC), prevPC(pc), @@ -80,6 +90,16 @@ ExecutionState::ExecutionState(KFunction *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), + roundingMode(llvm::APFloat::rmNearestTiesToEven), coveredNew(false), + forkDisabled(false) { + pushFrame(nullptr, kf); + setID(); +} + ExecutionState::~ExecutionState() { for (const auto &cur_mergehandler : openMergeStack) { cur_mergehandler->removeOpenState(this); @@ -94,9 +114,9 @@ ExecutionState::ExecutionState(const ExecutionState &state) stack(state.stack), incomingBBIndex(state.incomingBBIndex), depth(state.depth), multilevel(state.multilevel), level(state.level), addressSpace(state.addressSpace), constraints(state.constraints), - pathOS(state.pathOS), symPathOS(state.symPathOS), - coveredLines(state.coveredLines), symbolics(state.symbolics), - symbolicSizes(state.symbolicSizes), + targetForest(state.targetForest), pathOS(state.pathOS), + symPathOS(state.symPathOS), coveredLines(state.coveredLines), + symbolics(state.symbolics), symbolicSizes(state.symbolicSizes), resolvedPointers(state.resolvedPointers), cexPreferences(state.cexPreferences), arrayNames(state.arrayNames), openMergeStack(state.openMergeStack), @@ -108,8 +128,7 @@ ExecutionState::ExecutionState(const ExecutionState &state) ? state.unwindingInformation->clone() : nullptr), coveredNew(state.coveredNew), forkDisabled(state.forkDisabled), - targets(state.targets), gepExprBases(state.gepExprBases), - gepExprOffsets(state.gepExprOffsets) { + gepExprBases(state.gepExprBases), gepExprOffsets(state.gepExprOffsets) { for (const auto &cur_mergehandler : openMergeStack) cur_mergehandler->addOpenState(this); } @@ -134,6 +153,36 @@ bool ExecutionState::inSymbolics(const MemoryObject *mo) const { return false; } +ExecutionState *ExecutionState::withStackFrame(KInstIterator caller, + KFunction *kf) { + ExecutionState *newState = new ExecutionState(*this); + newState->setID(); + newState->pushFrame(caller, kf); + newState->initPC = kf->blockMap[&*kf->function->begin()]->instructions; + newState->pc = newState->initPC; + newState->prevPC = newState->pc; + return newState; +} + +ExecutionState *ExecutionState::withKFunction(KFunction *kf) { + return withStackFrame(nullptr, kf); +} + +ExecutionState *ExecutionState::withKBlock(KBlock *kb) { + ExecutionState *newState = new ExecutionState(*this); + newState->setID(); + newState->initPC = kb->instructions; + newState->pc = newState->initPC; + newState->prevPC = newState->pc; + return newState; +} + +ExecutionState *ExecutionState::copy() { + ExecutionState *newState = new ExecutionState(*this); + newState->setID(); + return newState; +} + void ExecutionState::pushFrame(KInstIterator caller, KFunction *kf) { stack.emplace_back(StackFrame(caller, kf)); } @@ -181,6 +230,9 @@ bool ExecutionState::getBase( case Expr::Concat: { ref base = ArrayExprHelper::hasOrderedReads(*dyn_cast(expr)); + if (!base) { + return false; + } auto parent = findMemoryObject(base->updates.root); if (!parent) { return false; @@ -295,6 +347,7 @@ bool ExecutionState::merge(const ExecutionState &b) { std::set> aConstraints(constraints.begin(), constraints.end()); std::set> bConstraints(b.constraints.begin(), b.constraints.end()); + std::set> commonConstraints, aSuffix, bSuffix; std::set_intersection( aConstraints.begin(), aConstraints.end(), bConstraints.begin(), @@ -306,6 +359,7 @@ bool ExecutionState::merge(const ExecutionState &b) { std::set_difference(bConstraints.begin(), bConstraints.end(), commonConstraints.begin(), commonConstraints.end(), std::inserter(bSuffix, bSuffix.end())); + if (DebugLogStateMerge) { llvm::errs() << "\tconstraint prefix: ["; for (std::set>::iterator it = commonConstraints.begin(), @@ -421,6 +475,7 @@ bool ExecutionState::merge(const ExecutionState &b) { constraints = ConstraintSet(); ConstraintManager m(constraints); + for (const auto &constraint : commonConstraints) m.addConstraint(constraint); m.addConstraint(OrExpr::create(inA, inB)); @@ -503,6 +558,12 @@ void ExecutionState::increaseLevel() { transitionLevel.insert(std::make_pair(srcbb, dstbb)); } +bool ExecutionState::isTransfered() { return getPrevPCBlock() != getPCBlock(); } + bool ExecutionState::isGEPExpr(ref expr) const { return UseGEPOptimization && gepExprBases.find(expr) != gepExprBases.end(); } + +bool ExecutionState::visited(KBlock *block) const { + return level.find(block->basicBlock) != level.end(); +} diff --git a/lib/Core/ExecutionState.h b/lib/Core/ExecutionState.h index e7bcca858a..1276c86d5d 100644 --- a/lib/Core/ExecutionState.h +++ b/lib/Core/ExecutionState.h @@ -12,14 +12,18 @@ #include "AddressSpace.h" #include "MergeHandler.h" -#include "Target.h" #include "klee/ADT/ImmutableSet.h" #include "klee/ADT/TreeStream.h" +#include "klee/Core/TerminationTypes.h" +#include "klee/Expr/Assignment.h" #include "klee/Expr/Constraints.h" #include "klee/Expr/Expr.h" #include "klee/Expr/ExprHashMap.h" #include "klee/Module/KInstIterator.h" +#include "klee/Module/Target.h" +#include "klee/Module/TargetForest.h" +#include "klee/Module/TargetHash.h" #include "klee/Solver/Solver.h" #include "klee/System/Time.h" @@ -28,6 +32,7 @@ #include #include #include +#include #include #include @@ -42,6 +47,8 @@ struct KInstruction; class MemoryObject; class PTreeNode; struct InstructionInfo; +struct Target; +struct TranstionHash; llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const MemoryMap &mm); @@ -163,9 +170,6 @@ struct Symbolic { } }; -// typedef std::pair, const Array *> Symbolic; -typedef std::pair Transition; - /// @brief ExecutionState representing a path under exploration class ExecutionState { #ifdef KLEE_UNITTEST @@ -196,7 +200,7 @@ class ExecutionState { /// @brief Remember from which Basic Block control flow arrived /// (i.e. to select the right phi values) - std::uint32_t incomingBBIndex; + std::int32_t incomingBBIndex; // Overall state of the state - Data specific @@ -215,6 +219,13 @@ class ExecutionState { /// @brief Constraints collected so far ConstraintSet constraints; + /// @brief Key points which should be visited through execution + TargetForest targetForest; + + /// @brief Velocity and acceleration of this state investigating new blocks + long long progressVelocity = 0; + unsigned long progressAcceleration = 1; + /// Statistics and information /// @brief Metadata utilized and collected by solvers for this state @@ -287,19 +298,18 @@ class ExecutionState { /// @brief Disables forking for this state. Set by user code bool forkDisabled = false; - /// @brief The targets that the state must achieve - std::set targets; - ExprHashMap, llvm::Type *>> gepExprBases; ExprHashMap> gepExprOffsets; + ReachWithError error = ReachWithError::None; + std::atomic terminationReasonType{ + HaltExecution::NotHalt}; + public: -#ifdef KLEE_UNITTEST - // provide this function only in the context of unittests - ExecutionState() = default; -#endif // only to create the initial state + explicit ExecutionState(); explicit ExecutionState(KFunction *kf); + explicit ExecutionState(KFunction *kf, KBlock *kb); // no copy assignment, use copy constructor ExecutionState &operator=(const ExecutionState &) = delete; // no move ctor @@ -310,6 +320,11 @@ class ExecutionState { ~ExecutionState(); ExecutionState *branch(); + ExecutionState *withKFunction(KFunction *kf); + ExecutionState *withStackFrame(KInstIterator caller, KFunction *kf); + ExecutionState *withKBlock(KBlock *kb); + ExecutionState *empty(); + ExecutionState *copy(); bool inSymbolics(const MemoryObject *mo) const; @@ -336,12 +351,15 @@ class ExecutionState { bool merge(const ExecutionState &b); void dumpStack(llvm::raw_ostream &out) const; + bool visited(KBlock *block) const; + std::uint32_t getID() const { return id; }; void setID() { id = nextID++; }; llvm::BasicBlock *getInitPCBlock() const; llvm::BasicBlock *getPrevPCBlock() const; llvm::BasicBlock *getPCBlock() const; void increaseLevel(); + bool isTransfered(); bool isGEPExpr(ref expr) const; }; diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index 3eed4642da..80bf88d5f8 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -19,11 +19,14 @@ #include "ImpliedValue.h" #include "Memory.h" #include "MemoryManager.h" +#include "PForest.h" #include "PTree.h" #include "Searcher.h" #include "SeedInfo.h" #include "SpecialFunctionHandler.h" #include "StatsTracker.h" +#include "TargetCalculator.h" +#include "TargetedExecutionManager.h" #include "TimingSolver.h" #include "TypeManager.h" #include "UserSearcher.h" @@ -34,6 +37,7 @@ #include "klee/Config/config.h" #include "klee/Core/Interpreter.h" #include "klee/Expr/ArrayExprOptimizer.h" +#include "klee/Expr/ArrayExprVisitor.h" #include "klee/Expr/Assignment.h" #include "klee/Expr/Constraints.h" #include "klee/Expr/Expr.h" @@ -101,6 +105,7 @@ typedef unsigned TypeSize; #include #include #include +#include #include #include #include @@ -126,25 +131,17 @@ cl::OptionCategory SeedingCat( "Seeding options", "These options are related to the use of seeds to start exploration."); -cl::OptionCategory - TerminationCat("State and overall termination options", - "These options control termination of the overall KLEE " - "execution and of individual states."); - cl::OptionCategory TestGenCat("Test generation options", "These options impact test generation."); -cl::opt - MaxTime("max-time", - cl::desc("Halt execution after the specified duration. " - "Set to 0s to disable (default=0s)"), - cl::init("0s"), cl::cat(TerminationCat)); - cl::opt LazyInitialization("use-lazy-initialization", cl::init(true), cl::desc("Enable lazy initialization (default=true)"), cl::cat(ExecCat)); +cl::opt ForkPartialValidity("fork-partial-validity", cl::init(false), + cl::cat(ExecCat)); + cl::opt TypeSystem("type-system", cl::desc("Use information about type system from specified " @@ -153,25 +150,50 @@ cl::opt "Use plain type system from LLVM"), clEnumValN(TypeSystemKind::CXX, "CXX", "Use type system from CXX")), - cl::init(TypeSystemKind::LLVM)); + cl::init(TypeSystemKind::LLVM), cl::cat(ExecCat)); cl::opt UseTBAA("use-tbaa", cl::desc("Turns on restrictions based on types compatibility for " "symbolic pointers (default=false)"), - cl::init(false)); + cl::init(false), cl::cat(ExecCat)); cl::opt AlignSymbolicPointers("align-symbolic-pointers", cl::desc("Makes symbolic pointers aligned according" "to the used type system (default=true)"), - cl::init(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::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 " + "and return null (default=false)"), + cl::cat(ExecCat)); + +enum class MockMutableGlobalsPolicy { + None, + PrimitiveFields, + All, +}; + +cl::opt MockMutableGlobals( + "mock-mutable-globals", + cl::values(clEnumValN(MockMutableGlobalsPolicy::None, "none", + "No mutable global object are allowed o mock except " + "external (default)"), + clEnumValN(MockMutableGlobalsPolicy::PrimitiveFields, + "primitive-fields", + "Only primitive fileds of mutable global objects are " + "allowed to mock."), + clEnumValN(MockMutableGlobalsPolicy::All, "all", + "All mutable global object are allowed o mock.")), + cl::init(MockMutableGlobalsPolicy::None), cl::cat(ExecCat)); } // namespace klee namespace { @@ -250,6 +272,18 @@ cl::opt AllExternalWarnings( "as opposed to once per function (default=false)"), cl::cat(ExtCallsCat)); +cl::opt + MockExternalCalls("mock-external-calls", cl::init(false), + cl::desc("If true, failed external calls are mocked, " + "i.e. return values are made symbolic " + "and then added to generated test cases. " + "If false, fails on externall calls."), + cl::cat(ExtCallsCat)); + +cl::opt MockAllExternals("mock-all-externals", cl::init(false), + cl::desc("If true, all externals are mocked."), + cl::cat(ExtCallsCat)); + /*** Seeding options ***/ cl::opt AlwaysOutputSeeds( @@ -296,114 +330,6 @@ cl::opt "search (default=0s (off))"), cl::cat(SeedingCat)); -/*** Termination criteria options ***/ - -cl::list ExitOnErrorType( - "exit-on-error-type", - cl::desc( - "Stop execution after reaching a specified condition (default=false)"), - cl::values(clEnumValN(StateTerminationType::Abort, "Abort", - "The program crashed (reached abort()/klee_abort())"), - clEnumValN(StateTerminationType::Assert, "Assert", - "An assertion was hit"), - clEnumValN(StateTerminationType::BadVectorAccess, - "BadVectorAccess", "Vector accessed out of bounds"), - clEnumValN(StateTerminationType::Execution, "Execution", - "Trying to execute an unexpected instruction"), - clEnumValN(StateTerminationType::External, "External", - "External objects referenced"), - clEnumValN(StateTerminationType::Free, "Free", - "Freeing invalid memory"), - clEnumValN(StateTerminationType::Model, "Model", - "Memory model limit hit"), - clEnumValN(StateTerminationType::Overflow, "Overflow", - "An overflow occurred"), - clEnumValN(StateTerminationType::Ptr, "Ptr", "Pointer error"), - clEnumValN(StateTerminationType::UndefinedBehavior, - "UndefinedBehavior", "Undefined behavior detected"), - clEnumValN(StateTerminationType::ReadOnly, "ReadOnly", - "Write to read-only memory"), - clEnumValN(StateTerminationType::ReportError, "ReportError", - "klee_report_error called"), - clEnumValN(StateTerminationType::User, "User", - "Wrong klee_* functions invocation")), - cl::ZeroOrMore, cl::cat(TerminationCat)); - -cl::opt - MaxInstructions("max-instructions", - cl::desc("Stop execution after this many instructions. " - "Set to 0 to disable (default=0)"), - cl::init(0), cl::cat(TerminationCat)); - -cl::opt MaxForks( - "max-forks", - cl::desc("Only fork this many times. Set to -1 to disable (default=-1)"), - cl::init(~0u), cl::cat(TerminationCat)); - -cl::opt MaxDepth("max-depth", - cl::desc("Only allow this many symbolic branches. " - "Set to 0 to disable (default=0)"), - cl::init(0), cl::cat(TerminationCat)); - -cl::opt - MaxMemory("max-memory", - cl::desc("Refuse to fork when above this amount of " - "memory (in MB) (see -max-memory-inhibit) and terminate " - "states when additional 100MB allocated (default=2000)"), - cl::init(2000), cl::cat(TerminationCat)); - -cl::opt MaxMemoryInhibit("max-memory-inhibit", - cl::desc("Inhibit forking when above memory cap " - "(see -max-memory) (default=true)"), - cl::init(true), cl::cat(TerminationCat)); - -cl::opt RuntimeMaxStackFrames( - "max-stack-frames", - cl::desc("Terminate a state after this many stack frames. Set to 0 to " - "disable (default=8192)"), - cl::init(8192), cl::cat(TerminationCat)); - -cl::opt MaxStaticForkPct( - "max-static-fork-pct", cl::init(1.), - cl::desc("Maximum percentage spent by an instruction forking out of the " - "forking of all instructions (default=1.0 (always))"), - cl::cat(TerminationCat)); - -cl::opt MaxStaticSolvePct( - "max-static-solve-pct", cl::init(1.), - cl::desc("Maximum percentage of solving time that can be spent by a single " - "instruction over total solving time for all instructions " - "(default=1.0 (always))"), - cl::cat(TerminationCat)); - -cl::opt MaxStaticCPForkPct( - "max-static-cpfork-pct", cl::init(1.), - cl::desc("Maximum percentage spent by an instruction of a call path " - "forking out of the forking of all instructions in the call path " - "(default=1.0 (always))"), - cl::cat(TerminationCat)); - -cl::opt MaxStaticCPSolvePct( - "max-static-cpsolve-pct", cl::init(1.), - cl::desc("Maximum percentage of solving time that can be spent by a single " - "instruction of a call path over total solving time for all " - "instructions (default=1.0 (always))"), - cl::cat(TerminationCat)); - -cl::opt MaxStaticPctCheckDelay( - "max-static-pct-check-delay", - cl::desc("Number of forks after which the --max-static-*-pct checks are " - "enforced (default=1000)"), - cl::init(1000), cl::cat(TerminationCat)); - -cl::opt TimerInterval( - "timer-interval", - cl::desc( - "Minimum interval to check timers. " - "Affects -max-time, -istats-write-interval, -stats-write-interval, and " - "-uncovered-update-interval (default=1s)"), - cl::init("1s"), cl::cat(TerminationCat)); - /*** Debugging options ***/ /// The different query logging solvers that can switched on/off @@ -453,14 +379,29 @@ cl::opt DebugCheckForImpliedValues( "debug-check-for-implied-values", cl::init(false), cl::desc("Debug the implied value optimization"), cl::cat(DebugCat)); +bool allLeafsConstant(const ref &expr) { + if (isa(expr)) { + return true; + } + + if (!isa(expr)) { + return false; + } + + const SelectExpr *sel = cast(expr); + return allLeafsConstant(sel->trueExpr) && allLeafsConstant(sel->falseExpr); +} + } // namespace extern llvm::cl::opt MaxConstantAllocationSize; extern llvm::cl::opt MaxSymbolicAllocationSize; // XXX hack -extern "C" unsigned dumpStates, dumpPTree; -unsigned dumpStates = 0, dumpPTree = 0; +extern "C" unsigned dumpStates, dumpPForest; +unsigned dumpStates = 0, dumpPForest = 0; + +bool Interpreter::hasTargetForest() const { return false; } const std::unordered_set Executor::supportedFPIntrinsics = { // Intrinsic::fabs, //handled individually because of its presence in @@ -480,16 +421,19 @@ Executor::Executor(LLVMContext &ctx, const InterpreterOptions &opts, pathWriter(0), symPathWriter(0), specialFunctionHandler(0), timers{time::Span(TimerInterval)}, concretizationManager(new ConcretizationManager(EqualitySubstitution)), - codeGraphDistance(new CodeGraphDistance()), replayKTest(0), replayPath(0), - usingSeeds(0), atMemoryLimit(false), inhibitForking(false), - haltExecution(false), ivcEnabled(false), + codeGraphDistance(new CodeGraphDistance()), + targetedExecutionManager(*codeGraphDistance), replayKTest(0), + replayPath(0), usingSeeds(0), atMemoryLimit(false), inhibitForking(false), + haltExecution(HaltExecution::NotHalt), ivcEnabled(false), debugLogBuffer(debugBufferString) { + guidanceKind = opts.Guidance; + const time::Span maxTime{MaxTime}; if (maxTime) timers.add(std::make_unique(maxTime, [&] { klee_message("HaltTimer invoked"); - setHaltExecution(true); + setHaltExecution(HaltExecution::MaxTime); })); coreSolverTimeout = time::Span{MaxCoreSolverTime}; @@ -546,7 +490,8 @@ llvm::Module * Executor::setModule(std::vector> &userModules, std::vector> &libsModules, const ModuleOptions &opts, - const std::vector &mainModuleFunctions) { + const std::vector &mainModuleFunctions, + std::unique_ptr origInfos) { assert(!kmodule && !userModules.empty() && "can only register one module"); // XXX gross @@ -598,6 +543,10 @@ Executor::setModule(std::vector> &userModules, mainModuleFunctions.begin(), mainModuleFunctions.end()); + if (origInfos) { + kmodule->origInfos = origInfos->getInstructions(); + } + specialFunctionHandler->bind(); initializeTypeManager(); @@ -833,6 +782,7 @@ void Executor::allocateGlobalObjects(ExecutionState &state) { /*alignment=*/globalObjectAlignment); if (!mo) klee_error("out of memory"); + globalObjects.emplace(&v, mo); globalAddresses.emplace(&v, mo->getBaseConstantExpr()); } @@ -898,12 +848,17 @@ void Executor::initializeGlobalObjects(ExecutionState &state) { } else { addr = externalDispatcher->resolveSymbol(v.getName().str()); } - if (!addr) { + if (MockAllExternals && !addr) { + executeMakeSymbolic( + state, mo, typeSystemManager->getWrappedType(v.getType()), + "mocked_extern", SourceBuilder::irreproducible(), false); + } else if (!addr) { klee_error("Unable to load symbol(%.*s) while initializing globals", static_cast(v.getName().size()), v.getName().data()); - } - for (unsigned offset = 0; offset < mo->size; offset++) { - os->write8(offset, static_cast(addr)[offset]); + } else { + for (unsigned offset = 0; offset < mo->size; offset++) { + os->write8(offset, static_cast(addr)[offset]); + } } } else if (v.hasInitializer()) { initializeGlobalObject(state, os, v.getInitializer(), 0); @@ -971,7 +926,7 @@ void Executor::branch(ExecutionState &state, ExecutionState *ns = es->branch(); addedStates.push_back(ns); result.push_back(ns); - processTree->attach(es->ptreeNode, ns, es, reason); + processForest->attach(es->ptreeNode, ns, es, reason); } } @@ -1087,7 +1042,6 @@ ref Executor::maxStaticPctChecks(ExecutionState ¤t, Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, bool isInternal, BranchType reason) { - Solver::Validity res; std::map>::iterator it = seedMap.find(¤t); bool isSeeding = it != seedMap.end(); @@ -1099,10 +1053,34 @@ 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, condition, res, - current.queryMetaData); + Solver::PartialValidity res; + if (ForkPartialValidity) { + res = + solver->evaluate(current.constraints, condition, current.queryMetaData); + } else { + Solver::Validity val; + bool success = solver->evaluate(current.constraints, condition, val, + current.queryMetaData); + if (!success) { + res = Solver::PartialValidity::None; + } else { + switch (val) { + case Solver::Validity::True: + res = Solver::PartialValidity::MustBeTrue; + break; + case Solver::Validity::False: + res = Solver::PartialValidity::MustBeFalse; + break; + case Solver::Validity::Unknown: + res = Solver::PartialValidity::TrueOrFalse; + break; + default: + assert(0 && "Validity did not match any switch case"); + } + } + } solver->setTimeout(time::Span()); - if (!success) { + if (res == Solver::PartialValidity::None) { current.pc = current.prevPC; terminateStateOnSolverError(current, "Query timed out (fork)."); return StatePair(nullptr, nullptr); @@ -1114,31 +1092,31 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, "ran out of branches in replay path mode"); bool branch = (*replayPath)[replayPosition++]; - if (res == Solver::True) { + if (res == Solver::PartialValidity::MustBeTrue) { assert(branch && "hit invalid branch in replay path mode"); - } else if (res == Solver::False) { + } else if (res == Solver::PartialValidity::MustBeFalse) { assert(!branch && "hit invalid branch in replay path mode"); } else { // add constraints if (branch) { - res = Solver::True; + res = Solver::PartialValidity::MustBeTrue; addConstraint(current, condition); } else { - res = Solver::False; + res = Solver::PartialValidity::MustBeFalse; addConstraint(current, Expr::createIsZero(condition)); } } - } else if (res == Solver::Unknown) { + } else if (res == Solver::PartialValidity::TrueOrFalse) { assert(!replayKTest && "in replay mode, only one branch can be true."); if (!branchingPermitted(current)) { TimerStatIncrementer timer(stats::forkTime); if (theRNG.getBool()) { addConstraint(current, condition); - res = Solver::True; + res = Solver::PartialValidity::MustBeTrue; } else { addConstraint(current, Expr::createIsZero(condition)); - res = Solver::False; + res = Solver::PartialValidity::MustBeFalse; } } } @@ -1147,7 +1125,7 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, // Fix branch in only-replay-seed mode, if we don't have both true // and false seeds. if (isSeeding && (current.forkDisabled || OnlyReplaySeeds) && - res == Solver::Unknown) { + res == Solver::PartialValidity::TrueOrFalse) { bool trueSeed = false, falseSeed = false; // Is seed extension still ok here? for (std::vector::iterator siit = it->second.begin(), @@ -1170,7 +1148,8 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, if (!(trueSeed && falseSeed)) { assert(trueSeed || falseSeed); - res = trueSeed ? Solver::True : Solver::False; + res = trueSeed ? Solver::PartialValidity::MustBeTrue + : Solver::PartialValidity::MustBeFalse; addConstraint(current, trueSeed ? condition : Expr::createIsZero(condition)); } @@ -1183,21 +1162,29 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, // the value it has been fixed at, we should take this as a nice // hint to just use the single constraint instead of all the binary // search ones. If that makes sense. - if (res == Solver::True) { + if (res == Solver::PartialValidity::MustBeTrue || + res == Solver::PartialValidity::MayBeTrue) { if (!isInternal) { if (pathWriter) { current.pathOS << "1"; } } + if (res == Solver::PartialValidity::MayBeTrue) { + current.addConstraint(condition); + } return StatePair(¤t, nullptr); - } else if (res == Solver::False) { + } else if (res == Solver::PartialValidity::MustBeFalse || + res == Solver::PartialValidity::MayBeFalse) { if (!isInternal) { if (pathWriter) { current.pathOS << "0"; } } + if (res == Solver::PartialValidity::MayBeFalse) { + current.addConstraint(Expr::createIsZero(condition)); + } return StatePair(nullptr, ¤t); } else { TimerStatIncrementer timer(stats::forkTime); @@ -1246,7 +1233,7 @@ Executor::StatePair Executor::fork(ExecutionState ¤t, ref condition, } } - processTree->attach(current.ptreeNode, falseState, trueState, reason); + processForest->attach(current.ptreeNode, falseState, trueState, reason); if (pathWriter) { // Need to update the pathOS.id field of falseState, otherwise the same id @@ -1331,7 +1318,7 @@ void Executor::addConstraint(ExecutionState &state, ref condition) { } const Cell &Executor::eval(KInstruction *ki, unsigned index, - ExecutionState &state) const { + ExecutionState &state, bool isSymbolic) { assert(index < ki->inst->getNumOperands()); int vnumber = ki->operands[index]; @@ -1345,6 +1332,10 @@ const Cell &Executor::eval(KInstruction *ki, unsigned index, } else { unsigned index = vnumber; StackFrame &sf = state.stack.back(); + ref reg = sf.locals[index].value; + if (isSymbolic && reg.isNull()) { + prepareSymbolicRegister(state, sf, index); + } return sf.locals[index]; } } @@ -1501,7 +1492,11 @@ void Executor::stepInstruction(ExecutionState &state) { ++state.pc; if (stats::instructions == MaxInstructions) - haltExecution = true; + haltExecution = HaltExecution::MaxInstructions; + + if (MaxSteppedInstructions && + state.steppedInstructions >= MaxSteppedInstructions) + haltExecution = HaltExecution::MaxSteppedInstructions; } static inline const llvm::fltSemantics *fpWidthToSemantics(unsigned width) { @@ -2177,7 +2172,23 @@ void Executor::executeCall(ExecutionState &state, KInstruction *ki, Function *f, } } -void Executor::transferToBasicBlock(BasicBlock *dst, BasicBlock *src, +void Executor::increaseProgressVelocity(ExecutionState &state, KBlock *block) { + if (state.visited(block)) { + if (state.progressVelocity >= 0) { + state.progressVelocity -= state.progressAcceleration; + state.progressAcceleration *= 2; + } + } else { + if (state.progressVelocity >= 0) { + state.progressVelocity += 1; + } else { + state.progressVelocity = 0; + state.progressAcceleration = 1; + } + } +} + +void Executor::transferToBasicBlock(KBlock *kdst, BasicBlock *src, ExecutionState &state) { // Note that in general phi nodes can reuse phi values from the same // block but the incoming value is the eval() result *before* the @@ -2192,17 +2203,63 @@ void Executor::transferToBasicBlock(BasicBlock *dst, BasicBlock *src, // instructions know which argument to eval, set the pc, and continue. // XXX this lookup has to go ? - KFunction *kf = state.stack.back().kf; - state.pc = kf->blockMap[dst]->instructions; + state.pc = kdst->instructions; state.increaseLevel(); if (state.pc->inst->getOpcode() == Instruction::PHI) { PHINode *first = static_cast(state.pc->inst); state.incomingBBIndex = first->getBasicBlockIndex(src); } + increaseProgressVelocity(state, kdst); +} + +void Executor::transferToBasicBlock(BasicBlock *dst, BasicBlock *src, + ExecutionState &state) { + KFunction *kf = state.stack.back().kf; + auto kdst = kf->blockMap[dst]; + transferToBasicBlock(kdst, src, state); +} + +void Executor::checkNullCheckAfterDeref(ref cond, ExecutionState &state, + ExecutionState *fstate, + ExecutionState *sstate) { + ref eqPointerCheck = nullptr; + if (isa(cond) && cast(cond)->left->getWidth() == + Context::get().getPointerWidth()) { + eqPointerCheck = cast(cond); + } + if (isa(Expr::createIsZero(cond)) && + cast(Expr::createIsZero(cond))->left->getWidth() == + Context::get().getPointerWidth()) { + eqPointerCheck = cast(Expr::createIsZero(cond)); + } + if (eqPointerCheck && eqPointerCheck->left->isZero() && + state.resolvedPointers.count(eqPointerCheck->right)) { + if (isa(cond) && !fstate) { + reportStateOnTargetError(*sstate, + ReachWithError::NullCheckAfterDerefException); + } + if (isa(Expr::createIsZero(cond)) && !sstate) { + reportStateOnTargetError(*fstate, + ReachWithError::NullCheckAfterDerefException); + } + } } void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { Instruction *i = ki->inst; + + if (guidanceKind == GuidanceKind::ErrorGuidance && + state.prevPC->inst->isTerminator()) { + for (auto kvp : state.targetForest) { + auto target = kvp.first; + if (target->isThatError(ReachWithError::Reachable) && + target->isTheSameAsIn(ki)) { + terminateStateOnTargetError(state, ReachWithError::Reachable); + return; + } + } + } + switch (i->getOpcode()) { // Control flow case Instruction::Ret: { @@ -2303,7 +2360,12 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { // We check that the return value has no users instead of // checking the type, since C defaults to returning int for // undeclared functions. - if (!caller->use_empty()) { + if (kmodule->WithPOSIXRuntime() && + cast(kcaller->parent)->getKFunction() && + cast(kcaller->parent)->getKFunction()->getName() == + "__klee_posix_wrapped_main") { + bindLocal(kcaller, state, ConstantExpr::alloc(0, Expr::Int32)); + } else if (!caller->use_empty()) { terminateStateOnExecError( state, "return void when caller expected a result"); } @@ -2321,6 +2383,7 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { ref cond = eval(ki, 0, state).value; cond = optimizer.optimizeExpr(cond, false); + Executor::StatePair branches = fork(state, cond, false, BranchType::ConditionalBranch); @@ -2337,6 +2400,10 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { if (branches.second) transferToBasicBlock(bi->getSuccessor(1), bi->getParent(), *branches.second); + + if (guidanceKind == GuidanceKind::ErrorGuidance) { + checkNullCheckAfterDeref(cond, state, branches.first, branches.second); + } } break; } @@ -2624,51 +2691,64 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { } else { ref v = eval(ki, 0, state).value; - ExecutionState *free = &state; - bool hasInvalid = false, first = true; - - /* XXX This is wasteful, no need to do a full evaluate since we - have already got a value. But in the end the caches should - handle it for us, albeit with some overhead. */ - do { - v = optimizer.optimizeExpr(v, true); - ref value; - bool success = - solver->getValue(free->constraints, v, value, free->queryMetaData); - assert(success && "FIXME: Unhandled solver failure"); - (void)success; - StatePair res = - fork(*free, EqExpr::create(v, value), true, BranchType::Call); - if (res.first) { - uint64_t addr = value->getZExtValue(); - auto it = legalFunctions.find(addr); - if (it != legalFunctions.end()) { - f = it->second; - - // Don't give warning on unique resolution - if (res.second || !first) - klee_warning_once(reinterpret_cast(addr), - "resolved symbolic function pointer to: %s", - f->getName().data()); - - executeCall(*res.first, ki, f, arguments); - } else { - if (!hasInvalid) { - terminateStateOnExecError(state, "invalid function pointer"); - hasInvalid = true; + if (!isa(v) && MockExternalCalls) { + if (ki->inst->getType()->isSized()) { + prepareSymbolicValue(state, ki, "mocked_extern", + SourceBuilder::irreproducible()); + } + } else { + ExecutionState *free = &state; + bool hasInvalid = false, first = true; + + /* XXX This is wasteful, no need to do a full evaluate since we + have already got a value. But in the end the caches should + handle it for us, albeit with some overhead. */ + do { + v = optimizer.optimizeExpr(v, true); + ref value; + bool success = solver->getValue(free->constraints, v, value, + free->queryMetaData); + assert(success && "FIXME: Unhandled solver failure"); + (void)success; + StatePair res = + fork(*free, EqExpr::create(v, value), true, BranchType::Call); + if (res.first) { + uint64_t addr = value->getZExtValue(); + auto it = legalFunctions.find(addr); + if (it != legalFunctions.end()) { + f = it->second; + + // Don't give warning on unique resolution + if (res.second || !first) + klee_warning_once(reinterpret_cast(addr), + "resolved symbolic function pointer to: %s", + f->getName().data()); + + executeCall(*res.first, ki, f, arguments); + } else { + if (!hasInvalid) { + terminateStateOnExecError(state, "invalid function pointer"); + hasInvalid = true; + } } } - } - first = false; - free = res.second; - } while (free); + first = false; + free = res.second; + timers.invoke(); + } while (free && !haltExecution); + } } break; } case Instruction::PHI: { - ref result = eval(ki, state.incomingBBIndex, state).value; - bindLocal(ki, state, result); + if (state.incomingBBIndex == -1) + prepareSymbolicValue(state, ki); + else { + ref result; + result = eval(ki, state.incomingBBIndex, state).value; + bindLocal(ki, state, result); + } break; } @@ -2944,16 +3024,14 @@ void Executor::executeInstruction(ExecutionState &state, KInstruction *ki) { if (kgepi->offset) offset = AddExpr::create(offset, Expr::createPointer(kgepi->offset)); ref address = AddExpr::create(base, offset); - if (!isa(address)) { - ref extractedOffset = ExtractExpr::create(offset, 0, Expr::Int32); - if (state.isGEPExpr(base)) { - state.gepExprBases[address] = state.gepExprBases[base]; - state.gepExprOffsets[address] = - AddExpr::create(state.gepExprOffsets[base], extractedOffset); - } else { - state.gepExprBases[address] = {base, gepInst->getSourceElementType()}; - state.gepExprOffsets[address] = extractedOffset; - } + ref extractedOffset = ExtractExpr::create(offset, 0, Expr::Int32); + if (state.isGEPExpr(base)) { + state.gepExprBases[address] = state.gepExprBases[base]; + state.gepExprOffsets[address] = + AddExpr::create(state.gepExprOffsets[base], extractedOffset); + } else { + state.gepExprBases[address] = {base, gepInst->getSourceElementType()}; + state.gepExprOffsets[address] = extractedOffset; } bindLocal(ki, state, address); break; @@ -3704,9 +3782,10 @@ void Executor::updateStates(ExecutionState *current) { seedMap.find(es); if (it3 != seedMap.end()) seedMap.erase(it3); - processTree->remove(es->ptreeNode); + processForest->remove(es->ptreeNode); delete es; } + removedButReachableStates.clear(); removedStates.clear(); } @@ -3831,6 +3910,23 @@ bool Executor::checkMemoryUsage() { return false; } +bool Executor::decreaseConfidenceFromStoppedStates( + SetOfStates &left_states, HaltExecution::Reason reason) { + bool hasStateWhichCanReachSomeTarget = false; + for (auto state : left_states) { + if (state->targetForest.empty()) + continue; + hasStateWhichCanReachSomeTarget = true; + auto realReason = reason ? reason : state->terminationReasonType.load(); + if (state->progressVelocity >= 0) { + assert(targets.count(state->targetForest.getEntryFunction()) != 0); + targets.at(state->targetForest.getEntryFunction()) + .subtractConfidencesFrom(state->targetForest, realReason); + } + } + return hasStateWhichCanReachSomeTarget; +} + void Executor::doDumpStates() { if (!DumpStatesOnHalt || states.empty()) { interpreterHandler->incPathsExplored(states.size()); @@ -3844,82 +3940,90 @@ void Executor::doDumpStates() { updateStates(nullptr); for (const auto &state : states) terminateStateEarly(*state, "Execution halting.", - StateTerminationType::Interrupted); + guidanceKind == GuidanceKind::ErrorGuidance + ? StateTerminationType::SilentExit + : StateTerminationType::Interrupted); updateStates(nullptr); } -void Executor::run(ExecutionState &initialState) { - // Delay init till now so that ticks don't accrue during optimization and - // such. - timers.reset(); +void Executor::seed(ExecutionState &initialState) { + std::vector &v = seedMap[&initialState]; - states.insert(&initialState); + for (std::vector::const_iterator it = usingSeeds->begin(), + ie = usingSeeds->end(); + it != ie; ++it) + v.push_back(SeedInfo(*it)); - if (usingSeeds) { - std::vector &v = seedMap[&initialState]; - - for (std::vector::const_iterator it = usingSeeds->begin(), - ie = usingSeeds->end(); - it != ie; ++it) - v.push_back(SeedInfo(*it)); - - int lastNumSeeds = usingSeeds->size() + 10; - time::Point lastTime, startTime = lastTime = time::getWallTime(); - ExecutionState *lastState = 0; - while (!seedMap.empty()) { - if (haltExecution) { - doDumpStates(); - return; - } - - std::map>::iterator it = - seedMap.upper_bound(lastState); - if (it == seedMap.end()) - it = seedMap.begin(); - lastState = it->first; - ExecutionState &state = *lastState; - KInstruction *ki = state.pc; - stepInstruction(state); - - executeInstruction(state, ki); - timers.invoke(); - if (::dumpStates) - dumpStates(); - if (::dumpPTree) - dumpPTree(); - updateStates(&state); + int lastNumSeeds = usingSeeds->size() + 10; + time::Point lastTime, startTime = lastTime = time::getWallTime(); + ExecutionState *lastState = 0; + while (!seedMap.empty()) { + if (haltExecution) { + doDumpStates(); + return; + } - if ((stats::instructions % 1000) == 0) { - int numSeeds = 0, numStates = 0; - for (std::map>::iterator - it = seedMap.begin(), - ie = seedMap.end(); - it != ie; ++it) { - numSeeds += it->second.size(); - numStates++; - } - const auto time = time::getWallTime(); - const time::Span seedTime(SeedTime); - if (seedTime && time > startTime + seedTime) { - klee_warning("seed time expired, %d seeds remain over %d states", - numSeeds, numStates); - break; - } else if (numSeeds <= lastNumSeeds - 10 || - time - lastTime >= time::seconds(10)) { - lastTime = time; - lastNumSeeds = numSeeds; - klee_message("%d seeds remaining over: %d states", numSeeds, - numStates); - } + std::map>::iterator it = + seedMap.upper_bound(lastState); + if (it == seedMap.end()) + it = seedMap.begin(); + lastState = it->first; + ExecutionState &state = *lastState; + KInstruction *ki = state.pc; + stepInstruction(state); + + executeInstruction(state, ki); + timers.invoke(); + if (::dumpStates) + dumpStates(); + if (::dumpPForest) + dumpPForest(); + updateStates(&state); + + if ((stats::instructions % 1000) == 0) { + int numSeeds = 0, numStates = 0; + for (std::map>::iterator + it = seedMap.begin(), + ie = seedMap.end(); + it != ie; ++it) { + numSeeds += it->second.size(); + numStates++; + } + const auto time = time::getWallTime(); + const time::Span seedTime(SeedTime); + if (seedTime && time > startTime + seedTime) { + klee_warning("seed time expired, %d seeds remain over %d states", + numSeeds, numStates); + break; + } else if (numSeeds <= lastNumSeeds - 10 || + time - lastTime >= time::seconds(10)) { + lastTime = time; + lastNumSeeds = numSeeds; + klee_message("%d seeds remaining over: %d states", numSeeds, numStates); } } + } - klee_message("seeding done (%d states remain)", (int)states.size()); + klee_message("seeding done (%d states remain)", (int)states.size()); - if (OnlySeed) { - doDumpStates(); - return; - } + if (OnlySeed) { + doDumpStates(); + return; + } +} + +void Executor::run(std::vector initialStates) { + // Delay init till now so that ticks don't accrue during optimization and + // such. + if (guidanceKind != GuidanceKind::ErrorGuidance) + timers.reset(); + + states.insert(initialStates.begin(), initialStates.end()); + + if (usingSeeds) { + assert(initialStates.size() == 1); + ExecutionState *initialState = initialStates.back(); + seed(*initialState); } searcher = constructUserSearcher(*this); @@ -3942,14 +4046,47 @@ void Executor::run(ExecutionState &initialState) { } if (searcher->empty()) - haltExecution = true; + haltExecution = HaltExecution::NoMoreStates; + } + + if (guidanceKind == GuidanceKind::ErrorGuidance) { + bool canReachNew1 = decreaseConfidenceFromStoppedStates(pausedStates); + bool canReachNew2 = + decreaseConfidenceFromStoppedStates(states, haltExecution); + + for (auto &startBlockAndWhiteList : targets) { + startBlockAndWhiteList.second.reportFalsePositives(canReachNew1 || + canReachNew2); + } + + if (searcher->empty()) + haltExecution = HaltExecution::NoMoreStates; } delete searcher; searcher = nullptr; doDumpStates(); - haltExecution = false; + haltExecution = HaltExecution::NotHalt; +} + +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); + + processForest = std::make_unique(); + processForest->addRoot(&state); + targetedRun(state, target); + processForest = nullptr; + + if (statsTracker) + statsTracker->done(); } void Executor::initializeTypeManager() { @@ -3974,8 +4111,8 @@ void Executor::executeStep(ExecutionState &state) { timers.invoke(); if (::dumpStates) dumpStates(); - if (::dumpPTree) - dumpPTree(); + if (::dumpPForest) + dumpPForest(); updateStates(&state); @@ -3985,6 +4122,48 @@ void Executor::executeStep(ExecutionState &state) { } } +void Executor::targetedRun(ExecutionState &initialState, KBlock *target, + ExecutionState **resultState) { + // Delay init till now so that ticks don't accrue during optimization and + // such. + if (guidanceKind != GuidanceKind::ErrorGuidance) + timers.reset(); + + states.insert(&initialState); + + TargetedSearcher *targetedSearcher = + new TargetedSearcher(Target::create(target), *codeGraphDistance); + searcher = targetedSearcher; + + std::vector newStates(states.begin(), states.end()); + searcher->update(0, newStates, std::vector()); + // main interpreter loop + KInstruction *terminator = + target != nullptr ? target->getFirstInstruction() : nullptr; + while (!searcher->empty() && !haltExecution) { + ExecutionState &state = searcher->selectState(); + + KInstruction *ki = state.pc; + + if (ki == terminator) { + *resultState = state.copy(); + terminateStateOnTerminator(state); + updateStates(&state); + haltExecution = HaltExecution::ReachedTarget; + break; + } + + executeStep(state); + } + + delete searcher; + searcher = nullptr; + + doDumpStates(); + if (*resultState) + haltExecution = HaltExecution::NotHalt; +} + std::string Executor::getAddressInfo(ExecutionState &state, ref address, const MemoryObject *mo) const { std::string Str; @@ -4001,12 +4180,12 @@ std::string Executor::getAddressInfo(ExecutionState &state, ref address, (void)success; example = value->getZExtValue(); info << "\texample: " << example << "\n"; - std::pair, ref> res = - mo ? std::make_pair( - mo->getBaseExpr(), - AddExpr::create(mo->getBaseExpr(), mo->getSizeExpr())) - : solver->getRange(state.constraints, address, state.queryMetaData); - info << "\trange: [" << res.first << ", " << res.second << "]\n"; + if (mo) { + std::pair, ref> res = + std::make_pair(mo->getBaseExpr(), + AddExpr::create(mo->getBaseExpr(), mo->getSizeExpr())); + info << "\trange: [" << res.first << ", " << res.second << "]\n"; + } } MemoryObject hack((unsigned)example); @@ -4038,7 +4217,22 @@ std::string Executor::getAddressInfo(ExecutionState &state, ref address, return info.str(); } -void Executor::terminateState(ExecutionState &state) { +HaltExecution::Reason fromStateTerminationType(StateTerminationType t) { + switch (t) { + case StateTerminationType::MaxDepth: + return HaltExecution::MaxDepth; + case StateTerminationType::OutOfStackMemory: + return HaltExecution::MaxStackFrames; + case StateTerminationType::Solver: + return HaltExecution::MaxSolverTime; + default: + return HaltExecution::Unspecified; + } +} + +void Executor::terminateState(ExecutionState &state, + StateTerminationType terminationType) { + state.terminationReasonType = fromStateTerminationType(terminationType); if (replayKTest && replayPosition != replayKTest->numObjects) { klee_warning_once(replayKTest, "replay did not consume all objects in test input."); @@ -4054,22 +4248,8 @@ void Executor::terminateState(ExecutionState &state) { interpreterHandler->incPathsExplored(); - std::vector::iterator it = - std::find(addedStates.begin(), addedStates.end(), &state); - if (it == addedStates.end()) { - state.pc = state.prevPC; - - removedStates.push_back(&state); - } else { - // never reached searcher, just delete immediately - std::map>::iterator it3 = - seedMap.find(&state); - if (it3 != seedMap.end()) - seedMap.erase(it3); - addedStates.erase(it); - processTree->remove(state.ptreeNode); - delete &state; - } + state.pc = state.prevPC; + removedStates.push_back(&state); } static bool shouldWriteTest(const ExecutionState &state) { @@ -4100,14 +4280,14 @@ void Executor::unpauseState(ExecutionState &state) { } void Executor::terminateStateOnExit(ExecutionState &state) { + auto terminationType = StateTerminationType::Exit; if (shouldWriteTest(state) || (AlwaysOutputSeeds && seedMap.count(&state))) interpreterHandler->processTestCase( - state, nullptr, - terminationTypeFileExtension(StateTerminationType::Exit).c_str()); + state, nullptr, terminationTypeFileExtension(terminationType).c_str()); interpreterHandler->incPathsCompleted(); targetCalculator->update(state); - terminateState(state); + terminateState(state, terminationType); } void Executor::terminateStateEarly(ExecutionState &state, const Twine &message, @@ -4119,8 +4299,15 @@ void Executor::terminateStateEarly(ExecutionState &state, const Twine &message, state, (message + "\n").str().c_str(), terminationTypeFileExtension(terminationType).c_str()); } + terminateState(state, terminationType); +} - terminateState(state); +void Executor::terminateStateOnTerminator(ExecutionState &state) { + if (shouldWriteTest(state) || (AlwaysOutputSeeds && seedMap.count(&state))) { + interpreterHandler->processTestCase(state, nullptr, nullptr); + } + targetCalculator->update(state); + terminateState(state, StateTerminationType::SilentExit); } void Executor::terminateStateOnUserError(ExecutionState &state, @@ -4178,6 +4365,57 @@ bool shouldExitOn(StateTerminationType reason) { return it != ExitOnErrorType.end(); } +void Executor::reportStateOnTargetError(ExecutionState &state, + ReachWithError error) { + if (guidanceKind == GuidanceKind::ErrorGuidance) { + bool reportedTruePositive = + targetedExecutionManager.reportTruePositive(state, error); + if (!reportedTruePositive) { + targetedExecutionManager.reportFalseNegative(state, error); + } + } +} + +void Executor::terminateStateOnTargetError(ExecutionState &state, + ReachWithError error) { + reportStateOnTargetError(state, error); + + // Proceed with normal `terminateStateOnError` call + std::string messaget; + StateTerminationType terminationType; + switch (error) { + case ReachWithError::NullPointerException: + messaget = "memory error: null pointer exception"; + terminationType = StateTerminationType::Ptr; + break; + case ReachWithError::DoubleFree: + messaget = "double free error"; + terminationType = StateTerminationType::Ptr; + break; + case ReachWithError::UseAfterFree: + messaget = "use after free error"; + terminationType = StateTerminationType::Ptr; + break; + case ReachWithError::NullCheckAfterDerefException: + messaget = "null check after deref"; + terminationType = StateTerminationType::Exit; + break; + case ReachWithError::Reachable: + messaget = ""; + terminationType = StateTerminationType::Exit; + break; + case ReachWithError::None: + default: + messaget = "unspecified error"; + terminationType = StateTerminationType::User; + } + if (error == ReachWithError::Reachable) { + terminateStateEarly(state, messaget, terminationType); + } else { + terminateStateOnError(state, messaget, terminationType); + } +} + void Executor::terminateStateOnError(ExecutionState &state, const llvm::Twine &messaget, StateTerminationType terminationType, @@ -4224,10 +4462,10 @@ void Executor::terminateStateOnError(ExecutionState &state, } targetCalculator->update(state); - terminateState(state); + terminateState(state, terminationType); if (shouldExitOn(terminationType)) - haltExecution = true; + haltExecution = HaltExecution::ErrorOnWhichShouldExit; } void Executor::terminateStateOnExecError(ExecutionState &state, @@ -4265,6 +4503,29 @@ void Executor::callExternalFunction(ExecutionState &state, KInstruction *target, return; } + if (ExternalCalls == ExternalCallPolicy::All && MockAllExternals) { + std::string TmpStr; + llvm::raw_string_ostream os(TmpStr); + os << "calling external: " << callable->getName().str() << "("; + for (unsigned i = 0; i < arguments.size(); i++) { + os << arguments[i]; + if (i != arguments.size() - 1) + os << ", "; + } + os << ") at " << state.pc->getSourceLocation(); + + if (AllExternalWarnings) + klee_warning("%s", os.str().c_str()); + else if (!SuppressExternalWarnings) + klee_warning_once(callable->getValue(), "%s", os.str().c_str()); + + if (target->inst->getType()->isSized()) { + prepareSymbolicValue(state, target, "mocked_extern", + SourceBuilder::irreproducible()); + } + return; + } + // normal external function handling path // allocate 128 bits for each argument (+return value) to support fp80's; // we could iterate through all the arguments first and determine the exact @@ -4389,8 +4650,16 @@ void Executor::callExternalFunction(ExecutionState &state, KInstruction *target, roundingMode); if (!success) { - terminateStateOnError(state, "failed external call: " + callable->getName(), - StateTerminationType::External); + if (MockExternalCalls) { + if (target->inst->getType()->isSized()) { + prepareSymbolicValue(state, target, "mocked_extern", + SourceBuilder::irreproducible()); + } + } else { + terminateStateOnError(state, + "failed external call: " + callable->getName(), + StateTerminationType::External); + } return; } @@ -4411,6 +4680,18 @@ void Executor::callExternalFunction(ExecutionState &state, KInstruction *target, if (resultType != Type::getVoidTy(kmodule->module->getContext())) { ref e = ConstantExpr::fromMemory((void *)args, getWidthForLLVMType(resultType)); + if (ExternCallsCanReturnNull && + e->getWidth() == Context::get().getPointerWidth()) { + const Array *symExternCallsCanReturnNull = makeArray( + state, Expr::createPointer(1), "symExternCallsCanReturnNull", + SourceBuilder::irreproducible()); + + ref symExternCallsCanReturnNullExpr = + Expr::createTempRead(symExternCallsCanReturnNull, Expr::Bool); + e = SelectExpr::create( + symExternCallsCanReturnNullExpr, + ConstantExpr::alloc(0, Context::get().getPointerWidth()), e); + } bindLocal(target, state, e); } } @@ -4444,6 +4725,16 @@ ref Executor::replaceReadWithSymbolic(ExecutionState &state, return res; } +ref Executor::mockValue(ExecutionState &state, ref result) { + static unsigned id; + const Array *array = makeArray( + state, Expr::createPointer(Expr::getMinBytesForWidth(result->getWidth())), + "mockedGlobalValue" + llvm::utostr(++id), + SourceBuilder::irreproducible()); + result = Expr::createTempRead(array, result->getWidth()); + return result; +} + ObjectState *Executor::bindObjectInState(ExecutionState &state, const MemoryObject *mo, KType *dynamicType, bool isLocal, @@ -4465,7 +4756,7 @@ ObjectState *Executor::bindObjectInState(ExecutionState &state, void Executor::executeAlloc(ExecutionState &state, ref size, bool isLocal, KInstruction *target, KType *type, bool zeroMemory, const ObjectState *reallocFrom, - size_t allocationAlignment) { + size_t allocationAlignment, bool checkOutOfMemory) { const llvm::Value *allocSite = state.prevPC->inst; if (allocationAlignment == 0) { allocationAlignment = getAllocationAlignment(allocSite); @@ -4492,7 +4783,19 @@ void Executor::executeAlloc(ExecutionState &state, ref size, bool isLocal, } else { os->initializeToRandom(); } - bindLocal(target, *bound, mo->getBaseExpr()); + + ref address = mo->getBaseExpr(); + if (checkOutOfMemory) { + const Array *symCheckOutOfMemory = + makeArray(state, Expr::createPointer(1), "symCheckOutOfMemory", + SourceBuilder::irreproducible()); + + ref symCheckOutOfMemoryExpr = + Expr::createTempRead(symCheckOutOfMemory, Expr::Bool); + address = SelectExpr::create(symCheckOutOfMemoryExpr, + Expr::createPointer(0), address); + } + bindLocal(target, state, address); if (reallocFrom) { unsigned count = std::min(reallocFrom->size, os->size); @@ -4525,8 +4828,13 @@ void Executor::executeFree(ExecutionState &state, ref address, } if (zeroPointer.second) { // address != 0 ExactResolutionList rl; - resolveExact(*zeroPointer.second, address, - typeSystemManager->getUnknownType(), rl, "free"); + if (!resolveExact(*zeroPointer.second, address, + typeSystemManager->getUnknownType(), rl, "free") && + guidanceKind == GuidanceKind::ErrorGuidance) { + terminateStateOnTargetError(*zeroPointer.second, + ReachWithError::DoubleFree); + return; + } for (Executor::ExactResolutionList::iterator it = rl.begin(), ie = rl.end(); it != ie; ++it) { @@ -4551,7 +4859,7 @@ void Executor::executeFree(ExecutionState &state, ref address, } } -void Executor::resolveExact(ExecutionState &state, ref p, KType *type, +bool Executor::resolveExact(ExecutionState &state, ref p, KType *type, ExactResolutionList &results, const std::string &name) { p = optimizer.optimizeExpr(p, true); @@ -4560,7 +4868,10 @@ void Executor::resolveExact(ExecutionState &state, ref p, KType *type, /* We do not need this variable here, just a placeholder for resolve */ ResolutionList rlSkipped; - state.addressSpace.resolve(state, solver, p, type, rl, rlSkipped); + solver->setTimeout(coreSolverTimeout); + bool incomplete = state.addressSpace.resolve(state, solver, p, type, rl, + rlSkipped, 0, coreSolverTimeout); + solver->setTimeout(time::Span()); ExecutionState *unbound = &state; for (ResolutionList::iterator it = rl.begin(), ie = rl.end(); it != ie; @@ -4579,11 +4890,20 @@ void Executor::resolveExact(ExecutionState &state, ref p, KType *type, break; } + if (guidanceKind == GuidanceKind::ErrorGuidance && rl.size() == 0) { + return false; + } + if (unbound) { - terminateStateOnError(*unbound, "memory error: invalid pointer: " + name, - StateTerminationType::Ptr, - getAddressInfo(*unbound, p)); + if (incomplete) { + terminateStateOnSolverError(*unbound, "Query timed out (resolve)."); + } else { + terminateStateOnError(*unbound, "memory error: invalid pointer: " + name, + StateTerminationType::Ptr, + getAddressInfo(*unbound, p)); + } } + return true; } MemoryObject *Executor::allocate(ExecutionState &state, ref size, @@ -4700,7 +5020,7 @@ MemoryObject *Executor::allocate(ExecutionState &state, ref size, } void Executor::executeMemoryOperation( - ExecutionState &state, bool isWrite, KType *targetType, ref address, + ExecutionState &estate, bool isWrite, KType *targetType, ref address, ref value /* undef if read */, KInstruction *target /* undef if write */) { Expr::Width type = (isWrite ? value->getWidth() @@ -4711,26 +5031,45 @@ void Executor::executeMemoryOperation( unsigned size = bytes; KType *baseTargetType = targetType; - if (state.isGEPExpr(address)) { - base = state.gepExprBases[address].first; + if (estate.isGEPExpr(address)) { + base = estate.gepExprBases[address].first; size = kmodule->targetData->getTypeStoreSize( - state.gepExprBases[address].second); + estate.gepExprBases[address].second); baseTargetType = typeSystemManager->getWrappedType( - llvm::PointerType::get(state.gepExprBases[address].second, + llvm::PointerType::get(estate.gepExprBases[address].second, kmodule->targetData->getAllocaAddrSpace())); } if (SimplifySymIndices) { if (!isa(address)) - address = ConstraintManager::simplifyExpr(state.constraints, address); + address = ConstraintManager::simplifyExpr(estate.constraints, address); if (!isa(base)) - base = ConstraintManager::simplifyExpr(state.constraints, base); + base = ConstraintManager::simplifyExpr(estate.constraints, base); if (isWrite && !isa(value)) - value = ConstraintManager::simplifyExpr(state.constraints, value); + value = ConstraintManager::simplifyExpr(estate.constraints, value); } address = optimizer.optimizeExpr(address, true); + ref uniqueBase = + ConstraintManager::simplifyExpr(estate.constraints, base); + 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) + return; + ExecutionState &state = *branches.second; + // fast path: single in-bounds resolution IDType idFastResult; bool success; @@ -4742,7 +5081,7 @@ void Executor::executeMemoryOperation( solver->setTimeout(coreSolverTimeout); if (!state.addressSpace.resolveOne(state, solver, address, targetType, - idFastResult, success)) { + idFastResult, success, haltExecution)) { address = toConstant(state, address, "resolveOne failure"); success = state.addressSpace.resolveOne(cast(address), targetType, idFastResult); @@ -4798,9 +5137,29 @@ void Executor::executeMemoryOperation( if (interpreterOpts.MakeConcreteSymbolic) result = replaceReadWithSymbolic(state, result); + if (MockMutableGlobals != MockMutableGlobalsPolicy::None && + mo->isGlobal && !wos->readOnly && !isa(result) && + (MockMutableGlobals != MockMutableGlobalsPolicy::PrimitiveFields || + !targetType->getRawType()->isPointerTy())) { + result = mockValue(state, result); + wos->write(offset, result); + } + bindLocal(target, state, result); } + return; + } + } else if (guidanceKind == GuidanceKind::ErrorGuidance && + allLeafsConstant(address)) { + + solver->setTimeout(coreSolverTimeout); + state.addressSpace.resolveOne(state, solver, base, baseTargetType, + idFastResult, success, haltExecution); + solver->setTimeout(time::Span()); + + if (!success) { + terminateStateOnTargetError(state, ReachWithError::UseAfterFree); return; } } @@ -4812,96 +5171,105 @@ void Executor::executeMemoryOperation( ResolutionList rl; ResolutionList rlSkipped; - solver->setTimeout(coreSolverTimeout); - bool incomplete = state.addressSpace.resolve( - state, solver, address, targetType, rl, rlSkipped, 0, coreSolverTimeout); - solver->setTimeout(time::Span()); + bool incomplete = false; + ExecutionState *unbound = &state; + ref checkOutOfBounds = ConstantExpr::create(1, Expr::Bool); + if (guidanceKind != GuidanceKind::ErrorGuidance) { + solver->setTimeout(coreSolverTimeout); + incomplete = + state.addressSpace.resolve(state, solver, address, targetType, rl, + rlSkipped, 0, coreSolverTimeout); + solver->setTimeout(time::Span()); - // XXX there is some query wasteage here. who cares? + // XXX there is some query wasteage here. who cares? - ref checkOutOfBounds = ConstantExpr::create(1, Expr::Bool); + checkOutOfBounds = ConstantExpr::create(1, Expr::Bool); - std::vector> resolveConditions; - std::vector resolvedMemoryObjects; + std::vector> resolveConditions; + std::vector resolvedMemoryObjects; - for (ResolutionList::iterator i = rl.begin(), ie = rl.end(); i != ie; ++i) { - const MemoryObject *mo = state.addressSpace.findObject(*i).first; - ref inBounds = mo->getBoundsCheckPointer(address, bytes); - ref outOfBound = NotExpr::create(inBounds); - if (state.isGEPExpr(address)) { - inBounds = AndExpr::create(inBounds, mo->getBoundsCheckPointer(base, 1)); - inBounds = - AndExpr::create(inBounds, mo->getBoundsCheckPointer(base, size)); - outOfBound = AndExpr::create(outOfBound, mo->getBoundsCheckPointer(base)); - } + for (ResolutionList::iterator i = rl.begin(), ie = rl.end(); i != ie; ++i) { + const MemoryObject *mo = state.addressSpace.findObject(*i).first; + ref inBounds = mo->getBoundsCheckPointer(address, bytes); + ref outOfBound = NotExpr::create(inBounds); + if (state.isGEPExpr(address)) { + inBounds = + AndExpr::create(inBounds, mo->getBoundsCheckPointer(base, 1)); + inBounds = + AndExpr::create(inBounds, mo->getBoundsCheckPointer(base, size)); + outOfBound = + AndExpr::create(outOfBound, mo->getBoundsCheckPointer(base)); + } - bool mayBeInBounds; - solver->setTimeout(coreSolverTimeout); - bool success = solver->mayBeTrue(state.constraints, inBounds, mayBeInBounds, - state.queryMetaData); - solver->setTimeout(time::Span()); - if (!success) { - terminateStateOnSolverError(state, "Query timed out (resolve)"); - return; - } + bool mayBeInBounds; + solver->setTimeout(coreSolverTimeout); + bool success = solver->mayBeTrue(state.constraints, inBounds, + mayBeInBounds, state.queryMetaData); + solver->setTimeout(time::Span()); + if (!success) { + terminateStateOnSolverError(state, "Query timed out (resolve)"); + return; + } - if (mayBeInBounds) { - resolveConditions.push_back(inBounds); - resolvedMemoryObjects.push_back(mo->id); + if (mayBeInBounds) { + resolveConditions.push_back(inBounds); + resolvedMemoryObjects.push_back(mo->id); + } + checkOutOfBounds = AndExpr::create(outOfBound, checkOutOfBounds); } - checkOutOfBounds = AndExpr::create(outOfBound, checkOutOfBounds); - } - // Fictive condition just to keep state for unbound case. - resolveConditions.push_back(ConstantExpr::create(1, Expr::Bool)); + // Fictive condition just to keep state for unbound case. + resolveConditions.push_back(ConstantExpr::create(1, Expr::Bool)); - std::vector statesForMemoryOperation; - branch(state, resolveConditions, statesForMemoryOperation, BranchType::MemOp); + std::vector statesForMemoryOperation; + branch(state, resolveConditions, statesForMemoryOperation, + BranchType::MemOp); - for (unsigned int i = 0; i < resolvedMemoryObjects.size(); ++i) { - ExecutionState *bound = statesForMemoryOperation[i]; - ObjectPair op = bound->addressSpace.findObject(resolvedMemoryObjects[i]); - const MemoryObject *mo = op.first; - const ObjectState *os = op.second; - - bound->addPointerResolution(base, address, mo); - - /* FIXME: Notice, that here we are creating a new instance of object - for every memory operation in order to handle type changes. This might - waste too much memory as we do now always modify something. To fix this - we can ask memory if we will make anything, and create a copy if - required. */ - ObjectState *wos = bound->addressSpace.getWriteable(mo, os); - - if (isWrite) { - wos->getDynamicType()->handleMemoryAccess( - targetType, mo->getOffsetExpr(address), - ConstantExpr::alloc(size, Context::get().getPointerWidth()), true); - if (wos->readOnly) { - terminateStateOnError(*bound, "memory error: object read only", - StateTerminationType::ReadOnly); + for (unsigned int i = 0; i < resolvedMemoryObjects.size(); ++i) { + ExecutionState *bound = statesForMemoryOperation[i]; + ObjectPair op = bound->addressSpace.findObject(resolvedMemoryObjects[i]); + const MemoryObject *mo = op.first; + const ObjectState *os = op.second; + + bound->addPointerResolution(base, address, mo); + + /* FIXME: Notice, that here we are creating a new instance of object + for every memory operation in order to handle type changes. This might + waste too much memory as we do now always modify something. To fix this + we can ask memory if we will make anything, and create a copy if + required. */ + ObjectState *wos = bound->addressSpace.getWriteable(mo, os); + + if (isWrite) { + wos->getDynamicType()->handleMemoryAccess( + targetType, mo->getOffsetExpr(address), + ConstantExpr::alloc(size, Context::get().getPointerWidth()), true); + if (wos->readOnly) { + terminateStateOnError(*bound, "memory error: object read only", + StateTerminationType::ReadOnly); + } else { + wos->write(mo->getOffsetExpr(address), value); + } } else { - wos->write(mo->getOffsetExpr(address), value); + wos->getDynamicType()->handleMemoryAccess( + targetType, mo->getOffsetExpr(address), + ConstantExpr::alloc(size, Context::get().getPointerWidth()), false); + ref result = wos->read(mo->getOffsetExpr(address), type); + + if (MockMutableGlobals != MockMutableGlobalsPolicy::None && + mo->isGlobal && !wos->readOnly && !isa(result) && + (MockMutableGlobals != MockMutableGlobalsPolicy::PrimitiveFields || + !targetType->getRawType()->isPointerTy())) { + result = mockValue(state, result); + wos->write(mo->getOffsetExpr(address), result); + } + + bindLocal(target, *bound, result); } - } else { - wos->getDynamicType()->handleMemoryAccess( - targetType, mo->getOffsetExpr(address), - ConstantExpr::alloc(size, Context::get().getPointerWidth()), false); - ref result = wos->read(mo->getOffsetExpr(address), type); - bindLocal(target, *bound, result); } + unbound = statesForMemoryOperation.back(); } - ExecutionState *unbound = statesForMemoryOperation.back(); - StatePair branches = - fork(*unbound, Expr::createIsZero(base), true, BranchType::MemOp); - ExecutionState *bound = branches.first; - if (bound) { - terminateStateOnError(*bound, "memory error: null pointer exception", - StateTerminationType::Ptr); - } - unbound = branches.second; - // XXX should we distinguish out of bounds and overlapped cases? if (unbound) { ref baseUniqueExpr = toUnique(*unbound, base); @@ -4910,8 +5278,7 @@ void Executor::executeMemoryOperation( terminateStateOnSolverError(*unbound, "Query timed out (resolve)."); } else if (LazyInitialization && !isa(baseUniqueExpr) && (isa(address) || isa(address) || - state.isGEPExpr(address)) && - size <= MaxSymbolicAllocationSize) { + state.isGEPExpr(address))) { IDType idLazyInitialization = lazyInitializeObject(*unbound, base, target, baseTargetType, size); @@ -5000,16 +5367,20 @@ IDType Executor::lazyInitializeObject(ExecutionState &state, ref address, timestamp = moBasePair.first->timestamp; } - const Array *lazyInstantiationSize = makeArray( - state, Expr::createPointer(Context::get().getPointerWidth() / CHAR_BIT), - "lazy_instantiation_size", - SourceBuilder::lazyInitializationMakeSymbolic()); - ref sizeExpr = Expr::createTempRead(lazyInstantiationSize, - Context::get().getPointerWidth()); - addConstraint(state, UgeExpr::create(sizeExpr, Expr::createPointer(size))); - addConstraint( - state, UleExpr::create(sizeExpr, - Expr::createPointer(MaxSymbolicAllocationSize))); + ref sizeExpr; + if (size <= MaxSymbolicAllocationSize) { + const Array *lazyInstantiationSize = makeArray( + state, Expr::createPointer(Context::get().getPointerWidth() / CHAR_BIT), + "lazy_instantiation_size", SourceBuilder::lazyInitializationSymbolic()); + sizeExpr = Expr::createTempRead(lazyInstantiationSize, + Context::get().getPointerWidth()); + addConstraint(state, UgeExpr::create(sizeExpr, Expr::createPointer(size))); + addConstraint( + state, UleExpr::create(sizeExpr, + Expr::createPointer(MaxSymbolicAllocationSize))); + } else { + sizeExpr = Expr::createPointer(size); + } std::string name = "lazy_initialization_content"; MemoryObject *mo = allocate(state, sizeExpr, false, @@ -5041,9 +5412,9 @@ IDType Executor::lazyInitializeObject(ExecutionState &state, ref address, return 0; } - addConstraint(state, EqExpr::create(mo->addressExpr, address)); + addConstraint(state, EqExpr::create(mo->getBaseExpr(), address)); executeMakeSymbolic(state, mo, targetType, name, - SourceBuilder::lazyInitializationMakeSymbolic(), false); + SourceBuilder::lazyInitializationSymbolic(), false); return mo->id; } @@ -5193,8 +5564,15 @@ void Executor::executeMakeSymbolic(ExecutionState &state, /***/ -void Executor::runFunctionAsMain(Function *f, int argc, char **argv, - char **envp) { +ExecutionState *Executor::formState(Function *f) { + ExecutionState *state = new ExecutionState( + kmodule->functionMap[f], kmodule->functionMap[f]->blockMap[&*f->begin()]); + initializeGlobals(*state); + return state; +} + +ExecutionState *Executor::formState(Function *f, int argc, char **argv, + char **envp) { std::vector> arguments; // force deterministic initialization of memory objects @@ -5269,6 +5647,7 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv, /*allocSite=*/state->pc->inst, /*alignment=*/8); if (!arg) klee_error("Could not allocate memory for function arguments"); + ObjectState *os = bindObjectInState( *state, arg, typeSystemManager->getWrappedType(argumentType), false); @@ -5282,6 +5661,178 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv, } initializeGlobals(*state); + return state; +} + +void Executor::clearGlobal() { + globalObjects.clear(); + globalAddresses.clear(); +} + +void Executor::clearMemory() { + // hack to clear memory objects + delete memory; + memory = new MemoryManager(NULL); +} + +void Executor::prepareSymbolicValue(ExecutionState &state, KInstruction *target, + std::string name, + ref source) { + Instruction *allocSite = target->inst; + uint64_t size = kmodule->targetData->getTypeStoreSize(allocSite->getType()); + uint64_t width = kmodule->targetData->getTypeSizeInBits(allocSite->getType()); + ref result = makeSymbolic(allocSite, state, size, width, name, source); + bindLocal(target, state, result); + if (isa(allocSite)) { + AllocaInst *ai = cast(allocSite); + unsigned elementSize = + kmodule->targetData->getTypeStoreSize(ai->getAllocatedType()); + ref size = Expr::createPointer(elementSize); + if (ai->isArrayAllocation()) { + ref count = eval(target, 0, state).value; + count = Expr::createZExtToPointerWidth(count); + size = MulExpr::create(size, count); + assert(isa(size)); + elementSize = cast(size)->getZExtValue(); + } + lazyInitializeObject(state, result, target, + typeSystemManager->getWrappedType(ai->getType()), + elementSize); + } +} + +void Executor::prepareSymbolicRegister(ExecutionState &state, StackFrame &sf, + unsigned regNum) { + KInstruction *allocInst = sf.kf->registerToInstructionMap[regNum]; + prepareSymbolicValue(state, allocInst); +} + +void Executor::prepareSymbolicArgs(ExecutionState &state, KFunction *kf) { + for (auto ai = kf->function->arg_begin(), ae = kf->function->arg_end(); + ai != ae; ai++) { + Argument *arg = *&ai; + uint64_t size = kmodule->targetData->getTypeStoreSize(arg->getType()); + uint64_t width = kmodule->targetData->getTypeSizeInBits(arg->getType()); + ref result = + makeSymbolicValue(arg, state, size, width, "symbolic_arg"); + bindArgument(state.stack.back().kf, arg->getArgNo(), state, result); + } +} + +ref Executor::makeSymbolic(Value *value, ExecutionState &state, + uint64_t size, Expr::Width width, + const std::string &name, + ref source) { + MemoryObject *mo = memory->allocate(size, true, /*isGlobal=*/false, value, + /*allocationAlignment=*/8); + const Array *array = + makeArray(state, Expr::createPointer(size), name, source); + state.addSymbolic(mo, array, + typeSystemManager->getWrappedType(value->getType())); + assert(value && "Attempted to make symbolic value from nullptr Value"); + ref result = Expr::createTempRead(array, width); + return result; +} + +ref Executor::makeSymbolicValue(Value *value, ExecutionState &state, + uint64_t size, Expr::Width width, + const std::string &name) { + return makeSymbolic(value, state, size, width, name, + SourceBuilder::symbolicValue()); +} + +void Executor::runFunctionAsMain(Function *f, int argc, char **argv, + char **envp) { + if (guidanceKind == GuidanceKind::ErrorGuidance && + (!interpreterOpts.Paths.has_value() || interpreterOpts.Paths->empty())) { + klee_warning("No targets found in error-guided mode"); + return; + } + + ExecutionState *state = formState(f, argc, argv, envp); + bindModuleConstants(llvm::APFloat::rmNearestTiesToEven); + std::vector states; + + if (guidanceKind == GuidanceKind::ErrorGuidance) { + KInstIterator caller; + if (kmodule->WithPOSIXRuntime()) { + state = prepareStateForPOSIX(caller, state->copy()); + } else { + state->popFrame(); + } + + auto &paths = interpreterOpts.Paths.value(); + auto prepTargets = targetedExecutionManager.prepareTargets( + kmodule.get(), std::move(paths)); + if (prepTargets.empty()) { + klee_warning( + "No targets found in error-guided mode after prepare targets"); + return; + } + + for (auto &startFunctionAndWhiteList : prepTargets) { + auto kf = + kmodule->functionMap.at(startFunctionAndWhiteList.first->function); + if (startFunctionAndWhiteList.second->empty()) { + klee_warning("No targets found for %s", + kf->function->getName().str().c_str()); + continue; + } + auto whitelist = startFunctionAndWhiteList.second; + targets.emplace(kf, TargetedHaltsOnTraces(whitelist)); + ExecutionState *initialState = state->withStackFrame(caller, kf); + prepareSymbolicArgs(*initialState, kf); + prepareTargetedExecution(initialState, whitelist); + states.push_back(initialState); + } + } else { + ExecutionState *initialState = state->copy(); + states.push_back(initialState); + delete state; + } + + TreeOStream pathOS; + TreeOStream symPathOS; + if (pathWriter) { + pathOS = pathWriter->open(); + } + + if (symPathWriter) { + symPathOS = symPathWriter->open(); + } + + processForest = std::make_unique(); + for (auto &state : states) { + if (statsTracker) + statsTracker->framePushed(*state, 0); + + if (pathWriter) + state->pathOS = pathOS; + if (symPathWriter) + state->symPathOS = symPathOS; + + processForest->addRoot(state); + } + + run(states); + processForest = nullptr; + + if (statsTracker) + statsTracker->done(); + + clearMemory(); + clearGlobal(); + + if (statsTracker) + 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(); @@ -5291,20 +5842,34 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv, if (statsTracker) statsTracker->framePushed(*state, 0); - processTree = std::make_unique(state); - bindModuleConstants(llvm::APFloat::rmNearestTiesToEven); - run(*state); - processTree = nullptr; - - // hack to clear memory objects - delete memory; - memory = new MemoryManager(NULL); + processForest = std::make_unique(); + processForest->addRoot(state); + ExecutionState *original = state->copy(); + ExecutionState *initialState = nullptr; + state->targetForest.add(Target::create(target)); + targetedRun(*state, target, &initialState); + state = initialState; + if (state) { + auto frame = state->stack.back(); + caller = frame.caller; + state->popFrame(); + delete original; + } else { + state = original; + state->popFrame(); + } - globalObjects.clear(); - globalAddresses.clear(); + processForest = nullptr; if (statsTracker) statsTracker->done(); + + return state; +} + +void Executor::prepareTargetedExecution(ExecutionState *initialState, + ref whitelist) { + initialState->targetForest = *whitelist->deepCopy(); } unsigned Executor::getPathStreamID(const ExecutionState &state) { @@ -5413,7 +5978,11 @@ void Executor::setInitializationGraph(const ExecutionState &state, for (const auto &offset : innerTypeOffset.second) { ref address = Expr::createTempRead( symbolic.array, Context::get().getPointerWidth(), offset); - ref constantAddress = model.evaluate(address); + ref addressInModel = model.evaluate(address); + if (!isa(addressInModel)) { + continue; + } + ref constantAddress = cast(addressInModel); IDType idResult; if (state.resolveOnSymbolics(constantAddress, idResult)) { @@ -5699,18 +6268,18 @@ int *Executor::getErrnoLocation(const ExecutionState &state) const { #endif } -void Executor::dumpPTree() { - if (!::dumpPTree) +void Executor::dumpPForest() { + if (!::dumpPForest) return; char name[32]; - snprintf(name, sizeof(name), "ptree%08d.dot", (int)stats::instructions); + snprintf(name, sizeof(name), "pforest%08d.dot", (int)stats::instructions); auto os = interpreterHandler->openOutputFile(name); if (os) { - processTree->dump(*os); + processForest->dump(*os); } - ::dumpPTree = 0; + ::dumpPForest = 0; } void Executor::dumpStates() { diff --git a/lib/Core/Executor.h b/lib/Core/Executor.h index 2c94a3eae0..17501df28a 100644 --- a/lib/Core/Executor.h +++ b/lib/Core/Executor.h @@ -16,6 +16,7 @@ #define KLEE_EXECUTOR_H #include "ExecutionState.h" +#include "TargetedExecutionManager.h" #include "UserSearcher.h" #include "klee/ADT/RNG.h" @@ -37,6 +38,7 @@ #include "llvm/IR/Intrinsics.h" #include "llvm/Support/raw_ostream.h" +#include #include #include #include @@ -80,7 +82,7 @@ class KModule; class MemoryManager; class MemoryObject; class ObjectState; -class PTree; +class PForest; class Searcher; class SeedInfo; class SpecialFunctionHandler; @@ -114,6 +116,7 @@ class Executor : public Interpreter { RNG theRNG; private: + using SetOfStates = std::set; /* Set of Intrinsic::ID. Plain type is used here to avoid including llvm in * the header */ static const std::unordered_set supportedFPIntrinsics; @@ -129,14 +132,15 @@ class Executor : public Interpreter { MemoryManager *memory; TypeManager *typeSystemManager; - std::set states; - std::set pausedStates; + SetOfStates states; + SetOfStates pausedStates; + SetOfStates removedButReachableStates; StatsTracker *statsTracker; TreeStreamWriter *pathWriter, *symPathWriter; SpecialFunctionHandler *specialFunctionHandler; TimerGroup timers; std::unique_ptr concretizationManager; - std::unique_ptr processTree; + std::unique_ptr processForest; std::unique_ptr codeGraphDistance; std::unique_ptr targetCalculator; @@ -171,6 +175,9 @@ class Executor : public Interpreter { /// Used to validate and dereference function pointers. std::unordered_map legalFunctions; + /// Manager for everything related to targeted execution mode + TargetedExecutionManager targetedExecutionManager; + /// When non-null the bindings that will be used for calls to /// klee_make_symbolic in order replay. const struct KTest *replayKTest; @@ -195,7 +202,7 @@ class Executor : public Interpreter { /// Signals the executor to halt execution at the next instruction /// step. - std::atomic_bool haltExecution; + HaltExecution::Reason haltExecution = HaltExecution::NotHalt; /// Whether implied-value concretization is enabled. Currently /// false, it is buggy (it needs to validate its writes). @@ -227,9 +234,13 @@ class Executor : public Interpreter { /// `nullptr` if merging is disabled MergingSearcher *mergingSearcher = nullptr; + std::unordered_map targets; + /// Typeids used during exception handling std::vector> eh_typeids; + GuidanceKind guidanceKind; + /// Return the typeid corresponding to a certain `type_info` ref getEhTypeidFor(ref type_info); @@ -237,7 +248,12 @@ class Executor : public Interpreter { void executeInstruction(ExecutionState &state, KInstruction *ki); - void run(ExecutionState &initialState); + 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 initializeTypeManager(); @@ -258,6 +274,8 @@ class Executor : public Interpreter { void updateStates(ExecutionState *current); void transferToBasicBlock(llvm::BasicBlock *dst, llvm::BasicBlock *src, ExecutionState &state); + void transferToBasicBlock(KBlock *dst, llvm::BasicBlock *src, + ExecutionState &state); void callExternalFunction(ExecutionState &state, KInstruction *target, KCallable *callable, @@ -276,7 +294,7 @@ class Executor : public Interpreter { /// state) pairs for each object the given address can point to the /// beginning of. typedef std::vector> ExactResolutionList; - void resolveExact(ExecutionState &state, ref p, KType *type, + bool resolveExact(ExecutionState &state, ref p, KType *type, ExactResolutionList &results, const std::string &name); MemoryObject *allocate(ExecutionState &state, ref size, bool isLocal, @@ -307,7 +325,8 @@ class Executor : public Interpreter { void executeAlloc(ExecutionState &state, ref size, bool isLocal, KInstruction *target, KType *type, bool zeroMemory = false, const ObjectState *reallocFrom = 0, - size_t allocationAlignment = 0); + size_t allocationAlignment = 0, + bool checkOutOfMemory = false); /// Free the given address with checking for errors. If target is /// given it will be bound to 0 in the resulting states (this is a @@ -340,6 +359,7 @@ class Executor : public Interpreter { IDType lazyInitializeObject(ExecutionState &state, ref address, KInstruction *target, KType *targetType, uint64_t size); + void executeMakeSymbolic(ExecutionState &state, const MemoryObject *mo, KType *type, const std::string &name, const ref source, bool isLocal); @@ -373,8 +393,10 @@ class Executor : public Interpreter { // Used for testing. ref replaceReadWithSymbolic(ExecutionState &state, ref e); - const Cell &eval(KInstruction *ki, unsigned index, - ExecutionState &state) const; + ref mockValue(ExecutionState &state, ref result); + + const Cell &eval(KInstruction *ki, unsigned index, ExecutionState &state, + bool isSymbolic = true); Cell &getArgumentCell(ExecutionState &state, KFunction *kf, unsigned index) { return state.stack.back().locals[kf->getArgRegister(index)]; @@ -437,7 +459,8 @@ class Executor : public Interpreter { llvm::Instruction **lastInstruction); /// Remove state from queue and delete state - void terminateState(ExecutionState &state); + void terminateState(ExecutionState &state, + StateTerminationType terminationType); // pause state void pauseState(ExecutionState &state); @@ -454,6 +477,12 @@ class Executor : public Interpreter { void terminateStateEarly(ExecutionState &state, const llvm::Twine &message, StateTerminationType terminationType); + void reportStateOnTargetError(ExecutionState &state, ReachWithError error); + + /// Save extra information in targeted mode + /// Then just call `terminateStateOnError` + void terminateStateOnTargetError(ExecutionState &state, ReachWithError error); + /// Call error handler and terminate state in case of program errors /// (e.g. free()ing globals, out-of-bound accesses) void terminateStateOnError(ExecutionState &state, const llvm::Twine &message, @@ -461,6 +490,8 @@ class Executor : public Interpreter { const llvm::Twine &longMessage = "", const char *suffix = nullptr); + void terminateStateOnTerminator(ExecutionState &state); + /// Call error handler and terminate state in case of execution errors /// (things that should not be possible, like illegal instruction or /// unlowered intrinsic, or unsupported intrinsics, like inline assembly) @@ -485,6 +516,21 @@ class Executor : public Interpreter { const std::string &name, const ref source); + ExecutionState *prepareStateForPOSIX(KInstIterator &caller, + ExecutionState *state); + + void prepareTargetedExecution(ExecutionState *initialState, + ref whitelist); + + void increaseProgressVelocity(ExecutionState &state, KBlock *block); + + bool decreaseConfidenceFromStoppedStates( + SetOfStates &left_states, + HaltExecution::Reason reason = HaltExecution::NotHalt); + + void checkNullCheckAfterDeref(ref cond, ExecutionState &state, + ExecutionState *fstate, ExecutionState *sstate); + template void computeOffsetsSeqTy(KGEPInstruction *kgepi, ref &constantOffset, uint64_t index, @@ -513,7 +559,7 @@ class Executor : public Interpreter { /// Only for debug purposes; enable via debugger or klee-control void dumpStates(); - void dumpPTree(); + void dumpPForest(); public: Executor(llvm::LLVMContext &ctx, const InterpreterOptions &opts, @@ -524,6 +570,8 @@ class Executor : public Interpreter { void setPathWriter(TreeStreamWriter *tsw) override { pathWriter = tsw; } + bool hasTargetForest() const override { return !targets.empty(); } + void setSymbolicPathWriter(TreeStreamWriter *tsw) override { symPathWriter = tsw; } @@ -544,18 +592,47 @@ class Executor : public Interpreter { setModule(std::vector> &userModules, std::vector> &libsModules, const ModuleOptions &opts, - const std::vector &mainModuleFunctions) override; + const std::vector &mainModuleFunctions, + std::unique_ptr origInfos) override; void useSeeds(const std::vector *seeds) override { usingSeeds = seeds; } + ExecutionState *formState(llvm::Function *f); + ExecutionState *formState(llvm::Function *f, int argc, char **argv, + char **envp); + + void clearGlobal(); + + void clearMemory(); + + void prepareSymbolicValue( + ExecutionState &state, KInstruction *targetW, + std::string name = "symbolic_value", + ref source = SourceBuilder::symbolicValue()); + + void prepareSymbolicRegister(ExecutionState &state, StackFrame &sf, + unsigned index); + + void prepareSymbolicArgs(ExecutionState &state, KFunction *kf); + + ref makeSymbolic(llvm::Value *value, ExecutionState &state, + uint64_t size, Expr::Width width, + const std::string &name, ref source); + + ref makeSymbolicValue(llvm::Value *value, ExecutionState &state, + uint64_t size, Expr::Width width, + const std::string &name); + void runFunctionAsMain(llvm::Function *f, int argc, char **argv, char **envp) override; /*** Runtime options ***/ - void setHaltExecution(bool value) override { haltExecution = value; } + void setHaltExecution(HaltExecution::Reason value) override { + haltExecution = value; + } void setInhibitForking(bool value) override { inhibitForking = value; } diff --git a/lib/Core/PForest.cpp b/lib/Core/PForest.cpp new file mode 100644 index 0000000000..d1374f7be4 --- /dev/null +++ b/lib/Core/PForest.cpp @@ -0,0 +1,49 @@ +//===-- PForest.cpp-------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PForest.h" + +#include "ExecutionState.h" + +#include "klee/Expr/Expr.h" +#include "klee/Expr/ExprPPrinter.h" +#include "klee/Support/OptionCategories.h" + +#include +#include + +using namespace klee; +using namespace llvm; + +void PForest::addRoot(ExecutionState *initialState) { + PTree *tree = new PTree(initialState, nextID++); + trees[tree->getID()] = tree; +} + +void PForest::attach(PTreeNode *node, ExecutionState *leftState, + ExecutionState *rightState, BranchType reason) { + assert(trees.find(node->getTreeID()) != trees.end()); + trees[node->getTreeID()]->attach(node, leftState, rightState, reason); +} + +void PForest::remove(PTreeNode *node) { + assert(trees.find(node->getTreeID()) != trees.end()); + trees[node->getTreeID()]->remove(node); +} + +void PForest::dump(llvm::raw_ostream &os) { + for (auto &ntree : trees) + ntree.second->dump(os); +} + +PForest::~PForest() { + for (auto &ntree : trees) + delete ntree.second; + trees.clear(); +} diff --git a/lib/Core/PForest.h b/lib/Core/PForest.h new file mode 100644 index 0000000000..dd4236e948 --- /dev/null +++ b/lib/Core/PForest.h @@ -0,0 +1,50 @@ +//===-- PForest.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_PFOREST_H +#define KLEE_PFOREST_H + +#include "PTree.h" + +#include + +namespace klee { +class ExecutionState; +class PTreeNode; +class PTree; + +class PForest { + // Number of registered ID + int registeredIds = 0; + std::map trees; + // The global tree counter + std::uint32_t nextID = 1; + +public: + PForest() = default; + ~PForest(); + void addRoot(ExecutionState *initialState); + void attach(PTreeNode *node, ExecutionState *leftState, + ExecutionState *rightState, BranchType reason); + void remove(PTreeNode *node); + const std::map &getPTrees() { return trees; } + void dump(llvm::raw_ostream &os); + std::uint8_t getNextId() { + std::uint8_t id = 1 << registeredIds++; + if (registeredIds > PtrBitCount) { + klee_error("PForest cannot support more than %d RandomPathSearchers", + PtrBitCount); + } + return id; + } +}; +} // namespace klee + +#endif /* KLEE_PFOREST_H */ diff --git a/lib/Core/PTree.cpp b/lib/Core/PTree.cpp index 482afd510d..64d83bd2dd 100644 --- a/lib/Core/PTree.cpp +++ b/lib/Core/PTree.cpp @@ -31,8 +31,9 @@ cl::opt } // namespace -PTree::PTree(ExecutionState *initialState) - : root(PTreeNodePtr(new PTreeNode(nullptr, initialState))) { +PTree::PTree(ExecutionState *initialState, uint32_t treeID) { + id = treeID; + root = PTreeNodePtr(new PTreeNode(nullptr, initialState, id)); initialState->ptreeNode = root.getPointer(); } @@ -42,14 +43,15 @@ void PTree::attach(PTreeNode *node, ExecutionState *leftState, assert(node == rightState->ptreeNode && "Attach assumes the right state is the current state"); node->state = nullptr; - node->left = PTreeNodePtr(new PTreeNode(node, leftState)); + node->left = PTreeNodePtr(new PTreeNode(node, leftState, id)); // The current node inherits the tag uint8_t currentNodeTag = root.getInt(); if (node->parent) currentNodeTag = node->parent->left.getPointer() == node ? node->parent->left.getInt() : node->parent->right.getInt(); - node->right = PTreeNodePtr(new PTreeNode(node, rightState), currentNodeTag); + node->right = + PTreeNodePtr(new PTreeNode(node, rightState, id), currentNodeTag); } void PTree::remove(PTreeNode *n) { @@ -128,8 +130,8 @@ void PTree::dump(llvm::raw_ostream &os) { delete pp; } -PTreeNode::PTreeNode(PTreeNode *parent, ExecutionState *state) - : parent{parent}, state{state} { +PTreeNode::PTreeNode(PTreeNode *parent, ExecutionState *state, uint32_t id) + : parent{parent}, state{state}, treeID{id} { state->ptreeNode = this; left = PTreeNodePtr(nullptr); right = PTreeNodePtr(nullptr); diff --git a/lib/Core/PTree.h b/lib/Core/PTree.h index f1f101737f..02602a247c 100644 --- a/lib/Core/PTree.h +++ b/lib/Core/PTree.h @@ -34,32 +34,31 @@ class PTreeNode { PTreeNodePtr right; ExecutionState *state = nullptr; + std::uint32_t treeID; + PTreeNode(const PTreeNode &) = delete; - PTreeNode(PTreeNode *parent, ExecutionState *state); + PTreeNode(PTreeNode *parent, ExecutionState *state, std::uint32_t id); ~PTreeNode() = default; + + std::uint32_t getTreeID() const { return treeID; }; }; class PTree { - // Number of registered ID - int registeredIds = 0; +private: + // The tree id + uint32_t id; public: PTreeNodePtr root; - explicit PTree(ExecutionState *initialState); + PTree(ExecutionState *initialState, uint32_t id); + explicit PTree(ExecutionState *initialState) : PTree(initialState, 0) {} ~PTree() = default; void attach(PTreeNode *node, ExecutionState *leftState, ExecutionState *rightState, BranchType reason); void remove(PTreeNode *node); void dump(llvm::raw_ostream &os); - std::uint8_t getNextId() { - std::uint8_t id = 1 << registeredIds++; - if (registeredIds > PtrBitCount) { - klee_error("PTree cannot support more than %d RandomPathSearchers", - PtrBitCount); - } - return id; - } + std::uint32_t getID() const { return id; }; }; } // namespace klee diff --git a/lib/Core/Searcher.cpp b/lib/Core/Searcher.cpp index dc5249e6ba..5222e5b138 100644 --- a/lib/Core/Searcher.cpp +++ b/lib/Core/Searcher.cpp @@ -15,7 +15,7 @@ #include "MergeHandler.h" #include "PTree.h" #include "StatsTracker.h" -#include "Target.h" +#include "TargetCalculator.h" #include "klee/ADT/DiscretePDF.h" #include "klee/ADT/RNG.h" @@ -24,6 +24,7 @@ #include "klee/Module/InstructionInfoTable.h" #include "klee/Module/KInstruction.h" #include "klee/Module/KModule.h" +#include "klee/Module/Target.h" #include "klee/Statistics/Statistics.h" #include "klee/Support/ErrorHandling.h" #include "klee/System/Time.h" @@ -147,12 +148,15 @@ static unsigned int ulog2(unsigned int val) { return ret; } -TargetedSearcher::TargetedSearcher(Target target, CodeGraphDistance &_distance) +/// + +TargetedSearcher::TargetedSearcher(ref target, + CodeGraphDistance &_distance) : states(std::make_unique< WeightedQueue>()), target(target), codeGraphDistance(_distance), distanceToTargetFunction( - codeGraphDistance.getBackwardDistance(target.getBlock()->parent)) {} + codeGraphDistance.getBackwardDistance(target->getBlock()->parent)) {} ExecutionState &TargetedSearcher::selectState() { return *states->choose(0); } @@ -161,7 +165,7 @@ bool TargetedSearcher::distanceInCallGraph(KFunction *kf, KBlock *kb, distance = UINT_MAX; const std::unordered_map &dist = codeGraphDistance.getDistance(kb); - KBlock *targetBB = target.getBlock(); + KBlock *targetBB = target->getBlock(); KFunction *targetF = targetBB->parent; if (kf == targetF && dist.count(targetBB) != 0) { @@ -171,11 +175,12 @@ bool TargetedSearcher::distanceInCallGraph(KFunction *kf, KBlock *kb, for (auto &kCallBlock : kf->kCallBlocks) { if (dist.count(kCallBlock) != 0) { - KFunction *calledKFunction = - kf->parent->functionMap[kCallBlock->calledFunction]; - if (distanceToTargetFunction.count(calledKFunction) != 0 && - distance > distanceToTargetFunction.at(calledKFunction) + 1) { - distance = distanceToTargetFunction.at(calledKFunction) + 1; + 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; + } } } } @@ -187,6 +192,7 @@ 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 = @@ -201,8 +207,10 @@ TargetedSearcher::tryGetLocalWeight(ExecutionState *es, weight_type &weight, if (localWeight == UINT_MAX) return Miss; - if (localWeight == 0 && prevKB != currentKB) + if (localWeight == 0 && (initKB == currentKB || prevKB != currentKB || + target->shouldFailOnThisTarget())) { return Done; + } intWeight += localWeight; weight = ulog2(intWeight); // number on [0,32)-discrete-interval @@ -215,10 +223,12 @@ TargetedSearcher::tryGetPreTargetWeight(ExecutionState *es, KFunction *currentKF = es->pc->parent->parent; std::vector localTargets; for (auto &kCallBlock : currentKF->kCallBlocks) { - KFunction *calledKFunction = - currentKF->parent->functionMap[kCallBlock->calledFunction]; - if (distanceToTargetFunction.count(calledKFunction) > 0) { - localTargets.push_back(kCallBlock); + for (auto &calledFunction : kCallBlock->calledFunctions) { + KFunction *calledKFunction = + currentKF->parent->functionMap[calledFunction]; + if (distanceToTargetFunction.count(calledKFunction) > 0) { + localTargets.push_back(kCallBlock); + } } } @@ -246,27 +256,33 @@ TargetedSearcher::tryGetPostTargetWeight(ExecutionState *es, TargetedSearcher::WeightResult TargetedSearcher::tryGetTargetWeight(ExecutionState *es, weight_type &weight) { - std::vector localTargets = {target.getBlock()}; + 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()) { - if (es->prevPC->parent == target.getBlock() && - es->prevPC == target.getBlock()->getLastInstruction()) { + 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()) { + } else if (es->pc->parent == target->getBlock()) { weight = 0; return Continue; } } + if (target->shouldFailOnThisTarget() && target->isTheSameAsIn(es->prevPC) && + target->isThatError(es->error)) { + return Done; + } + BasicBlock *bb = es->getPCBlock(); KBlock *kb = es->pc->parent->parent->blockMap[bb]; KInstruction *ki = es->pc; - if (kb->numInstructions && kb->getFirstInstruction() != ki && + if (!target->shouldFailOnThisTarget() && kb->numInstructions && + !isa(kb) && kb->getFirstInstruction() != ki && states->tryGetWeight(es, weight)) { return Continue; } @@ -276,7 +292,16 @@ TargetedSearcher::tryGetWeight(ExecutionState *es, weight_type &weight) { unsigned callWeight; if (distanceInCallGraph(sfi->kf, kb, callWeight)) { callWeight *= 2; - callWeight += sfNum; + 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; @@ -300,13 +325,25 @@ TargetedSearcher::tryGetWeight(ExecutionState *es, weight_type &weight) { 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; } 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(), @@ -314,12 +351,14 @@ void TargetedSearcher::update( switch (tryGetWeight(current, weight)) { case Continue: states->update(current, weight); + canReach = true; break; case Done: - reachedOnLastUpdate.push_back(current); + reachedOnLastUpdate.insert(current); + canReach = true; break; case Miss: - current->targets.erase(target); + current->targetForest.remove(target); states->remove(current); break; } @@ -330,37 +369,43 @@ void TargetedSearcher::update( switch (tryGetWeight(state, weight)) { case Continue: states->insert(state, weight); + canReach = true; break; case Done: states->insert(state, weight); - reachedOnLastUpdate.push_back(state); + reachedOnLastUpdate.insert(state); + canReach = true; break; case Miss: - state->targets.erase(target); + state->targetForest.remove(target); break; } } // remove states for (const auto state : removedStates) { - if (target.atReturn() && - state->prevPC == target.getBlock()->getLastInstruction()) - reachedOnLastUpdate.push_back(state); - else { + if (target->atReturn() && !target->shouldFailOnThisTarget() && + state->prevPC == target->getBlock()->getLastInstruction()) { + canReach = true; + reachedOnLastUpdate.insert(state); + } else { switch (tryGetWeight(state, weight)) { case Done: - reachedOnLastUpdate.push_back(state); + reachedOnLastUpdate.insert(state); + canReach = true; break; case Miss: - state->targets.erase(target); + state->targetForest.remove(target); states->remove(state); break; case Continue: states->remove(state); + canReach = true; break; } } } + return canReach; } bool TargetedSearcher::empty() { return states->empty(); } @@ -372,19 +417,19 @@ void TargetedSearcher::printName(llvm::raw_ostream &os) { TargetedSearcher::~TargetedSearcher() { while (!states->empty()) { auto &state = selectState(); - state.targets.erase(target); + state.targetForest.remove(target); states->remove(&state); } } -std::vector TargetedSearcher::reached() { +std::set TargetedSearcher::reached() { return reachedOnLastUpdate; } void TargetedSearcher::removeReached() { for (auto state : reachedOnLastUpdate) { states->remove(state); - state->targets.erase(target); + state->targetForest.stepTo(target); } reachedOnLastUpdate.clear(); } @@ -394,24 +439,45 @@ void TargetedSearcher::removeReached() { GuidedSearcher::GuidedSearcher( Searcher *baseSearcher, CodeGraphDistance &codeGraphDistance, TargetCalculator &stateHistory, + std::set + &removedButReachableStates, std::set &pausedStates, - std::size_t bound, RNG &rng, bool stopAfterReachingTarget) - : baseSearcher(baseSearcher), codeGraphDistance(codeGraphDistance), - stateHistory(stateHistory), pausedStates(pausedStates), bound(bound), - theRNG(rng), stopAfterReachingTarget(stopAfterReachingTarget) {} + std::size_t bound, RNG &rng) + : guidance(CoverageGuidance), baseSearcher(baseSearcher), + codeGraphDistance(codeGraphDistance), stateHistory(&stateHistory), + removedButReachableStates(removedButReachableStates), + pausedStates(pausedStates), bound(bound), theRNG(rng) {} + +GuidedSearcher::GuidedSearcher( + CodeGraphDistance &codeGraphDistance, + std::set + &removedButReachableStates, + std::set &pausedStates, + std::size_t bound, RNG &rng) + : guidance(ErrorGuidance), baseSearcher(nullptr), + codeGraphDistance(codeGraphDistance), stateHistory(nullptr), + removedButReachableStates(removedButReachableStates), + pausedStates(pausedStates), bound(bound), theRNG(rng) {} ExecutionState &GuidedSearcher::selectState() { - unsigned size = targets.size(); + unsigned size = historiesAndTargets.size(); index = theRNG.getInt32() % (size + 1); - if (stopAfterReachingTarget && index == size) { - return baseSearcher->selectState(); + ExecutionState *state = nullptr; + if (CoverageGuidance == guidance && index == size) { + assert(baseSearcher); + state = &baseSearcher->selectState(); } else { index = index % size; - Target target = targets[index]; - assert(targetedSearchers.find(target) != targetedSearchers.end() && - !targetedSearchers[target]->empty()); - return targetedSearchers[target]->selectState(); + auto &historyTargetPair = historiesAndTargets[index]; + 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(); } + return *state; } bool GuidedSearcher::isStuck(ExecutionState &state) { @@ -420,6 +486,78 @@ bool GuidedSearcher::isStuck(ExecutionState &state) { state.multilevel.count(state.getPCBlock()) > bound); } +bool GuidedSearcher::updateTargetedSearcher( + ref history, ref target, + ExecutionState *current, std::vector &addedStates, + std::vector &removedStates) { + bool canReach = false; + auto &historiedTargetedSearchers = targetedSearchers[history]; + + if (historiedTargetedSearchers.count(target) != 0 || + tryAddTarget(history, target)) { + + if (current) { + assert(current->targetForest.contains(target)); + for (auto &reached : reachedTargets) { + current->targetForest.blockIn(target, reached); + } + if (!current->targetForest.contains(target)) { + removedStates.push_back(current); + } + } + for (std::vector::iterator is = addedStates.begin(), + ie = addedStates.end(); + is != ie;) { + ExecutionState *state = *is; + assert(state->targetForest.contains(target)); + for (auto &reached : reachedTargets) { + state->targetForest.blockIn(target, reached); + } + if (!state->targetForest.contains(target)) { + is = addedStates.erase(is); + ie = addedStates.end(); + } else { + ++is; + } + } + + canReach = historiedTargetedSearchers[target]->updateCheckCanReach( + current, addedStates, removedStates); + if (historiedTargetedSearchers[target]->empty()) { + removeTarget(history, target); + } + } else if (isReached(history, target)) { + canReach = true; + if (current) { + current->targetForest.remove(target); + } + assert(removedStates.empty()); + for (auto &state : addedStates) { + state->targetForest.remove(target); + } + } + if (targetedSearchers[history].empty()) + targetedSearchers.erase(history); + return canReach; +} + +static void updateConfidences(ExecutionState *current, + const GuidedSearcher::TargetToStateUnorderedSetMap + &reachableStatesOfTarget) { + if (current) + current->targetForest.divideConfidenceBy(reachableStatesOfTarget); +} + +static void updateConfidences(ExecutionState *current, + const std::vector &addedStates, + const GuidedSearcher::TargetToStateUnorderedSetMap + &reachableStatesOfTarget) { + updateConfidences(current, reachableStatesOfTarget); + for (auto state : addedStates) { + updateConfidences(state, reachableStatesOfTarget); + } +} + void GuidedSearcher::innerUpdate( ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { @@ -428,37 +566,46 @@ void GuidedSearcher::innerUpdate( baseRemovedStates.insert(baseRemovedStates.end(), removedStates.begin(), removedStates.end()); - std::set innerTargets; - - for (const auto state : baseAddedStates) { - if (!state->targets.empty()) { - for (auto target : state->targets) { - innerTargets.insert(target); - addedTStates[target].push_back(state); + 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); + auto is = + std::find(baseAddedStates.begin(), baseAddedStates.end(), state); + assert(is != baseAddedStates.end()); + baseAddedStates.erase(is); } - } else { - targetlessStates.push_back(state); } } - for (const auto state : baseRemovedStates) { - for (auto target : state->targets) { - innerTargets.insert(target); - removedTStates[target].push_back(state); + for (const auto state : baseAddedStates) { + if (!state->targetForest.empty()) { + targetedAddedStates.push_back(state); + } else { + targetlessStates.push_back(state); } } - std::set currTargets; + TargetForest::TargetsSet currTargets; if (current) { - currTargets = current->targets; + auto targets = current->targetForest.getTargets(); + for (auto &targetF : *targets) { + auto target = targetF.first; + assert(target && "Target should be not null!"); + currTargets.insert(target); + } } - if (current && !currTargets.empty()) { - for (auto target : current->targets) { - innerTargets.insert(target); - } - } else if (current && std::find(removedStates.begin(), removedStates.end(), - current) == removedStates.end()) { + if (current && currTargets.empty() && + std::find(baseRemovedStates.begin(), baseRemovedStates.end(), current) == + baseRemovedStates.end()) { targetlessStates.push_back(current); } @@ -475,74 +622,130 @@ void GuidedSearcher::innerUpdate( } } - for (const auto state : targetlessStates) { - KInstruction *prevKI = state->prevPC; - if (isStuck(*state)) { - Target target(stateHistory.calculate(*state)); - if (target) { - state->targets.insert(target); - innerTargets.insert(target); - addedTStates[target].push_back(state); - } else { - pausedStates.insert(state); - if (std::find(addedStates.begin(), addedStates.end(), state) != - addedStates.end()) { - auto is = - std::find(baseAddedStates.begin(), baseAddedStates.end(), state); - baseAddedStates.erase(is); + std::vector tmpAddedStates; + std::vector tmpRemovedStates; + + if (CoverageGuidance == guidance) { + assert(stateHistory); + 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 { - baseRemovedStates.push_back(state); + 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(); - for (auto target : innerTargets) { - ExecutionState *currTState = - currTargets.count(target) != 0 ? current : nullptr; + TargetToStateUnorderedSetMap reachableStatesOfTarget; - if (targetedSearchers.count(target) == 0) { - addTarget(target); + 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(); } - targetedSearchers[target]->update(currTState, addedTStates[target], - removedTStates[target]); - if (targetedSearchers[target]->empty()) { - removeTarget(target); + } + + for (const auto state : targetedAddedStates) { + 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 (state->targetForest.contains(target)) { + tmpAddedStates.push_back(state); + assert(!state->targetForest.empty()); + + bool canReach = updateTargetedSearcher( + history, target, nullptr, tmpAddedStates, tmpRemovedStates); + if (canReach) + reachableStatesOfTarget[target].insert(state); + } + tmpAddedStates.clear(); + tmpRemovedStates.clear(); } - addedTStates[target].clear(); - removedTStates[target].clear(); } + targetedAddedStates.clear(); - baseSearcher->update(current, baseAddedStates, baseRemovedStates); + for (const auto state : baseRemovedStates) { + 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) { + tmpRemovedStates.push_back(state); + bool canReach = updateTargetedSearcher(history, target, nullptr, + tmpAddedStates, tmpRemovedStates); + if (canReach) { + reachableStatesOfTarget[target].insert(state); + removedButReachableStates.insert(state); + } + tmpAddedStates.clear(); + tmpRemovedStates.clear(); + } + } + + if (CoverageGuidance == guidance) { + assert(baseSearcher); + baseSearcher->update(current, baseAddedStates, baseRemovedStates); + } + updateConfidences(current, baseAddedStates, reachableStatesOfTarget); baseAddedStates.clear(); baseRemovedStates.clear(); } -void GuidedSearcher::update( - ExecutionState *current, const std::vector &addedStates, - const std::vector &removedStates, - std::map> &reachedStates) { - innerUpdate(current, addedStates, removedStates); - collectReached(reachedStates); - clearReached(); -} - void GuidedSearcher::update( ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { innerUpdate(current, addedStates, removedStates); - clearReached(); + clearReached(removedStates); } -void GuidedSearcher::collectReached( - std::map> &reachedStates) { - std::map> ret; - std::vector targets; - for (auto const &targetSearcher : targetedSearchers) - targets.push_back(targetSearcher.first); - for (auto target : targets) { - auto reached = targetedSearchers[target]->reached(); +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); @@ -550,49 +753,127 @@ void GuidedSearcher::collectReached( } } -void GuidedSearcher::clearReached() { - std::vector targets; - for (auto const &targetSearcher : targetedSearchers) - targets.push_back(targetSearcher.first); - for (auto target : targets) { - auto reached = targetedSearchers[target]->reached(); - if (!reached.empty()) { - targetedSearchers[target]->removeReached(); - if (stopAfterReachingTarget || targetedSearchers[target]->empty()) { - removeTarget(target); +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); + } + } + } + + 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 (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 &target : stateTargets) { + if (state->targetForest.contains(target)) { + tmpAddedStates.push_back(state); + updateTargetedSearcher(history, target, nullptr, tmpAddedStates, + tmpRemovedStates); + } + tmpAddedStates.clear(); + tmpRemovedStates.clear(); + } + } } bool GuidedSearcher::empty() { - return stopAfterReachingTarget ? baseSearcher->empty() - : targetedSearchers.empty(); + return CoverageGuidance == guidance ? baseSearcher->empty() + : targetedSearchers.empty(); } void GuidedSearcher::printName(llvm::raw_ostream &os) { - os << "GuidedSearcher"; + os << "GuidedSearcher\n"; +} + +bool GuidedSearcher::isReached(ref history, + ref target) { + return reachedTargets.count(target) != 0; } -void GuidedSearcher::addTarget(Target target) { - targetedSearchers[target] = +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] = std::make_unique(target, codeGraphDistance); auto it = std::find_if( - targets.begin(), targets.end(), - [target](const Target &element) { return element == target; }); - assert(it == targets.end()); - targets.push_back(target); - assert(targets.size() == targetedSearchers.size()); + 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()); + historiesAndTargets.push_back({history, target}); + return true; } -void GuidedSearcher::removeTarget(Target target) { - targetedSearchers.erase(target); +GuidedSearcher::TargetForestHisoryTargetVector::iterator +GuidedSearcher::removeTarget(ref history, + ref target) { + targetedSearchers.at(history).erase(target); auto it = std::find_if( - targets.begin(), targets.end(), - [target](const Target &element) { return element == target; }); - assert(it != targets.end()); - targets.erase(it); - assert(targets.size() == targetedSearchers.size()); + historiesAndTargets.begin(), historiesAndTargets.end(), + [&history, &target]( + const std::pair, ref> &element) { + return element.first.get() == history.get() && + element.second.get() == target.get(); + }); + assert(it != historiesAndTargets.end()); + return historiesAndTargets.erase(it); } /// @@ -721,15 +1002,19 @@ void WeightedRandomSearcher::printName(llvm::raw_ostream &os) { #define IS_OUR_NODE_VALID(n) \ (((n).getPointer() != nullptr) && (((n).getInt() & idBitMask) != 0)) -RandomPathSearcher::RandomPathSearcher(PTree &processTree, RNG &rng) - : processTree{processTree}, theRNG{rng}, idBitMask{ - processTree.getNextId()} {}; +RandomPathSearcher::RandomPathSearcher(PForest &processForest, RNG &rng) + : processForest{processForest}, theRNG{rng}, + idBitMask{processForest.getNextId()} {}; ExecutionState &RandomPathSearcher::selectState() { - unsigned flips = 0, bits = 0; - assert(processTree.root.getInt() & idBitMask && - "Root should belong to the searcher"); - PTreeNode *n = processTree.root.getPointer(); + unsigned flips = 0, bits = 0, range = 0; + PTreeNodePtr *root = nullptr; + while (!root || !IS_OUR_NODE_VALID(*root)) + root = &processForest.getPTrees() + .at(range++ % processForest.getPTrees().size() + 1) + ->root; + assert(root->getInt() & idBitMask && "Root should belong to the searcher"); + PTreeNode *n = root->getPointer(); while (!n->state) { if (!IS_OUR_NODE_VALID(n->left)) { assert(IS_OUR_NODE_VALID(n->right) && @@ -757,13 +1042,14 @@ void RandomPathSearcher::update( ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) { // insert states - for (auto es : addedStates) { + for (auto &es : addedStates) { PTreeNode *pnode = es->ptreeNode, *parent = pnode->parent; + PTreeNodePtr &root = processForest.getPTrees().at(pnode->getTreeID())->root; PTreeNodePtr *childPtr; childPtr = parent ? ((parent->left.getPointer() == pnode) ? &parent->left : &parent->right) - : &processTree.root; + : &root; while (pnode && !IS_OUR_NODE_VALID(*childPtr)) { childPtr->setInt(childPtr->getInt() | idBitMask); pnode = parent; @@ -773,20 +1059,21 @@ void RandomPathSearcher::update( childPtr = parent ? ((parent->left.getPointer() == pnode) ? &parent->left : &parent->right) - : &processTree.root; + : &root; } } // remove states for (auto es : removedStates) { PTreeNode *pnode = es->ptreeNode, *parent = pnode->parent; + PTreeNodePtr &root = processForest.getPTrees().at(pnode->getTreeID())->root; while (pnode && !IS_OUR_NODE_VALID(pnode->left) && !IS_OUR_NODE_VALID(pnode->right)) { auto childPtr = parent ? ((parent->left.getPointer() == pnode) ? &parent->left : &parent->right) - : &processTree.root; + : &root; assert(IS_OUR_NODE_VALID(*childPtr) && "Removing pTree child not ours"); childPtr->setInt(childPtr->getInt() & ~idBitMask); pnode = parent; @@ -797,7 +1084,10 @@ void RandomPathSearcher::update( } bool RandomPathSearcher::empty() { - return !IS_OUR_NODE_VALID(processTree.root); + bool res = true; + for (const auto &ntree : processForest.getPTrees()) + res = res && !IS_OUR_NODE_VALID(ntree.second->root); + return res; } void RandomPathSearcher::printName(llvm::raw_ostream &os) { diff --git a/lib/Core/Searcher.h b/lib/Core/Searcher.h index 52120d58bb..064e190fa2 100644 --- a/lib/Core/Searcher.h +++ b/lib/Core/Searcher.h @@ -11,6 +11,7 @@ #define KLEE_SEARCHER_H #include "ExecutionState.h" +#include "PForest.h" #include "PTree.h" #include "klee/ADT/RNG.h" #include "klee/Module/KModule.h" @@ -22,6 +23,7 @@ #include #include #include +#include #include namespace llvm { @@ -37,6 +39,8 @@ template class DiscretePDF; template class WeightedQueue; class ExecutionState; class Executor; +class TargetCalculator; +class TargetForest; /// A Searcher implements an exploration strategy for the Executor by selecting /// states for further exploration using different strategies or heuristics. @@ -137,10 +141,10 @@ class TargetedSearcher final : public Searcher { std::unique_ptr> states; - Target target; + ref target; CodeGraphDistance &codeGraphDistance; const std::unordered_map &distanceToTargetFunction; - std::vector reachedOnLastUpdate; + std::set reachedOnLastUpdate; bool distanceInCallGraph(KFunction *kf, KBlock *kb, unsigned int &distance); WeightResult tryGetLocalWeight(ExecutionState *es, weight_type &weight, @@ -151,66 +155,119 @@ class TargetedSearcher final : public Searcher { WeightResult tryGetWeight(ExecutionState *es, weight_type &weight); public: - TargetedSearcher(Target target, CodeGraphDistance &distance); + TargetedSearcher(ref target, CodeGraphDistance &distance); ~TargetedSearcher() override; ExecutionState &selectState() override; void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) override; + bool updateCheckCanReach(ExecutionState *current, + const std::vector &addedStates, + const std::vector &removedStates); bool empty() override; void printName(llvm::raw_ostream &os) override; - std::vector reached(); + std::set reached(); void removeReached(); }; class GuidedSearcher final : public Searcher { +private: + template + class TargetHashMap + : public std::unordered_map, T, RefTargetHash, RefTargetCmp> { + }; + class TargetHashSet + : public std::unordered_set, RefTargetHash, RefTargetCmp> {}; + +public: + using TargetToStateUnorderedSetMap = + TargetHashMap>; private: + using TargetToSearcherMap = TargetHashMap>; + using TargetToStateSetMap = + TargetHashMap>; + using TargetToStateVectorMap = TargetHashMap>; + + template + class TargetForestHistoryHashMap + : public std::unordered_map, T, + RefTargetsHistoryHash, RefTargetsHistoryCmp> { + }; + template + class TargetForestHistoryTargetsHashMap + : public TargetForestHistoryHashMap> {}; + + using TargetForestHistoryToSearcherMap = + TargetForestHistoryTargetsHashMap>; + using TargetForestHistoryToStateSetMap = + TargetForestHistoryTargetsHashMap>; + using TargetForestHisoryToStateVectorMap = + TargetForestHistoryTargetsHashMap>; + using TargetForestHisoryToTargetSet = TargetForestHistoryHashMap< + std::unordered_set, RefTargetHash, RefTargetCmp>>; + using TargetForestHisoryToTargetVector = + TargetForestHistoryHashMap>>; + using TargetForestHisoryTargetVector = + std::vector, ref>>; + + enum Guidance { CoverageGuidance, ErrorGuidance }; + + Guidance guidance; std::unique_ptr baseSearcher; - std::map> targetedSearchers; + TargetForestHistoryToSearcherMap targetedSearchers; CodeGraphDistance &codeGraphDistance; - TargetCalculator &stateHistory; + TargetCalculator *stateHistory; + TargetHashSet reachedTargets; + std::set + &removedButReachableStates; std::set &pausedStates; std::size_t bound; RNG &theRNG; unsigned index{1}; - bool stopAfterReachingTarget; std::vector baseAddedStates; std::vector baseRemovedStates; - std::map> addedTStates; - std::map> removedTStates; + std::vector targetedAddedStates; std::vector targetlessStates; - std::vector targets; + TargetForestHisoryTargetVector historiesAndTargets; - void addTarget(Target target); - void removeTarget(Target target); + 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 clearReached(); - void collectReached( - std::map> &reachedStates); + void clearReached(const std::vector &removedStates); + void collectReached(TargetToStateSetMap &reachedStates); + bool isReached(ref history, ref target); public: GuidedSearcher( Searcher *baseSearcher, CodeGraphDistance &codeGraphDistance, TargetCalculator &stateHistory, + std::set + &removedButReachableStates, + std::set &pausedStates, + std::size_t bound, RNG &rng); + GuidedSearcher( + CodeGraphDistance &codeGraphDistance, + std::set + &removedButReachableStates, std::set &pausedStates, - std::size_t bound, RNG &rng, bool stopAfterReachingTarget = true); + std::size_t bound, RNG &rng); ~GuidedSearcher() override = default; ExecutionState &selectState() override; void update(ExecutionState *current, const std::vector &addedStates, const std::vector &removedStates) override; - void - update(ExecutionState *current, - const std::vector &addedStates, - const std::vector &removedStates, - std::map> &reachedStates); bool empty() override; void printName(llvm::raw_ostream &os) override; @@ -270,7 +327,7 @@ class WeightedRandomSearcher final : public Searcher { /// /// The ownership bits are maintained in the update method. class RandomPathSearcher final : public Searcher { - PTree &processTree; + PForest &processForest; RNG &theRNG; // Unique bitmask of this searcher @@ -279,7 +336,7 @@ class RandomPathSearcher final : public Searcher { public: /// \param processTree The process tree. /// \param RNG A random number generator. - RandomPathSearcher(PTree &processTree, RNG &rng); + RandomPathSearcher(PForest &processForest, RNG &rng); ~RandomPathSearcher() override = default; ExecutionState &selectState() override; diff --git a/lib/Core/SpecialFunctionHandler.cpp b/lib/Core/SpecialFunctionHandler.cpp index 9963e50931..24caa37e49 100644 --- a/lib/Core/SpecialFunctionHandler.cpp +++ b/lib/Core/SpecialFunctionHandler.cpp @@ -37,6 +37,7 @@ #include #include +#include using namespace llvm; using namespace klee; @@ -56,6 +57,11 @@ cl::opt "condition given to klee_assume() rather than " "emitting an error (default=false)"), cl::cat(TerminationCat)); + +cl::opt CheckOutOfMemory("check-out-of-memory", cl::init(false), + cl::desc("Enable out-of-memory checking during " + "memory allocation (default=false)"), + cl::cat(ExecCat)); } // namespace /// \todo Almost all of the demands in this file should be replaced @@ -86,6 +92,7 @@ static SpecialFunctionHandler::HandlerInfo handlerInfo[] = { add("calloc", handleCalloc, true), add("free", handleFree, false), add("klee_assume", handleAssume, false), + add("klee_sleep", handleSleep, false), add("klee_check_memory_access", handleCheckMemoryAccess, false), add("klee_get_valuef", handleGetValue, true), add("klee_get_valued", handleGetValue, true), @@ -138,6 +145,8 @@ static SpecialFunctionHandler::HandlerInfo handlerInfo[] = { // operator new[](unsigned long) add("_Znam", handleNewArray, true), + // operator new[](unsigned long, std::nothrow_t const&) + add("_ZnamRKSt9nothrow_t", handleNewNothrowArray, true), // operator new(unsigned long) add("_Znwm", handleNew, true), @@ -476,7 +485,8 @@ void SpecialFunctionHandler::handleNew(ExecutionState &state, // XXX should type check args assert(arguments.size() == 1 && "invalid number of arguments to new"); executor.executeAlloc(state, arguments[0], false, target, - executor.typeSystemManager->handleAlloc(arguments[0])); + executor.typeSystemManager->handleAlloc(arguments[0]), + false, nullptr, 0, CheckOutOfMemory); } void SpecialFunctionHandler::handleDelete(ExecutionState &state, @@ -496,7 +506,20 @@ void SpecialFunctionHandler::handleNewArray(ExecutionState &state, // XXX should type check args assert(arguments.size() == 1 && "invalid number of arguments to new[]"); executor.executeAlloc(state, arguments[0], false, target, - executor.typeSystemManager->handleAlloc(arguments[0])); + executor.typeSystemManager->handleAlloc(arguments[0]), + false, nullptr, 0, CheckOutOfMemory); +} + +void SpecialFunctionHandler::handleNewNothrowArray( + ExecutionState &state, KInstruction *target, + std::vector> &arguments) { + // XXX should type check args + assert(arguments.size() == 2 && + "invalid number of arguments to new[](unsigned long, std::nothrow_t " + "const&)"); + executor.executeAlloc(state, arguments[0], false, target, + executor.typeSystemManager->handleAlloc(arguments[0]), + false, nullptr, 0, CheckOutOfMemory); } void SpecialFunctionHandler::handleDeleteArray( @@ -513,7 +536,8 @@ void SpecialFunctionHandler::handleMalloc(ExecutionState &state, // XXX should type check args assert(arguments.size() == 1 && "invalid number of arguments to malloc"); executor.executeAlloc(state, arguments[0], false, target, - executor.typeSystemManager->handleAlloc(arguments[0])); + executor.typeSystemManager->handleAlloc(arguments[0]), + false, nullptr, 0, CheckOutOfMemory); } void SpecialFunctionHandler::handleMemalign(ExecutionState &state, @@ -590,6 +614,12 @@ void SpecialFunctionHandler::handleEhTypeid(ExecutionState &state, } #endif // SUPPORT_KLEE_EH_CXX +void SpecialFunctionHandler::handleSleep(ExecutionState &state, + KInstruction *target, + std::vector> &arguments) { + nanosleep((const struct timespec[]){{1, 0}}, NULL); +} + void SpecialFunctionHandler::handleAssume(ExecutionState &state, KInstruction *target, std::vector> &arguments) { @@ -606,7 +636,7 @@ void SpecialFunctionHandler::handleAssume(ExecutionState &state, assert(success && "FIXME: Unhandled solver failure"); if (res) { if (SilentKleeAssume) { - executor.terminateState(state); + executor.terminateState(state, StateTerminationType::SilentExit); } else { executor.terminateStateOnUserError( state, "invalid klee_assume call (provably false)"); @@ -811,7 +841,8 @@ void SpecialFunctionHandler::handleCalloc(ExecutionState &state, ref size = MulExpr::create(arguments[0], arguments[1]); executor.executeAlloc(state, size, false, target, - executor.typeSystemManager->handleAlloc(size), true); + executor.typeSystemManager->handleAlloc(size), true, + nullptr, 0, CheckOutOfMemory); } void SpecialFunctionHandler::handleRealloc(ExecutionState &state, @@ -835,7 +866,8 @@ void SpecialFunctionHandler::handleRealloc(ExecutionState &state, if (zeroPointer.first) { // address == 0 executor.executeAlloc(*zeroPointer.first, size, false, target, - executor.typeSystemManager->handleAlloc(size)); + executor.typeSystemManager->handleAlloc(size), + false, nullptr, 0, CheckOutOfMemory); } if (zeroPointer.second) { // address != 0 Executor::ExactResolutionList rl; @@ -851,7 +883,7 @@ void SpecialFunctionHandler::handleRealloc(ExecutionState &state, executor.executeAlloc(*it->second, size, false, target, executor.typeSystemManager->handleRealloc( os->getDynamicType(), size), - false, os); + false, os, 0, CheckOutOfMemory); } } } diff --git a/lib/Core/SpecialFunctionHandler.h b/lib/Core/SpecialFunctionHandler.h index d21e49c066..10ec81217a 100644 --- a/lib/Core/SpecialFunctionHandler.h +++ b/lib/Core/SpecialFunctionHandler.h @@ -103,6 +103,7 @@ class SpecialFunctionHandler { HANDLER(handleAbort); HANDLER(handleAssert); HANDLER(handleAssertFail); + HANDLER(handleSleep); HANDLER(handleAssume); HANDLER(handleCalloc); HANDLER(handleCheckMemoryAccess); @@ -128,6 +129,7 @@ class SpecialFunctionHandler { HANDLER(handleCloseMerge); HANDLER(handleNew); HANDLER(handleNewArray); + HANDLER(handleNewNothrowArray); HANDLER(handlePreferCex); HANDLER(handlePosixPreferCex); HANDLER(handlePrintExpr); diff --git a/lib/Core/StatsTracker.cpp b/lib/Core/StatsTracker.cpp index 862010d9f6..d69b85a028 100644 --- a/lib/Core/StatsTracker.cpp +++ b/lib/Core/StatsTracker.cpp @@ -1120,7 +1120,7 @@ void StatsTracker::checkCoverage() { case CoverageCheckPoliciy::Halting: klee_message("HaltTimer invoked due to absense of progress in branch " "coverage"); - executor.setHaltExecution(true); + executor.setHaltExecution(HaltExecution::CovCheck); break; case CoverageCheckPoliciy::Release: if (!releaseStates) { @@ -1139,7 +1139,7 @@ void StatsTracker::checkCoverage() { } else { klee_message("HaltTimer invoked due to absense of progress in branch " "coverage"); - executor.setHaltExecution(true); + executor.setHaltExecution(HaltExecution::CovCheck); } break; } diff --git a/lib/Core/Target.cpp b/lib/Core/TargetCalculator.cpp similarity index 91% rename from lib/Core/Target.cpp rename to lib/Core/TargetCalculator.cpp index 9f884c2463..8975b32e21 100644 --- a/lib/Core/Target.cpp +++ b/lib/Core/TargetCalculator.cpp @@ -1,4 +1,4 @@ -//===-- Target.cpp --------------------------------------------------------===// +//===-- TargetCalculator.cpp ---------- -----------------------------------===// // // The KLEE Symbolic Virtual Machine // @@ -7,11 +7,14 @@ // //===----------------------------------------------------------------------===// -#include "Target.h" +#include "TargetCalculator.h" + #include "ExecutionState.h" #include "klee/Module/CodeGraphDistance.h" #include "klee/Module/KInstruction.h" +#include "klee/Module/Target.h" +#include "klee/Module/TargetHash.h" #include #include @@ -34,15 +37,6 @@ llvm::cl::opt TargetCalculatorMode( cl::init(TargetCalculateBy::Default), cl::cat(ExecCat)); } // namespace klee -std::string Target::toString() const { - std::string repr = "Target: "; - repr += block->getAssemblyLocation(); - if (atReturn()) { - repr += " (at the end)"; - } - return repr; -} - void TargetCalculator::update(const ExecutionState &state) { switch (TargetCalculatorMode) { case TargetCalculateBy::Default: @@ -91,7 +85,7 @@ bool TargetCalculator::differenceIsEmpty( return diff.empty(); } -Target TargetCalculator::calculate(ExecutionState &state) { +ref TargetCalculator::calculate(ExecutionState &state) { BasicBlock *initialBlock = state.getInitPCBlock(); std::unordered_map &history = blocksHistory[initialBlock]; @@ -100,7 +94,7 @@ Target TargetCalculator::calculate(ExecutionState &state) { BasicBlock *bb = state.getPCBlock(); KFunction *kf = module.functionMap.at(bb->getParent()); KBlock *kb = kf->blockMap[bb]; - KBlock *nearestBlock = nullptr; + ref nearestBlock; unsigned int minDistance = UINT_MAX; unsigned int sfNum = 0; bool newCov = false; @@ -135,13 +129,13 @@ Target TargetCalculator::calculate(ExecutionState &state) { } else { newCov = true; } - nearestBlock = target; + nearestBlock = Target::create(target); minDistance = distance; } } if (nearestBlock) { - return Target(nearestBlock); + return nearestBlock; } if (sfi->caller) { @@ -149,5 +143,5 @@ Target TargetCalculator::calculate(ExecutionState &state) { } } - return Target(nearestBlock); + return nearestBlock; } diff --git a/lib/Core/Target.h b/lib/Core/TargetCalculator.h similarity index 66% rename from lib/Core/Target.h rename to lib/Core/TargetCalculator.h index bf6b564628..b63f3673c8 100644 --- a/lib/Core/Target.h +++ b/lib/Core/TargetCalculator.h @@ -1,4 +1,4 @@ -//===-- Target.h ------------------------------------------------*- C++ -*-===// +//===-- TargetCalculator.h --------------------------------------*- C++ -*-===// // // The KLEE Symbolic Virtual Machine // @@ -7,54 +7,35 @@ // //===----------------------------------------------------------------------===// -#ifndef KLEE_TARGET_H -#define KLEE_TARGET_H +#ifndef KLEE_TARGETCALCULATOR_H +#define KLEE_TARGETCALCULATOR_H +#include "klee/ADT/RNG.h" #include "klee/Module/KModule.h" +#include "klee/Module/Target.h" +#include "klee/Module/TargetHash.h" +#include "klee/System/Time.h" + #include "klee/Support/OptionCategories.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/raw_ostream.h" #include +#include +#include #include +#include namespace klee { class CodeGraphDistance; class ExecutionState; -class Executor; +struct TransitionHash; enum TargetCalculateBy { Default, Blocks, Transitions }; -struct Target { -private: - KBlock *block; - -public: - explicit Target(KBlock *_block) : block(_block) {} - - bool operator<(const Target &other) const { return block < other.block; } - - bool operator==(const Target &other) const { return block == other.block; } - - bool atReturn() const { return llvm::isa(block); } - - KBlock *getBlock() const { return block; } - - bool isNull() const { return block == nullptr; } - - explicit operator bool() const noexcept { return !isNull(); } - - std::string toString() const; -}; - typedef std::pair Transition; -struct TransitionHash { - std::size_t operator()(const Transition &p) const { - return reinterpret_cast(p.first) * 31 + - reinterpret_cast(p.second); - } -}; - class TargetCalculator { typedef std::unordered_set VisitedBlocks; typedef std::unordered_set VisitedTransitions; @@ -75,7 +56,7 @@ class TargetCalculator { void update(const ExecutionState &state); - Target calculate(ExecutionState &state); + ref calculate(ExecutionState &state); private: const KModule &module; @@ -94,4 +75,4 @@ class TargetCalculator { }; } // namespace klee -#endif /* KLEE_TARGET_H */ +#endif /* KLEE_TARGETCALCULATOR_H */ diff --git a/lib/Core/TargetedExecutionManager.cpp b/lib/Core/TargetedExecutionManager.cpp new file mode 100644 index 0000000000..3cf9cd1810 --- /dev/null +++ b/lib/Core/TargetedExecutionManager.cpp @@ -0,0 +1,539 @@ +//===-- TargetedExecutionManager.cpp --------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "TargetedExecutionManager.h" + +#include "ExecutionState.h" +#include "klee/Core/TerminationTypes.h" +#include "klee/Module/CodeGraphDistance.h" +#include "klee/Module/KInstruction.h" +#include "klee/Support/ErrorHandling.h" + +#include + +using namespace llvm; +using namespace klee; + +namespace klee { +cl::OptionCategory + TerminationCat("State and overall termination options", + "These options control termination of the overall KLEE " + "execution and of individual states."); + +/*** Termination criteria options ***/ + +cl::opt + MaxTime("max-time", + cl::desc("Halt execution after the specified duration. " + "Set to 0s to disable (default=0s)"), + cl::init("0s"), cl::cat(TerminationCat)); + +cl::list ExitOnErrorType( + "exit-on-error-type", + cl::desc( + "Stop execution after reaching a specified condition (default=false)"), + cl::values(clEnumValN(StateTerminationType::Abort, "Abort", + "The program crashed (reached abort()/klee_abort())"), + clEnumValN(StateTerminationType::Assert, "Assert", + "An assertion was hit"), + clEnumValN(StateTerminationType::BadVectorAccess, + "BadVectorAccess", "Vector accessed out of bounds"), + clEnumValN(StateTerminationType::Execution, "Execution", + "Trying to execute an unexpected instruction"), + clEnumValN(StateTerminationType::External, "External", + "External objects referenced"), + clEnumValN(StateTerminationType::Free, "Free", + "Freeing invalid memory"), + clEnumValN(StateTerminationType::Model, "Model", + "Memory model limit hit"), + clEnumValN(StateTerminationType::Overflow, "Overflow", + "An overflow occurred"), + clEnumValN(StateTerminationType::Ptr, "Ptr", "Pointer error"), + clEnumValN(StateTerminationType::UndefinedBehavior, + "UndefinedBehavior", "Undefined behavior detected"), + clEnumValN(StateTerminationType::ReadOnly, "ReadOnly", + "Write to read-only memory"), + clEnumValN(StateTerminationType::ReportError, "ReportError", + "klee_report_error called"), + clEnumValN(StateTerminationType::User, "User", + "Wrong klee_* functions invocation")), + cl::ZeroOrMore, cl::cat(TerminationCat)); + +cl::opt + MaxInstructions("max-instructions", + cl::desc("Stop execution after this many instructions. " + "Set to 0 to disable (default=0)"), + cl::init(0), cl::cat(TerminationCat)); + +cl::opt MaxSteppedInstructions( + "max-stepped-instructions", + cl::desc("Stop state execution after this many instructions. Set to 0 to " + "disable (default=0)"), + cl::init(0), cl::cat(TerminationCat)); + +cl::opt MaxForks( + "max-forks", + cl::desc("Only fork this many times. Set to -1 to disable (default=-1)"), + cl::init(~0u), cl::cat(TerminationCat)); + +cl::opt MaxDepth("max-depth", + cl::desc("Only allow this many symbolic branches. " + "Set to 0 to disable (default=0)"), + cl::init(0), cl::cat(TerminationCat)); + +cl::opt + MaxMemory("max-memory", + cl::desc("Refuse to fork when above this amount of " + "memory (in MB) (see -max-memory-inhibit) and terminate " + "states when additional 100MB allocated (default=2000)"), + cl::init(2000), cl::cat(TerminationCat)); + +cl::opt MaxMemoryInhibit("max-memory-inhibit", + cl::desc("Inhibit forking when above memory cap " + "(see -max-memory) (default=true)"), + cl::init(true), cl::cat(TerminationCat)); + +cl::opt RuntimeMaxStackFrames( + "max-stack-frames", + cl::desc("Terminate a state after this many stack frames. Set to 0 to " + "disable (default=8192)"), + cl::init(8192), cl::cat(TerminationCat)); + +cl::opt MaxStaticForkPct( + "max-static-fork-pct", cl::init(1.), + cl::desc("Maximum percentage spent by an instruction forking out of the " + "forking of all instructions (default=1.0 (always))"), + cl::cat(TerminationCat)); + +cl::opt MaxStaticSolvePct( + "max-static-solve-pct", cl::init(1.), + cl::desc("Maximum percentage of solving time that can be spent by a single " + "instruction over total solving time for all instructions " + "(default=1.0 (always))"), + cl::cat(TerminationCat)); + +cl::opt MaxStaticCPForkPct( + "max-static-cpfork-pct", cl::init(1.), + cl::desc("Maximum percentage spent by an instruction of a call path " + "forking out of the forking of all instructions in the call path " + "(default=1.0 (always))"), + cl::cat(TerminationCat)); + +cl::opt MaxStaticCPSolvePct( + "max-static-cpsolve-pct", cl::init(1.), + cl::desc("Maximum percentage of solving time that can be spent by a single " + "instruction of a call path over total solving time for all " + "instructions (default=1.0 (always))"), + cl::cat(TerminationCat)); + +cl::opt MaxStaticPctCheckDelay( + "max-static-pct-check-delay", + cl::desc("Number of forks after which the --max-static-*-pct checks are " + "enforced (default=1000)"), + cl::init(1000), cl::cat(TerminationCat)); + +cl::opt TimerInterval( + "timer-interval", + cl::desc( + "Minimum interval to check timers. " + "Affects -max-time, -istats-write-interval, -stats-write-interval, and " + "-uncovered-update-interval (default=1s)"), + cl::init("1s"), cl::cat(TerminationCat)); + +llvm::cl::opt CheckTraversability( + "check-traversability", cl::init(false), + cl::desc("Check error trace for traversability (default=false)")); + +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)")); +} // namespace klee + +void LocatedEventManager::prefetchFindFilename(const std::string &filename) { + auto it = filenameCacheMap.find(filename); + if (it != filenameCacheMap.end()) { + filenameCache = it->second.get(); + } else { + filenameCache = nullptr; + } +} + +bool LocatedEventManager::isInside(Location &loc, + const klee::FunctionInfo &fi) { + bool isInside = false; + if (filenameCache == nullptr) { + isInside = loc.isInside(fi); + auto filenameCachePtr = std::make_unique(); + filenameCache = filenameCachePtr.get(); + filenameCacheMap.insert( + std::make_pair(fi.file, std::move(filenameCachePtr))); + filenameCache->insert(std::make_pair(loc.filename, isInside)); + } else { + auto it = filenameCache->find(loc.filename); + if (it == filenameCache->end()) { + isInside = loc.isInside(fi); + filenameCache->insert(std::make_pair(loc.filename, isInside)); + } else { + isInside = it->second; + } + } + return isInside; +} + +TargetedHaltsOnTraces::TargetedHaltsOnTraces(ref &forest) { + auto leafs = forest->leafs(); + for (auto finalTargetSetPair : leafs) { + traceToHaltTypeToConfidence.emplace(finalTargetSetPair.first, + HaltTypeToConfidence()); + } +} + +void TargetedHaltsOnTraces::subtractConfidencesFrom( + TargetForest &forest, HaltExecution::Reason reason) { + auto leafs = forest.leafs(); + for (auto finalTargetSetPair : leafs) { + auto &haltTypeToConfidence = + traceToHaltTypeToConfidence.at(finalTargetSetPair.first); + auto confidence = finalTargetSetPair.second; + auto it = haltTypeToConfidence.find(reason); + if (it == haltTypeToConfidence.end()) { + haltTypeToConfidence.emplace(reason, confidence); + } else { + haltTypeToConfidence[reason] = it->second + confidence; + } + } +} + +void TargetedHaltsOnTraces::totalConfidenceAndTopContributor( + const HaltTypeToConfidence &haltTypeToConfidence, + confidence::ty *confidence, HaltExecution::Reason *reason) { + *confidence = confidence::MaxConfidence; + HaltExecution::Reason maxReason = HaltExecution::MaxTime; + confidence::ty maxConfidence = confidence::MinConfidence; + for (auto p : haltTypeToConfidence) { + auto r = p.first; + auto c = p.second; + if (c > maxConfidence) { + maxConfidence = c; + maxReason = r; + } + *confidence -= c; + } + *reason = maxReason; +} + +std::string +getAdviseWhatToIncreaseConfidenceRate(HaltExecution::Reason reason) { + std::string what = ""; + switch (reason) { + case HaltExecution::MaxSolverTime: + what = MaxCoreSolverTime.ArgStr.str(); + break; + case HaltExecution::MaxStackFrames: + what = RuntimeMaxStackFrames.ArgStr.str(); + break; + case HaltExecution::MaxTests: + what = "max-tests"; // TODO: taken from run_klee.cpp + break; + case HaltExecution::MaxInstructions: + what = MaxInstructions.ArgStr.str(); + break; + case HaltExecution::MaxSteppedInstructions: + what = MaxSteppedInstructions.ArgStr.str(); + break; + case HaltExecution::CovCheck: + what = "cov-check"; // TODO: taken from StatsTracker.cpp + break; + case HaltExecution::MaxDepth: + what = MaxDepth.ArgStr.str(); + break; + case HaltExecution::ErrorOnWhichShouldExit: // this should never be the case + case HaltExecution::ReachedTarget: // this should never be the case + case HaltExecution::NoMoreStates: // this should never be the case + case HaltExecution::Interrupt: // it is ok to advise to increase time if we + // were interrupted by user + case HaltExecution::MaxTime: +#ifndef ENABLE_KLEE_DEBUG + default: +#endif + what = MaxTime.ArgStr.str(); + break; +#ifdef ENABLE_KLEE_DEBUG + default: + what = std::to_string(reason); + break; +#endif + } + return what; +} + +void TargetedHaltsOnTraces::reportFalsePositives(bool canReachSomeTarget) { + confidence::ty confidence; + HaltExecution::Reason reason; + for (const auto &targetSetWithConfidences : traceToHaltTypeToConfidence) { + const auto &target = targetSetWithConfidences.first->getTargets().front(); + if (!target->shouldFailOnThisTarget()) + continue; + bool atLeastOneReported = false; + for (const auto &target : targetSetWithConfidences.first->getTargets()) { + if (target->isReported) { + atLeastOneReported = true; + break; + } + } + + if (atLeastOneReported) { + continue; + } + + totalConfidenceAndTopContributor(targetSetWithConfidences.second, + &confidence, &reason); + if (canReachSomeTarget && + confidence::isVeryConfident( + confidence)) { // We should not be so sure if there are states that + // can reach some targets + confidence = confidence::VeryConfident; + reason = HaltExecution::MaxTime; + } + reportFalsePositive(confidence, target->getErrors(), target->getId(), + getAdviseWhatToIncreaseConfidenceRate(reason)); + target->isReported = true; + } +} + +TargetedExecutionManager::LocationToBlocks +TargetedExecutionManager::prepareAllLocations(KModule *kmodule, + Locations &locations) const { + LocationToBlocks locToBlocks; + LocatedEventManager lem; + const auto &infos = kmodule->infos; + for (const auto &kfunc : kmodule->functions) { + const auto &fi = infos->getFunctionInfo(*kfunc->function); + lem.prefetchFindFilename(fi.file); + if (kmodule->origInfos.count(fi.file) == 0) { + continue; + } + const auto &origInstsInFile = kmodule->origInfos.at(fi.file); + for (auto it = locations.begin(); it != locations.end();) { + auto loc = *it; + if (locToBlocks.count(loc) != 0) { + ++it; + continue; + } + + if (!lem.isInside(*loc, fi)) { + ++it; + continue; + } + Blocks blocks = Blocks(); + for (const auto &kblock : kfunc->blocks) { + auto b = kblock.get(); + if (!loc->isInside(b, origInstsInFile)) { + continue; + } + blocks.insert(b); + } + + if (blocks.size() > 0) { + locToBlocks[loc] = blocks; + it = locations.erase(it); + } else { + ++it; + } + } + } + + return locToBlocks; +} + +TargetedExecutionManager::Locations +TargetedExecutionManager::collectAllLocations(const SarifReport &paths) const { + Locations locations; + for (const auto &res : paths.results) { + for (const auto &loc : res.locations) { + locations.insert(loc); + } + } + + return locations; +} + +bool TargetedExecutionManager::canReach(const ref &from, + const ref &to, + LocationToBlocks &locToBlocks) const { + for (auto fromBlock : locToBlocks[from]) { + for (auto toBlock : locToBlocks[to]) { + auto fromKf = fromBlock->parent; + auto toKf = toBlock->parent; + if (fromKf == toKf) { + if (fromBlock == toBlock) { + return true; + } + + const auto &blockDist = codeGraphDistance.getDistance(fromBlock); + if (blockDist.count(toBlock) != 0) { + return true; + } + } else { + const auto &funcDist = codeGraphDistance.getDistance(fromKf); + if (funcDist.count(toKf) != 0) { + return true; + } + + const auto &backwardFuncDist = + codeGraphDistance.getBackwardDistance(fromKf); + if (backwardFuncDist.count(toKf) != 0) { + return true; + } + } + } + } + + return false; +} + +bool TargetedExecutionManager::tryResolveLocations( + Result &result, LocationToBlocks &locToBlocks) const { + std::vector> resolvedLocations; + size_t index = 0; + for (const auto &location : result.locations) { + auto it = locToBlocks.find(location); + if (it != locToBlocks.end()) { + if (!resolvedLocations.empty() && CheckTraversability) { + if (!canReach(resolvedLocations.back(), location, locToBlocks)) { + klee_warning("Trace %u is untraversable! Can't reach location %s " + "from location %s, so skipping this trace.", + result.id, location->toString().c_str(), + resolvedLocations.back()->toString().c_str()); + return false; + } + } + resolvedLocations.push_back(location); + } else if (index == result.locations.size() - 1) { + klee_warning( + "Trace %u is malformed! %s at location %s, so skipping this trace.", + result.id, getErrorsString(result.errors).c_str(), + location->toString().c_str()); + return false; + } + ++index; + } + + result.locations = std::move(resolvedLocations); + + return true; +} + +KFunction *TargetedExecutionManager::tryResolveEntryFunction( + const Result &result, LocationToBlocks &locToBlocks) const { + assert(result.locations.size() > 0); + + auto resKf = (*locToBlocks[result.locations[0]].begin())->parent; + if (SmartResolveEntryFunction) { + for (size_t i = 1; i < result.locations.size(); ++i) { + const auto &funcDist = codeGraphDistance.getDistance(resKf); + auto curKf = (*locToBlocks[result.locations[i]].begin())->parent; + if (funcDist.count(curKf) == 0) { + const auto &curFuncDist = codeGraphDistance.getDistance(curKf); + if (curFuncDist.count(resKf) == 0) { + klee_warning("Trace %u is malformed! Can't resolve entry function, " + "so skipping this trace.", + result.id); + return nullptr; + } else { + resKf = curKf; + } + } + } + } + + return resKf; +} + +std::unordered_map> +TargetedExecutionManager::prepareTargets(KModule *kmodule, SarifReport paths) { + Locations locations = collectAllLocations(paths); + LocationToBlocks locToBlocks = prepareAllLocations(kmodule, locations); + + std::unordered_map> whitelists; + + for (auto &result : paths.results) { + bool isFullyResolved = tryResolveLocations(result, locToBlocks); + if (!isFullyResolved) { + broken_traces.insert(result.id); + continue; + } + + auto kf = tryResolveEntryFunction(result, locToBlocks); + if (!kf) { + broken_traces.insert(result.id); + continue; + } + + if (whitelists.count(kf) == 0) { + ref whitelist = new TargetForest(kf); + whitelists[kf] = whitelist; + } + whitelists[kf]->addTrace(result, locToBlocks); + } + + return whitelists; +} + +void TargetedExecutionManager::reportFalseNegative(ExecutionState &state, + ReachWithError error) { + klee_warning("100.00%% %s False Negative at: %s", getErrorString(error), + state.prevPC->getSourceLocation().c_str()); +} + +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())) + continue; + + /// The following code checks if target is a `call ...` instruction and we + /// failed somewhere *inside* call + auto possibleInstruction = state.prevPC; + int i = state.stack.size() - 1; + bool found = true; + + while (!target->isTheSameAsIn( + possibleInstruction)) { // TODO: target->getBlock() == + // possibleInstruction should also be checked, + // but more smartly + if (i <= 0) { + found = false; + break; + } + possibleInstruction = state.stack[i].caller; + i--; + } + if (!found) + continue; + + state.error = error; + atLeastOneReported = true; + assert(!target->isReported); + if (target->isThatError(ReachWithError::Reachable)) { + klee_warning("100.00%% %s Reachable at trace %u", getErrorString(error), + target->getId()); + } else { + klee_warning("100.00%% %s True Positive at trace %u", + getErrorString(error), target->getId()); + } + target->isReported = true; + reported_traces.insert(target->getId()); + } + return atLeastOneReported; +} diff --git a/lib/Core/TargetedExecutionManager.h b/lib/Core/TargetedExecutionManager.h new file mode 100644 index 0000000000..7488f5cc55 --- /dev/null +++ b/lib/Core/TargetedExecutionManager.h @@ -0,0 +1,142 @@ +//===-- TargetedExecutionManager.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 for targeted execution mode +// +//===----------------------------------------------------------------------===// + +#ifndef KLEE_TARGETEDEXECUTIONMANAGER_H +#define KLEE_TARGETEDEXECUTIONMANAGER_H + +#include "klee/Core/TargetedExecutionReporter.h" +#include "klee/Module/KModule.h" +#include "klee/Module/Target.h" +#include "klee/Module/TargetForest.h" + +#include + +namespace klee { +extern llvm::cl::OptionCategory TerminationCat; + +/*** Termination criteria options ***/ + +extern llvm::cl::opt MaxTime; + +extern llvm::cl::list ExitOnErrorType; + +extern llvm::cl::opt MaxInstructions; + +extern llvm::cl::opt MaxSteppedInstructions; + +extern llvm::cl::opt MaxForks; + +extern llvm::cl::opt MaxDepth; + +extern llvm::cl::opt MaxMemory; + +extern llvm::cl::opt MaxMemoryInhibit; + +extern llvm::cl::opt RuntimeMaxStackFrames; + +extern llvm::cl::opt MaxStaticForkPct; + +extern llvm::cl::opt MaxStaticSolvePct; + +extern llvm::cl::opt MaxStaticCPForkPct; + +extern llvm::cl::opt MaxStaticCPSolvePct; + +extern llvm::cl::opt MaxStaticPctCheckDelay; + +extern llvm::cl::opt TimerInterval; + +class CodeGraphDistance; + +class LocatedEventManager { + using FilenameCache = std::unordered_map; + std::unordered_map> + filenameCacheMap; + FilenameCache *filenameCache = nullptr; + +public: + LocatedEventManager() {} + + void prefetchFindFilename(const std::string &filename); + + bool isInside(Location &loc, const klee::FunctionInfo &fi); +}; + +class TargetedHaltsOnTraces { + using HaltTypeToConfidence = + std::unordered_map; + using TraceToHaltTypeToConfidence = + std::unordered_map, + HaltTypeToConfidence, + TargetForest::RefUnorderedTargetsSetHash, + TargetForest::RefUnorderedTargetsSetCmp>; + TraceToHaltTypeToConfidence traceToHaltTypeToConfidence; + + static void totalConfidenceAndTopContributor( + const HaltTypeToConfidence &haltTypeToConfidence, + confidence::ty *confidence, HaltExecution::Reason *reason); + +public: + explicit TargetedHaltsOnTraces(ref &forest); + + void subtractConfidencesFrom(TargetForest &forest, + HaltExecution::Reason reason); + + /* Report for targeted static analysis mode */ + void reportFalsePositives(bool canReachSomeTarget); +}; + +class TargetedExecutionManager { +private: + using Blocks = std::unordered_set; + using LocationToBlocks = std::unordered_map, Blocks, + RefLocationHash, RefLocationCmp>; + using Locations = + std::unordered_set, RefLocationHash, RefLocationCmp>; + + 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; + + bool tryResolveLocations(Result &result, LocationToBlocks &locToBlocks) const; + LocationToBlocks prepareAllLocations(KModule *kmodule, + Locations &locations) const; + Locations collectAllLocations(const SarifReport &paths) const; + + bool canReach(const ref &from, const ref &to, + LocationToBlocks &locToBlocks) const; + + KFunction *tryResolveEntryFunction(const Result &result, + LocationToBlocks &locToBlocks) const; + + CodeGraphDistance &codeGraphDistance; + +public: + explicit TargetedExecutionManager(CodeGraphDistance &codeGraphDistance_) + : codeGraphDistance(codeGraphDistance_) {} + std::unordered_map> + prepareTargets(KModule *kmodule, SarifReport paths); + + void reportFalseNegative(ExecutionState &state, ReachWithError error); + + // Return true if report is successful + bool reportTruePositive(ExecutionState &state, ReachWithError error); +}; + +} // namespace klee + +#endif /* KLEE_TARGETEDEXECUTIONMANAGER_H */ diff --git a/lib/Core/TargetedExecutionReporter.cpp b/lib/Core/TargetedExecutionReporter.cpp new file mode 100644 index 0000000000..e073c6b1c5 --- /dev/null +++ b/lib/Core/TargetedExecutionReporter.cpp @@ -0,0 +1,59 @@ +//===-- TargetedExecutionReporter.cpp--------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Core/TargetedExecutionReporter.h" +#include "klee/Support/ErrorHandling.h" + +#include + +using namespace klee; + +void klee::reportFalsePositive(confidence::ty confidence, + const std::unordered_set &errors, + unsigned id, std::string whatToIncrease) { + std::ostringstream out; + out << getErrorsString(errors) << " False Positive at trace " << id; + if (!confidence::isConfident(confidence)) { + out << ". Advice: " + << "increase --" << whatToIncrease << " command line parameter value"; + } + klee_warning("%s%% %s", confidence::toString(confidence).c_str(), + out.str().c_str()); +} + +bool confidence::isConfident(ty conf) { return conf > Confident; } + +bool confidence::isVeryConfident(ty conf) { return conf > VeryConfident; } + +template +std::string string_format( + const std::string &format, + Args... args) { // from https://stackoverflow.com/a/26221725/10547926 + int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + + 1; // Extra space for '\0' + if (size_s <= 0) + return ""; + auto size = static_cast(size_s); + std::unique_ptr buf(new char[size]); + std::snprintf(buf.get(), size, format.c_str(), args...); + return std::string(buf.get(), + buf.get() + size - 1); // We don't want the '\0' inside +} + +std::string confidence::toString(ty conf) { + return string_format("%.2f", conf); +} + +confidence::ty confidence::min(confidence::ty left, confidence::ty right) { + return fmin(left, right); +} + +bool confidence::isNormal(confidence::ty conf) { + return confidence::MinConfidence <= conf && conf <= confidence::MaxConfidence; +} diff --git a/lib/Core/TimingSolver.cpp b/lib/Core/TimingSolver.cpp index 1fbbdd5614..63ba715614 100644 --- a/lib/Core/TimingSolver.cpp +++ b/lib/Core/TimingSolver.cpp @@ -95,6 +95,26 @@ bool TimingSolver::tryGetUnique(const ConstraintSet &constraints, ref e, return true; } +Solver::PartialValidity TimingSolver::evaluate(const ConstraintSet &constraints, + ref expr, + SolverQueryMetaData &metaData) { + // Fast path, to avoid timer and OS overhead. + if (ConstantExpr *CE = dyn_cast(expr)) { + return CE->isTrue() ? Solver::MustBeTrue : Solver::MustBeFalse; + } + + TimerStatIncrementer timer(stats::solverTime); + + if (simplifyExprs) + expr = ConstraintManager::simplifyExpr(constraints, expr); + + auto validity = solver->evaluate(Query(constraints, expr)); + + metaData.queryCost += timer.delta(); + + return validity; +} + bool TimingSolver::mustBeTrue(const ConstraintSet &constraints, ref expr, bool &result, SolverQueryMetaData &metaData, bool produceValidityCore) { diff --git a/lib/Core/TimingSolver.h b/lib/Core/TimingSolver.h index 22e39f4868..1e56309847 100644 --- a/lib/Core/TimingSolver.h +++ b/lib/Core/TimingSolver.h @@ -62,6 +62,9 @@ class TimingSolver { bool tryGetUnique(const ConstraintSet &, ref, ref &result, SolverQueryMetaData &metaData); + Solver::PartialValidity evaluate(const ConstraintSet &, ref, + SolverQueryMetaData &metaData); + bool mustBeTrue(const ConstraintSet &, ref, bool &result, SolverQueryMetaData &metaData, bool produceValidityCore = false); diff --git a/lib/Core/UserSearcher.cpp b/lib/Core/UserSearcher.cpp index 407864b12c..0c9e450245 100644 --- a/lib/Core/UserSearcher.cpp +++ b/lib/Core/UserSearcher.cpp @@ -13,6 +13,7 @@ #include "MergeHandler.h" #include "Searcher.h" +#include "klee/Core/Interpreter.h" #include "klee/Support/ErrorHandling.h" #include "klee/Support/OptionCategories.h" @@ -64,15 +65,6 @@ cl::opt UseBatchingSearch( "(default=false)"), cl::init(false), cl::cat(SearchCat)); -cl::opt UseGuidedSearch( - "use-guided-search", - cl::desc("Takes place in two steps. First, all acyclic paths are executed, " - "then the execution is guided to sections of the program not yet " - "covered. These steps are repeated until all blocks of the " - "program are covered" - "(default=true)"), - cl::init(true), cl::cat(SearchCat)); - cl::opt BatchInstructions( "batch-instructions", cl::desc("Number of instructions to batch when using " @@ -120,7 +112,7 @@ bool klee::userSearcherRequiresMD2U() { } Searcher *getNewSearcher(Searcher::CoreSearchType type, RNG &rng, - PTree &processTree) { + PForest &processForest) { Searcher *searcher = nullptr; switch (type) { case Searcher::DFS: @@ -133,7 +125,7 @@ Searcher *getNewSearcher(Searcher::CoreSearchType type, RNG &rng, searcher = new RandomSearcher(rng); break; case Searcher::RandomPath: - searcher = new RandomPathSearcher(processTree, rng); + searcher = new RandomPathSearcher(processForest, rng); break; case Searcher::NURS_CovNew: searcher = @@ -169,7 +161,7 @@ Searcher *getNewSearcher(Searcher::CoreSearchType type, RNG &rng, Searcher *klee::constructUserSearcher(Executor &executor) { Searcher *searcher = - getNewSearcher(CoreSearch[0], executor.theRNG, *executor.processTree); + getNewSearcher(CoreSearch[0], executor.theRNG, *executor.processForest); if (CoreSearch.size() > 1) { std::vector s; @@ -177,7 +169,7 @@ Searcher *klee::constructUserSearcher(Executor &executor) { for (unsigned i = 1; i < CoreSearch.size(); i++) s.push_back(getNewSearcher(CoreSearch[i], executor.theRNG, - *executor.processTree)); + *executor.processForest)); searcher = new InterleavedSearcher(s); } @@ -198,9 +190,17 @@ Searcher *klee::constructUserSearcher(Executor &executor) { searcher = ms; } - if (UseGuidedSearch) { + if (executor.guidanceKind == Interpreter::GuidanceKind::CoverageGuidance) { searcher = new GuidedSearcher( searcher, *executor.codeGraphDistance.get(), *executor.targetCalculator, + executor.removedButReachableStates, executor.pausedStates, + MaxCycles - 1, executor.theRNG); + } + + if (executor.guidanceKind == Interpreter::GuidanceKind::ErrorGuidance) { + delete searcher; + searcher = new GuidedSearcher( + *executor.codeGraphDistance.get(), executor.removedButReachableStates, executor.pausedStates, MaxCycles - 1, executor.theRNG); } diff --git a/lib/Core/UserSearcher.h b/lib/Core/UserSearcher.h index 0931d24379..69d07d9cfd 100644 --- a/lib/Core/UserSearcher.h +++ b/lib/Core/UserSearcher.h @@ -10,6 +10,8 @@ #ifndef KLEE_USERSEARCHER_H #define KLEE_USERSEARCHER_H +#include "llvm/Support/CommandLine.h" + namespace klee { class Executor; class Searcher; diff --git a/lib/Expr/SourceBuilder.cpp b/lib/Expr/SourceBuilder.cpp index 63394f9bda..9095623c3e 100644 --- a/lib/Expr/SourceBuilder.cpp +++ b/lib/Expr/SourceBuilder.cpp @@ -8,8 +8,12 @@ ref SourceBuilder::constantSource = ref(new ConstantSource()); ref SourceBuilder::makeSymbolicSource = ref(new MakeSymbolicSource()); -ref SourceBuilder::lazyInitializationMakeSymbolicSource = +ref SourceBuilder::lazyInitializationSymbolicSource = ref(new LazyInitializationSymbolicSource()); +ref SourceBuilder::irreproducibleSource = + ref(new IrreproducibleSource()); +ref SourceBuilder::symbolicValueSource = + ref(new SymbolicValueSource()); ref SourceBuilder::constant() { return SourceBuilder::constantSource; @@ -32,6 +36,14 @@ ref SourceBuilder::symbolicSize() { return new SymbolicSizeSource(); } -ref SourceBuilder::lazyInitializationMakeSymbolic() { - return SourceBuilder::lazyInitializationMakeSymbolicSource; +ref SourceBuilder::lazyInitializationSymbolic() { + return SourceBuilder::lazyInitializationSymbolicSource; +} + +ref SourceBuilder::irreproducible() { + return SourceBuilder::irreproducibleSource; +} + +ref SourceBuilder::symbolicValue() { + return SourceBuilder::symbolicValueSource; } diff --git a/lib/Module/CMakeLists.txt b/lib/Module/CMakeLists.txt index e1dde40d26..06d5861701 100644 --- a/lib/Module/CMakeLists.txt +++ b/lib/Module/CMakeLists.txt @@ -24,7 +24,11 @@ set(KLEE_MODULE_COMPONENT_SRCS PhiCleaner.cpp RaiseAsm.cpp ReturnSplitter.cpp + SarifReport.cpp StreamWithLine.cpp + Target.cpp + TargetHash.cpp + TargetForest.cpp ) if (USE_WORKAROUND_LLVM_PR39177) diff --git a/lib/Module/CodeGraphDistance.cpp b/lib/Module/CodeGraphDistance.cpp index 0cc5dbcd18..9fb483355b 100644 --- a/lib/Module/CodeGraphDistance.cpp +++ b/lib/Module/CodeGraphDistance.cpp @@ -71,15 +71,16 @@ void CodeGraphDistance::calculateDistance(KFunction *kf) { while (!nodes.empty()) { KFunction *currKF = nodes.front(); for (auto &callBlock : currKF->kCallBlocks) { - if (!callBlock->calledFunction || - callBlock->calledFunction->isDeclaration()) { - continue; - } - KFunction *callKF = functionMap[callBlock->calledFunction]; - if (dist.count(callKF) == 0) { - dist[callKF] = dist[currKF] + 1; - sort.push_back({callKF, dist[currKF] + 1}); - nodes.push_back(callKF); + for (auto &calledFunction : callBlock->calledFunctions) { + if (!calledFunction || calledFunction->isDeclaration()) { + continue; + } + KFunction *callKF = functionMap[calledFunction]; + if (dist.count(callKF) == 0) { + dist[callKF] = dist[currKF] + 1; + sort.push_back({callKF, dist[currKF] + 1}); + nodes.push_back(callKF); + } } } nodes.pop_front(); diff --git a/lib/Module/InstructionInfoTable.cpp b/lib/Module/InstructionInfoTable.cpp index 31c9a7962b..321f97556c 100644 --- a/lib/Module/InstructionInfoTable.cpp +++ b/lib/Module/InstructionInfoTable.cpp @@ -153,7 +153,8 @@ class DebugInfoExtractor { }; InstructionInfoTable::InstructionInfoTable( - const llvm::Module &m, std::unique_ptr assemblyFS) { + const llvm::Module &m, std::unique_ptr assemblyFS, + bool withInstructions) { // Generate all debug instruction information DebugInfoExtractor DI(internedStrings, m, std::move(assemblyFS)); @@ -165,7 +166,12 @@ InstructionInfoTable::InstructionInfoTable( for (auto it = llvm::inst_begin(Func), ie = llvm::inst_end(Func); it != ie; ++it) { auto instr = &*it; - infos.emplace(instr, DI.getInstructionInfo(*instr, FR)); + auto instInfo = DI.getInstructionInfo(*instr, FR); + if (withInstructions) { + insts[instInfo->file][instInfo->line][instInfo->column].insert( + instr->getOpcode()); + } + infos.emplace(instr, std::move(instInfo)); } } @@ -199,3 +205,7 @@ InstructionInfoTable::getFunctionInfo(const llvm::Function &f) const { return *found->second.get(); } + +InstructionInfoTable::Instructions InstructionInfoTable::getInstructions() { + return std::move(insts); +} diff --git a/lib/Module/KModule.cpp b/lib/Module/KModule.cpp index 889b5e3a6a..912650a489 100644 --- a/lib/Module/KModule.cpp +++ b/lib/Module/KModule.cpp @@ -184,7 +184,7 @@ injectStaticConstructorsAndDestructors(Module *m, GlobalVariable *ctors = m->getNamedGlobal("llvm.global_ctors"); GlobalVariable *dtors = m->getNamedGlobal("llvm.global_dtors"); - if (!ctors && !dtors) + if ((!ctors && !dtors) || entryFunction.empty()) return; Function *mainFn = m->getFunction(entryFunction); @@ -273,6 +273,7 @@ void KModule::instrument(const Interpreter::ModuleOptions &opts) { pm.add(new IntrinsicCleanerPass(*targetData, opts.WithFPRuntime)); pm.run(*module); + withPosixRuntime = opts.WithPOSIXRuntime; } void KModule::optimiseAndPrepare( @@ -318,7 +319,8 @@ void KModule::optimiseAndPrepare( // going to be unresolved. We really need to handle the intrinsics // directly I think? legacy::PassManager pm3; - pm3.add(createCFGSimplificationPass()); + if (opts.Simplify) + pm3.add(createCFGSimplificationPass()); switch (SwitchType) { case eSwitchTypeInternal: break; @@ -402,7 +404,23 @@ void KModule::manifest(InterpreterHandler *ih, bool forceSourceOutput) { for (auto &kfp : functions) { for (auto &kcb : kfp.get()->kCallBlocks) { - callMap[kcb->calledFunction].insert(kfp.get()->function); + bool isInlineAsm = false; +#if LLVM_VERSION_CODE >= LLVM_VERSION(8, 0) + const CallBase &cs = cast(*kcb->kcallInstruction->inst); + if (isa(cs.getCalledOperand())) { +#else + const CallSite cs(kcb->kcallInstruction->inst); + if (isa(cs.getCalledValue())) { +#endif + isInlineAsm = true; + } + if (kcb->calledFunctions.empty() && !isInlineAsm) { + kcb->calledFunctions.insert(escapingFunctions.begin(), + escapingFunctions.end()); + } + for (auto &calledFunction : kcb->calledFunctions) { + callMap[calledFunction].insert(kfp.get()->function); + } } } @@ -423,7 +441,7 @@ void KModule::checkModule() { legacy::PassManager pm; if (!DontVerify) - pm.add(createVerifierPass()); + pm.add(createVerifierPass(false)); pm.add(operandTypeCheckPass); pm.run(*module); @@ -589,9 +607,13 @@ KFunction::KFunction(llvm::Function *_function, KModule *_km) Value *fp = cs.getCalledValue(); #endif Function *f = getTargetFunction(fp); - KCallBlock *ckb = - new KCallBlock(this, &*bbit, parent, instructionToRegisterMap, - registerToInstructionMap, f, &instructions[n]); + std::set calledFunctions; + if (f) { + calledFunctions.insert(f); + } + KCallBlock *ckb = new KCallBlock( + this, &*bbit, parent, instructionToRegisterMap, + registerToInstructionMap, calledFunctions, &instructions[n]); kCallBlocks.push_back(ckb); kb = ckb; } else if (SplitReturns && isa(lit)) { @@ -657,22 +679,27 @@ KCallBlock::KCallBlock( KFunction *_kfunction, llvm::BasicBlock *block, KModule *km, std::unordered_map &instructionToRegisterMap, std::unordered_map ®isterToInstructionMap, - llvm::Function *_calledFunction, KInstruction **instructionsKF) + std::set _calledFunctions, KInstruction **instructionsKF) : KBlock::KBlock(_kfunction, block, km, instructionToRegisterMap, registerToInstructionMap, instructionsKF), - kcallInstruction(this->instructions[0]), calledFunction(_calledFunction) { -} + kcallInstruction(this->instructions[0]), + calledFunctions(_calledFunctions) {} bool KCallBlock::intrinsic() const { - return calledFunction->getIntrinsicID() != llvm::Intrinsic::not_intrinsic; + return calledFunctions.size() == 1 && + (*calledFunctions.begin())->getIntrinsicID() != + llvm::Intrinsic::not_intrinsic; } bool KCallBlock::internal() const { - return parent->parent->functionMap[calledFunction] != nullptr; + return calledFunctions.size() == 1 && + parent->parent->functionMap[*calledFunctions.begin()] != nullptr; } KFunction *KCallBlock::getKFunction() const { - return parent->parent->functionMap[calledFunction]; + return calledFunctions.size() == 1 + ? parent->parent->functionMap[*calledFunctions.begin()] + : nullptr; } KReturnBlock::KReturnBlock( diff --git a/lib/Module/SarifReport.cpp b/lib/Module/SarifReport.cpp new file mode 100644 index 0000000000..0052f350a3 --- /dev/null +++ b/lib/Module/SarifReport.cpp @@ -0,0 +1,271 @@ +//===-- SarifReport.cpp----------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Module/SarifReport.h" + +#include "klee/Module/KInstruction.h" +#include "klee/Module/KModule.h" +#include "klee/Support/ErrorHandling.h" + +#include "llvm/IR/IntrinsicInst.h" + +using namespace llvm; + +namespace { +using namespace klee; + +bool isOSSeparator(char c) { return c == '/' || c == '\\'; } + +optional> +tryConvertLocationJson(const LocationJson &locationJson) { + const auto &physicalLocation = locationJson.physicalLocation; + if (!physicalLocation.has_value()) { + return nonstd::nullopt; + } + + const auto &artifactLocation = physicalLocation->artifactLocation; + if (!artifactLocation.has_value() || !artifactLocation->uri.has_value()) { + return nonstd::nullopt; + } + + const auto filename = std::move(*(artifactLocation->uri)); + + const auto ®ion = physicalLocation->region; + if (!region.has_value() || !region->startLine.has_value()) { + return nonstd::nullopt; + } + + return Location::create(std::move(filename), *(region->startLine), + region->endLine, region->startColumn, + region->endColumn); +} + +std::unordered_set +tryConvertRuleJson(const std::string &ruleId, const std::string &toolName, + const optional &errorMessage) { + if (toolName == "huawei") { + if ("NullDereference" == ruleId) { + return {ReachWithError::NullPointerException}; + } else if ("CheckAfterDeref" == ruleId) { + return {ReachWithError::NullCheckAfterDerefException}; + } else if ("DoubleFree" == ruleId) { + return {ReachWithError::DoubleFree}; + } else if ("UseAfterFree" == ruleId) { + return {ReachWithError::UseAfterFree}; + } else if ("Reached" == ruleId) { + return {ReachWithError::Reachable}; + } else { + return {}; + } + } else if (toolName == "clang") { + if ("core.NullDereference" == ruleId) { + return {ReachWithError::NullPointerException}; + } else if ("unix.Malloc" == ruleId) { + if (errorMessage.has_value()) { + if (errorMessage->text == "Attempt to free released memory") { + return {ReachWithError::DoubleFree}; + } else if (errorMessage->text == "Use of memory after it is freed") { + return {ReachWithError::UseAfterFree}; + } else { + return {}; + } + } else { + return {ReachWithError::UseAfterFree, ReachWithError::DoubleFree}; + } + } else { + return {}; + } + } else if (toolName == "CppCheck") { + if ("nullPointer" == ruleId || "ctunullpointer" == ruleId) { + return {ReachWithError::NullPointerException}; + } else if ("doubleFree" == ruleId) { + return {ReachWithError::DoubleFree}; + } else { + return {}; + } + } else if (toolName == "Infer") { + if ("NULL_DEREFERENCE" == ruleId || "NULLPTR_DEREFERENCE" == ruleId) { + return {ReachWithError::NullPointerException}; + } else if ("USE_AFTER_DELETE" == ruleId || "USE_AFTER_FREE" == ruleId) { + return {ReachWithError::UseAfterFree, ReachWithError::DoubleFree}; + } else { + return {}; + } + } else { + return {}; + } +} + +optional tryConvertResultJson(const ResultJson &resultJson, + const std::string &toolName, + unsigned id) { + std::unordered_set errors = {ReachWithError::None}; + if (!resultJson.ruleId.has_value()) { + errors = {ReachWithError::Reachable}; + } else { + errors = + tryConvertRuleJson(*resultJson.ruleId, toolName, resultJson.message); + if (errors.size() == 0) { + klee_warning("undefined error in %u result", id); + return nonstd::nullopt; + } + } + + std::vector> locations; + std::vector> metadatas; + + if (resultJson.codeFlows.size() > 0) { + assert(resultJson.codeFlows.size() == 1); + assert(resultJson.codeFlows[0].threadFlows.size() == 1); + + const auto &threadFlow = resultJson.codeFlows[0].threadFlows[0]; + for (auto &threadFlowLocation : threadFlow.locations) { + if (!threadFlowLocation.location.has_value()) { + return nonstd::nullopt; + } + + auto maybeLocation = tryConvertLocationJson(*threadFlowLocation.location); + if (maybeLocation.has_value()) { + locations.push_back(*maybeLocation); + metadatas.push_back(std::move(threadFlowLocation.metadata)); + } + } + } else { + assert(resultJson.locations.size() == 1); + auto maybeLocation = tryConvertLocationJson(resultJson.locations[0]); + if (maybeLocation.has_value()) { + locations.push_back(*maybeLocation); + } + } + + if (locations.empty()) { + return nonstd::nullopt; + } + + return Result{std::move(locations), std::move(metadatas), id, + std::move(errors)}; +} +} // anonymous namespace + +namespace klee { +const char *getErrorString(ReachWithError error) { + return ReachWithErrorNames[error]; +} + +std::string getErrorsString(const std::unordered_set &errors) { + if (errors.size() == 1) { + return getErrorString(*errors.begin()); + } + + std::string res = "("; + size_t index = 0; + for (auto err : errors) { + res += getErrorString(err); + if (index != errors.size() - 1) { + res += "|"; + } + index++; + } + res += ")"; + return res; +} + +SarifReport convertAndFilterSarifJson(const SarifReportJson &reportJson) { + SarifReport report; + + if (reportJson.runs.size() == 0) { + return report; + } + + assert(reportJson.runs.size() == 1); + + const RunJson &run = reportJson.runs[0]; + + unsigned id = 0; + + for (const auto &resultJson : run.results) { + auto maybeResult = + tryConvertResultJson(resultJson, run.tool.driver.name, ++id); + + if (maybeResult.has_value()) { + report.results.push_back(*maybeResult); + } + } + + return report; +} + +Location::EquivLocationHashSet Location::cachedLocations; +Location::LocationHashSet Location::locations; + +ref Location::create(std::string filename_, unsigned int startLine_, + optional endLine_, + optional startColumn_, + optional endColumn_) { + Location *loc = + new Location(filename_, startLine_, endLine_, startColumn_, endColumn_); + std::pair success = + cachedLocations.insert(loc); + if (success.second) { + // Cache miss + locations.insert(loc); + return loc; + } + // Cache hit + delete loc; + loc = *(success.first); + return loc; +} + +Location::~Location() { + if (locations.find(this) != locations.end()) { + locations.erase(this); + cachedLocations.erase(this); + } +} + +bool Location::isInside(const FunctionInfo &info) const { + size_t suffixSize = 0; + int m = info.file.size() - 1, n = filename.size() - 1; + for (; m >= 0 && n >= 0 && info.file[m] == filename[n]; m--, n--) { + suffixSize++; + if (isOSSeparator(filename[n])) + return true; + } + return suffixSize >= 3 && (n == -1 ? (m == -1 || isOSSeparator(info.file[m])) + : (m == -1 && isOSSeparator(filename[n]))); +} + +bool Location::isInside(KBlock *block, const Instructions &origInsts) const { + auto first = block->getFirstInstruction()->info; + auto last = block->getLastInstruction()->info; + if (!startColumn.has_value()) { + if (first->line > endLine) + return false; + return startLine <= last->line; // and `first <= line` from above + } else { + for (size_t i = 0; i < block->numInstructions; ++i) { + auto inst = block->instructions[i]->info; + auto opCode = block->instructions[i]->inst->getOpcode(); + if (!isa(block->instructions[i]->inst) && + inst->line <= endLine && inst->line >= startLine && + inst->column <= *endColumn && inst->column >= *startColumn && + origInsts.at(inst->line).at(inst->column).count(opCode) != 0) { + return true; + } + } + + return false; + } +} + +std::string Location::toString() const { + return filename + ":" + std::to_string(startLine); +} +} // namespace klee diff --git a/lib/Module/Target.cpp b/lib/Module/Target.cpp new file mode 100644 index 0000000000..0c72c8c221 --- /dev/null +++ b/lib/Module/Target.cpp @@ -0,0 +1,108 @@ +//===-- Target.cpp --------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Module/Target.h" +#include "klee/Module/TargetHash.h" + +#include "klee/Module/CodeGraphDistance.h" +#include "klee/Module/KInstruction.h" + +#include +#include +#include + +using namespace llvm; +using namespace klee; + +namespace klee { +llvm::cl::opt LocationAccuracy( + "location-accuracy", cl::init(false), + cl::desc("Check location with line and column accuracy (default=false)")); +} + +std::string Target::toString() const { + std::ostringstream repr; + repr << "Target " << getId() << ": "; + if (shouldFailOnThisTarget()) { + repr << "error in "; + } + repr << block->getAssemblyLocation(); + if (atReturn()) { + repr << " (at the end)"; + } + return repr.str(); +} + +Target::EquivTargetHashSet Target::cachedTargets; +Target::TargetHashSet Target::targets; + +ref Target::getFromCacheOrReturn(Target *target) { + std::pair success = + cachedTargets.insert(target); + if (success.second) { + // Cache miss + targets.insert(target); + return target; + } + // Cache hit + delete target; + target = *(success.first); + return target; +} + +ref Target::create(const std::unordered_set &_errors, + unsigned _id, optional _loc, + KBlock *_block) { + Target *target = new Target(_errors, _id, _loc, _block); + return getFromCacheOrReturn(target); +} + +ref Target::create(KBlock *_block) { + return create({ReachWithError::None}, 0, nonstd::nullopt, _block); +} + +bool Target::isTheSameAsIn(KInstruction *instr) const { + if (!loc.has_value()) { + return false; + } + const auto &errLoc = *loc; + return instr->info->line >= errLoc.startLine && + instr->info->line <= errLoc.endLine && + (!LocationAccuracy || !errLoc.startColumn.has_value() || + (instr->info->column >= *errLoc.startColumn && + instr->info->column <= *errLoc.endColumn)); +} + +int Target::compare(const Target &other) const { + if (block != other.block) { + return block < other.block ? -1 : 1; + } + if (errors != other.errors) { + return *errors.begin() < *other.errors.begin() ? -1 : 1; + } + if (id != other.id) { + return id < other.id ? -1 : 1; + } + return 0; +} + +bool Target::equals(const Target &other) const { return compare(other) == 0; } + +bool Target::operator<(const Target &other) const { + return compare(other) == -1; +} + +bool Target::operator==(const Target &other) const { return equals(other); } + +Target::~Target() { + if (targets.find(this) != targets.end()) { + cachedTargets.erase(this); + targets.erase(this); + } +} diff --git a/lib/Module/TargetForest.cpp b/lib/Module/TargetForest.cpp new file mode 100644 index 0000000000..d8c5b92d34 --- /dev/null +++ b/lib/Module/TargetForest.cpp @@ -0,0 +1,624 @@ +//===-- TargetForest.cpp --------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Module/TargetForest.h" + +#include "klee/Core/TargetedExecutionReporter.h" +#include "klee/Expr/Expr.h" +#include "klee/Module/KInstruction.h" +#include "klee/Module/KModule.h" +#include "klee/Module/TargetHash.h" + +using namespace klee; +using namespace llvm; + +TargetForest::UnorderedTargetsSet::EquivUnorderedTargetsSetHashSet + TargetForest::UnorderedTargetsSet::cachedVectors; +TargetForest::UnorderedTargetsSet::UnorderedTargetsSetHashSet + TargetForest::UnorderedTargetsSet::vectors; + +TargetForest::UnorderedTargetsSet::UnorderedTargetsSet( + const ref &target) { + targetsVec.push_back(target); + sortAndComputeHash(); +} + +TargetForest::UnorderedTargetsSet::UnorderedTargetsSet( + const TargetsSet &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); + } + hashValue = seed; +} + +ref +TargetForest::UnorderedTargetsSet::create(const ref &target) { + UnorderedTargetsSet *vec = new UnorderedTargetsSet(target); + return create(vec); +} + +ref +TargetForest::UnorderedTargetsSet::create(const TargetsSet &targets) { + UnorderedTargetsSet *vec = new UnorderedTargetsSet(targets); + return create(vec); +} + +ref +TargetForest::UnorderedTargetsSet::create(UnorderedTargetsSet *vec) { + std::pair success = + cachedVectors.insert(vec); + if (success.second) { + // Cache miss + vectors.insert(vec); + return vec; + } + // Cache hit + delete vec; + vec = *(success.first); + return vec; +} + +TargetForest::UnorderedTargetsSet::~UnorderedTargetsSet() { + if (vectors.find(this) != vectors.end()) { + vectors.erase(this); + cachedVectors.erase(this); + } +} + +void TargetForest::Layer::addTrace( + const Result &result, + const std::unordered_map, std::unordered_set, + RefLocationHash, RefLocationCmp> &locToBlocks) { + auto forest = this; + for (size_t i = 0; i < result.locations.size(); ++i) { + const auto &loc = result.locations[i]; + auto it = locToBlocks.find(loc); + assert(it != locToBlocks.end()); + TargetsSet targets; + for (auto block : it->second) { + ref target = nullptr; + if (i == result.locations.size() - 1) { + target = Target::create(result.errors, result.id, + ErrorLocation{loc->startLine, loc->endLine, + loc->startColumn, loc->endColumn}, + block); + } else { + target = Target::create(block); + } + targets.insert(target); + } + + ref targetsVec = UnorderedTargetsSet::create(targets); + if (forest->forest.count(targetsVec) == 0) { + ref next = new TargetForest::Layer(); + forest->insert(targetsVec, next); + } + + for (auto &target : targetsVec->getTargets()) { + forest->insertTargetsToVec(target, targetsVec); + } + + forest = forest->forest[targetsVec].get(); + } +} + +void TargetForest::Layer::propagateConfidenceToChildren() { + auto parentConfidence = getConfidence(); + for (auto &kv : forest) { + kv.second->confidence = kv.second->getConfidence(parentConfidence); + } +} + +void TargetForest::Layer::unionWith(TargetForest::Layer *other) { + if (other->forest.empty()) + return; + other->propagateConfidenceToChildren(); + for (const auto &kv : other->forest) { + auto it = forest.find(kv.first); + if (it == forest.end()) { + forest.insert(std::make_pair(kv.first, kv.second)); + continue; + } + auto layer = new Layer(it->second); + layer->unionWith(kv.second.get()); + it->second = layer; + } + + for (const auto &kv : other->targetsToVector) { + auto it = targetsToVector.find(kv.first); + if (it == targetsToVector.end()) { + targetsToVector.insert(std::make_pair(kv.first, kv.second)); + continue; + } + it->second.insert(kv.second.begin(), kv.second.end()); + } +} + +void TargetForest::Layer::block(ref target) { + if (empty()) + return; + removeTarget(target); + for (InternalLayer::iterator itf = forest.begin(); itf != forest.end();) { + ref layer = itf->second->blockLeaf(target); + itf->second = layer; + if (itf->second->empty()) { + for (auto &itfTarget : itf->first->getTargets()) { + auto it = targetsToVector.find(itfTarget); + if (it != targetsToVector.end()) { + it->second.erase(itf->first); + if (it->second.empty()) { + targetsToVector.erase(it); + } + } + } + itf = forest.erase(itf); + } else { + ++itf; + } + } +} + +void TargetForest::Layer::removeTarget(ref target) { + auto it = targetsToVector.find(target); + + if (it == targetsToVector.end()) { + return; + } + + auto targetsVectors = std::move(it->second); + + targetsToVector.erase(it); + + for (auto &targetsVec : targetsVectors) { + bool shouldDelete = true; + + for (auto &localTarget : targetsVec->getTargets()) { + if (targetsToVector.count(localTarget) != 0) { + shouldDelete = false; + } + } + + if (shouldDelete) { + forest.erase(targetsVec); + } + } +} + +bool TargetForest::Layer::deepFind(ref target) const { + if (empty()) + return false; + auto res = targetsToVector.find(target); + if (res != targetsToVector.end()) { + return true; + } + for (auto &f : forest) { + if (f.second->deepFind(target)) + return true; + } + return false; +} + +bool TargetForest::Layer::deepFindIn(ref child, + ref target) const { + auto res = targetsToVector.find(child); + if (res == targetsToVector.end()) { + return false; + } + + if (child == target) { + return true; + } + for (auto &targetsVec : res->second) { + auto it = forest.find(targetsVec); + assert(it != forest.end()); + if (it->second->deepFind(target)) { + return true; + } + } + + return false; +} + +TargetForest::Layer *TargetForest::Layer::removeChild(ref child) const { + auto result = new Layer(this); + result->removeTarget(child); + return result; +} + +TargetForest::Layer * +TargetForest::Layer::removeChild(ref child) const { + auto result = new Layer(this); + result->forest.erase(child); + for (auto &target : child->getTargets()) { + auto it = result->targetsToVector.find(target); + if (it == result->targetsToVector.end()) { + continue; + } + + it->second.erase(child); + if (it->second.empty()) { + result->targetsToVector.erase(it); + } + } + return result; +} + +TargetForest::Layer *TargetForest::Layer::addChild(ref child) const { + auto targetsVec = UnorderedTargetsSet::create(child); + auto result = new Layer(this); + if (forest.count(targetsVec) != 0) { + return result; + } + result->forest.insert({targetsVec, new Layer()}); + + result->targetsToVector[child].insert(targetsVec); + return result; +} + +TargetForest::Layer * +TargetForest::Layer::blockLeafInChild(ref child, + ref leaf) const { + auto subtree = forest.find(child); + assert(subtree != forest.end()); + if (subtree->second->forest.empty()) { + auto targetsVectors = targetsToVector.find(leaf); + if (targetsVectors == targetsToVector.end() || + targetsVectors->second.count(subtree->first) == 0) { + return new Layer(this); + } else { + return removeChild(leaf); + } + } + + ref sublayer = new Layer(subtree->second); + sublayer->block(leaf); + if (sublayer->empty()) { + return removeChild(child); + } else { + InternalLayer subforest; + subforest[child] = sublayer; + TargetsToVector subTargetsToVector; + for (auto &target : child->getTargets()) { + subTargetsToVector[target].insert(child); + } + sublayer = new Layer(subforest, subTargetsToVector, confidence); + auto result = replaceChildWith(child, sublayer.get()); + return result; + } +} + +TargetForest::Layer * +TargetForest::Layer::blockLeafInChild(ref child, + ref leaf) const { + TargetForest::Layer *res = nullptr; + auto it = targetsToVector.find(child); + if (it == targetsToVector.end()) { + return res; + } + for (auto &targetsVec : it->second) { + if (res) { + res = res->blockLeafInChild(targetsVec, leaf); + } else { + res = blockLeafInChild(targetsVec, leaf); + } + } + return res; +} + +TargetForest::Layer *TargetForest::Layer::blockLeaf(ref leaf) const { + auto result = new Layer(this); + if (forest.empty()) { + return result; + } + + for (auto &layer : forest) { + result = ref(result)->blockLeafInChild(layer.first, leaf); + } + return result; +} + +TargetForest::Layer *TargetForest::Layer::replaceChildWith( + ref const child, + TargetForest::Layer *other) const { + auto result = removeChild(child); + result->unionWith(other); + return result; +} + +TargetForest::Layer *TargetForest::Layer::replaceChildWith( + ref child, + const std::unordered_set, + RefUnorderedTargetsSetHash, + RefUnorderedTargetsSetCmp> &other) const { + std::vector layers; + for (auto &targetsVec : other) { + auto it = forest.find(targetsVec); + assert(it != forest.end()); + layers.push_back(it->second.get()); + } + auto result = new Layer(this); + for (auto &targetsVec : other) { + for (auto &target : targetsVec->getTargets()) { + auto it = result->targetsToVector.find(target); + if (it != result->targetsToVector.end()) { + it->second.erase(targetsVec); + if (it->second.empty()) { + result->targetsToVector.erase(it); + } + } + } + result->forest.erase(targetsVec); + } + for (auto layer : layers) { + result->unionWith(layer); + } + 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"; + for (const auto &kv : forest) { + llvm::errs() << kv.first->toString() << "\n"; + } + llvm::errs() << "-----------------------\n"; + if (!forest.empty()) { + for (const auto &kv : forest) { + kv.second->dump(n + 1); + } + llvm::errs() << "++++++++++++++++++++++\n"; + } +} + +TargetForest::History::EquivTargetsHistoryHashSet + TargetForest::History::cachedHistories; +TargetForest::History::TargetsHistoryHashSet TargetForest::History::histories; + +ref +TargetForest::History::create(ref _target, + ref _visitedTargets) { + History *history = new History(_target, _visitedTargets); + + std::pair success = + cachedHistories.insert(history); + if (success.second) { + // Cache miss + histories.insert(history); + return history; + } + // Cache hit + delete history; + history = *(success.first); + return history; +} + +ref TargetForest::History::create(ref _target) { + return create(_target, nullptr); +} + +ref TargetForest::History::create() { + return create(nullptr); +} + +int TargetForest::History::compare(const History &h) const { + if (this == &h) + return 0; + + 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) + 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; +} + +void TargetForest::History::dump() const { + if (target) { + llvm::errs() << target->toString() << "\n"; + } else { + llvm::errs() << "end.\n"; + assert(!visitedTargets); + return; + } + if (visitedTargets) + visitedTargets->dump(); +} + +TargetForest::History::~History() { + if (histories.find(this) != histories.end()) { + histories.erase(this); + cachedHistories.erase(this); + } +} + +void TargetForest::stepTo(ref loc) { + if (forest->empty()) + return; + auto res = forest->find(loc); + if (res == forest->end()) { + return; + } + if (loc->shouldFailOnThisTarget()) { + forest = new Layer(); + } else { + history = history->add(loc); + forest = forest->replaceChildWith(loc, res->second); + loc->isReported = true; + } + if (forest->empty() && !loc->shouldFailOnThisTarget()) { + history = History::create(); + } +} + +void TargetForest::add(ref target) { + if (forest->find(target) != forest->end()) { + return; + } + forest = forest->addChild(target); +} + +void TargetForest::remove(ref target) { + if (forest->find(target) == forest->end()) { + return; + } + forest = forest->removeChild(target); +} + +void TargetForest::blockIn(ref subtarget, ref target) { + if (!forest->deepFindIn(subtarget, target)) { + return; + } + forest = forest->blockLeafInChild(subtarget, target); +} + +void TargetForest::dump() const { + llvm::errs() << "History:\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 { + for (const auto &targetAndForest : forest) { + auto targetsVec = targetAndForest.first; + auto layer = targetAndForest.second; + auto confidence = layer->getConfidence(parentConfidence); + if (layer->empty()) { + leafs.push_back(std::make_pair(targetsVec, confidence)); + } else { + layer->addLeafs(leafs, confidence); + } + } +} + +std::vector, confidence::ty>> +TargetForest::leafs() const { + std::vector, confidence::ty>> leafs; + forest->addLeafs(leafs, forest->getConfidence()); + return leafs; +} + +ref TargetForest::deepCopy() { + return new TargetForest(forest->deepCopy(), entryFunction); +} + +ref TargetForest::Layer::deepCopy() { + auto copyForest = new TargetForest::Layer(this); + for (auto &targetAndForest : forest) { + auto targetsVec = targetAndForest.first; + auto layer = targetAndForest.second; + copyForest->forest[targetsVec] = layer->deepCopy(); + } + return copyForest; +} + +void TargetForest::Layer::divideConfidenceBy(unsigned factor) { + for (auto &targetAndForest : forest) { + auto layer = targetAndForest.second; + layer->confidence /= factor; + } +} + +TargetForest::Layer *TargetForest::Layer::divideConfidenceBy( + const TargetForest::TargetToStateSetMap &reachableStatesOfTarget) { + if (forest.empty() || reachableStatesOfTarget.empty()) + return this; + auto result = new Layer(this); + for (auto &targetAndForest : forest) { + auto targetsVec = targetAndForest.first; + auto layer = targetAndForest.second; + std::unordered_set reachableStatesForTargetsVec; + for (const auto &target : targetsVec->getTargets()) { + auto it = reachableStatesOfTarget.find(target); + if (it == reachableStatesOfTarget.end()) { + continue; + } + + for (auto state : it->second) { + reachableStatesForTargetsVec.insert(state); + } + } + + size_t count = reachableStatesForTargetsVec.size(); + if (count) { + if (count == 1) + continue; + auto next = new Layer(layer); + result->forest[targetsVec] = next; + next->confidence /= count; + } else + result->forest[targetsVec] = + layer->divideConfidenceBy(reachableStatesOfTarget); + } + 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 new file mode 100644 index 0000000000..b187311b83 --- /dev/null +++ b/lib/Module/TargetHash.cpp @@ -0,0 +1,47 @@ +//===-- TargetHash.c-------------------------------------------------------===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Module/TargetHash.h" + +#include "klee/Module/Target.h" + +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 { + return t->hash(); +} + +bool RefTargetCmp::operator()(const ref &a, + const ref &b) const { + return a.get() == b.get(); +} + +std::size_t TransitionHash::operator()(const Transition &p) const { + return reinterpret_cast(p.first) * 31 + + reinterpret_cast(p.second); +} diff --git a/lib/Runner/CMakeLists.txt b/lib/Runner/CMakeLists.txt index d140d85b11..b28503013d 100644 --- a/lib/Runner/CMakeLists.txt +++ b/lib/Runner/CMakeLists.txt @@ -1,4 +1,5 @@ klee_add_component(kleeRunner + parse_static_analysis_json.cpp run_klee.cpp ) diff --git a/lib/Runner/parse_static_analysis_json.cpp b/lib/Runner/parse_static_analysis_json.cpp new file mode 100644 index 0000000000..38441be483 --- /dev/null +++ b/lib/Runner/parse_static_analysis_json.cpp @@ -0,0 +1,32 @@ +#include "klee/Runner/run_klee.h" + +/* -*- mode: c++; c-basic-offset: 2; -*- */ + +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The KLEE Symbolic Virtual Machine +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "klee/Module/SarifReport.h" +#include "klee/Support/ErrorHandling.h" + +#include + +using json = nlohmann::json; + +using namespace llvm; +using namespace klee; + +SarifReport parseInputPathTree(const std::string &inputPathTreePath) { + std::ifstream file(inputPathTreePath); + if (file.fail()) + klee_error("Cannot read file %s", inputPathTreePath.c_str()); + json reportJson = json::parse(file); + SarifReportJson sarifJson = reportJson.get(); + klee_warning("we are parsing file %s", inputPathTreePath.c_str()); + return klee::convertAndFilterSarifJson(sarifJson); +} diff --git a/lib/Runner/run_klee.cpp b/lib/Runner/run_klee.cpp index 74f04438dc..905e19630b 100644 --- a/lib/Runner/run_klee.cpp +++ b/lib/Runner/run_klee.cpp @@ -15,6 +15,9 @@ #include "klee/ADT/TreeStream.h" #include "klee/Config/Version.h" #include "klee/Core/Interpreter.h" +#include "klee/Core/TargetedExecutionReporter.h" +#include "klee/Module/SarifReport.h" +#include "klee/Module/TargetForest.h" #include "klee/Solver/SolverCmdLine.h" #include "klee/Statistics/Statistics.h" #include "klee/Support/Debug.h" @@ -58,6 +61,8 @@ #include #include +using json = nlohmann::json; + using namespace llvm; using namespace klee; @@ -152,6 +157,12 @@ cl::opt ProcessNumber("process-number", "lie in [1, 50] (default = 1)."), cl::init(1), cl::cat(StartCat)); +cl::opt + AnalysisReproduce("analysis-reproduce", + cl::desc("Path of JSON file containing static analysis " + "paths to be reproduced"), + cl::init(""), cl::cat(StartCat)); + cl::opt RunInDir("run-in-dir", cl::desc("Change to the given directory before starting execution " @@ -172,12 +183,34 @@ cl::opt OptimizeModule( "optimize", cl::desc("Optimize the code before execution (default=false)."), cl::init(false), cl::cat(StartCat)); +cl::opt SimplifyModule( + "simplify", cl::desc("Simplify the code before execution (default=true)."), + cl::init(true), cl::cat(StartCat)); + cl::opt WarnAllExternals( "warn-all-external-symbols", cl::desc( "Issue a warning on startup for all external symbols (default=false)."), cl::cat(StartCat)); +cl::opt UseGuidedSearch( + "use-guided-search", + cl::values(clEnumValN(Interpreter::GuidanceKind::NoGuidance, "none", + "Use basic klee symbolic execution"), + clEnumValN(Interpreter::GuidanceKind::CoverageGuidance, + "coverage", + "Takes place in two steps. First, all acyclic " + "paths are executed, " + "then the execution is guided to sections of the " + "program not yet covered. " + "These steps are repeated until all blocks of the " + "program are covered"), + clEnumValN(Interpreter::GuidanceKind::ErrorGuidance, "error", + "The execution is guided to sections of the " + "program errors not yet covered")), + cl::init(Interpreter::GuidanceKind::CoverageGuidance), + cl::desc("Kind of execution mode"), cl::cat(StartCat)); + /*** Linking options ***/ cl::OptionCategory LinkCat("Linking options", @@ -611,7 +644,7 @@ void KleeHandler::processTestCase(const ExecutionState &state, } if (m_numGeneratedTests == MaxTests) - m_interpreter->setHaltExecution(true); + m_interpreter->setHaltExecution(HaltExecution::MaxTests); if (WriteTestInfo) { time::Span elapsed_time(time::getWallTime() - start_time); @@ -987,7 +1020,7 @@ void externalsAndGlobalsCheck(const llvm::Module *m) { if (ext.compare(0, 5, "llvm.") != 0) { // not an LLVM reserved name if (unsafe.count(ext)) { foundUnsafe.insert(*it); - } else { + } else if (WarnAllExternals) { klee_warning("undefined reference to %s: %s", it->second ? "variable" : "function", ext.c_str()); } @@ -1005,11 +1038,14 @@ void externalsAndGlobalsCheck(const llvm::Module *m) { } static Interpreter *theInterpreter = 0; +static nonstd::optional paths = nonstd::nullopt; -static bool interrupted = false; +static std::atomic_bool interrupted{false}; // Pulled out so it can be easily called from a debugger. -extern "C" void halt_execution() { theInterpreter->setHaltExecution(true); } +extern "C" void halt_execution() { + theInterpreter->setHaltExecution(HaltExecution::Interrupt); +} extern "C" void stop_forking() { theInterpreter->setInhibitForking(true); } @@ -1019,6 +1055,12 @@ static void interrupt_handle() { halt_execution(); sys::SetInterruptFunction(interrupt_handle); } else { + if (paths && (!theInterpreter || !theInterpreter->hasTargetForest())) { + for (const auto &res : paths->results) { + reportFalsePositive(confidence::MinConfidence, res.errors, res.id, + "max-time"); + } + } llvm::errs() << "KLEE: ctrl-c detected, exiting.\n"; exit(1); } @@ -1182,12 +1224,20 @@ linkWithUclibc(StringRef libDir, std::string opt_suffix, } #endif +static nonstd::optional parseStaticAnalysisInput() { + if (AnalysisReproduce != "") + return parseInputPathTree(AnalysisReproduce); + return nonstd::nullopt; +} + static int run_klee_on_function(int pArgc, char **pArgv, char **pEnvp, - KleeHandler *handler, Interpreter *interpreter, + std::unique_ptr &handler, + std::unique_ptr &interpreter, llvm::Module *finalModule, std::vector &replayPath) { Function *mainFn = finalModule->getFunction(EntryPoint); - if (!mainFn) { + if ((UseGuidedSearch != Interpreter::GuidanceKind::ErrorGuidance) && + !mainFn) { // in error guided mode we do not need main function klee_error("Entry function '%s' not found in module.", EntryPoint.c_str()); } @@ -1295,6 +1345,7 @@ static int run_klee_on_function(int pArgc, char **pArgv, char **pEnvp, sys::StrError(errno).c_str()); } } + interpreter->runFunctionAsMain(mainFn, pArgc, pArgv, pEnvp); while (!seeds.empty()) { @@ -1478,6 +1529,30 @@ int run_klee(int argc, char **argv, char **envp) { } llvm::Module *mainModule = loadedUserModules.front().get(); + std::unique_ptr origInfos; + std::unique_ptr assemblyFS; + + if (UseGuidedSearch == Interpreter::GuidanceKind::ErrorGuidance) { + std::vector args; + origInfos = std::make_unique( + *mainModule, std::move(assemblyFS), true); + args.push_back(llvm::Type::getInt32Ty(ctx)); // argc + args.push_back(llvm::PointerType::get( + Type::getInt8PtrTy(ctx), + mainModule->getDataLayout().getAllocaAddrSpace())); // argv + args.push_back(llvm::PointerType::get( + Type::getInt8PtrTy(ctx), + mainModule->getDataLayout().getAllocaAddrSpace())); // envp + std::string stubEntryPoint = "__klee_entry_point_main"; + Function *stub = Function::Create( + llvm::FunctionType::get(llvm::Type::getInt32Ty(ctx), args, false), + GlobalVariable::ExternalLinkage, stubEntryPoint, mainModule); + BasicBlock *bb = BasicBlock::Create(ctx, "entry", stub); + + llvm::IRBuilder<> Builder(bb); + Builder.CreateRet(ConstantInt::get(Type::getInt32Ty(ctx), 0)); + EntryPoint = stubEntryPoint; + } std::vector mainModuleFunctions; for (auto &Function : *mainModule) { @@ -1499,18 +1574,25 @@ int run_klee(int argc, char **argv, char **envp) { if (module_triple.find("i686") != std::string::npos || module_triple.find("i586") != std::string::npos || module_triple.find("i486") != std::string::npos || - module_triple.find("i386") != std::string::npos) + module_triple.find("i386") != std::string::npos || + module_triple.find("arm") != std::string::npos) opt_suffix = "32"; // Add additional user-selected suffix opt_suffix += "_" + RuntimeBuild.getValue(); + if (UseGuidedSearch == Interpreter::GuidanceKind::ErrorGuidance) { + SimplifyModule = false; + } + std::string LibraryDir = KleeHandler::getRunTimeLibraryPath(argv[0]); Interpreter::ModuleOptions Opts(LibraryDir.c_str(), EntryPoint, opt_suffix, /*Optimize=*/OptimizeModule, + /*Simplify*/ SimplifyModule, /*CheckDivZero=*/CheckDivZero, /*CheckOvershift=*/CheckOvershift, - /*WithFPRuntime=*/WithFPRuntime); + /*WithFPRuntime=*/WithFPRuntime, + /*WithPOSIXRuntime=*/WithPOSIXRuntime); if (WithPOSIXRuntime) { SmallString<128> Path(Opts.LibraryDir); @@ -1657,13 +1739,21 @@ int run_klee(int argc, char **argv, char **envp) { KleeHandler::loadPathFile(ReplayPathFile, replayPath); } - Interpreter::InterpreterOptions IOpts; + std::unique_ptr handler = + std::make_unique(pArgc, pArgv); + + if (UseGuidedSearch == Interpreter::GuidanceKind::ErrorGuidance) { + paths = parseStaticAnalysisInput(); + } + + Interpreter::InterpreterOptions IOpts(paths); IOpts.MakeConcreteSymbolic = MakeConcreteSymbolic; - KleeHandler *handler = new KleeHandler(pArgc, pArgv); - Interpreter *interpreter = theInterpreter = - Interpreter::create(ctx, IOpts, handler); + IOpts.Guidance = UseGuidedSearch; + std::unique_ptr interpreter( + Interpreter::create(ctx, IOpts, handler.get())); + theInterpreter = interpreter.get(); assert(interpreter); - handler->setInterpreter(interpreter); + handler->setInterpreter(interpreter.get()); for (int i = 0; i < argc; i++) { handler->getInfoStream() << argv[i] << (i + 1 < argc ? " " : "\n"); @@ -1673,8 +1763,9 @@ int run_klee(int argc, char **argv, char **envp) { // Get the desired main function. klee_main initializes uClibc // locale and other data and then calls main. - auto finalModule = interpreter->setModule( - loadedUserModules, loadedLibsModules, Opts, mainModuleFunctions); + auto finalModule = + interpreter->setModule(loadedUserModules, loadedLibsModules, Opts, + mainModuleFunctions, std::move(origInfos)); Function *mainFn = finalModule->getFunction(EntryPoint); if (!mainFn) { klee_error("Entry function '%s' not found in module.", EntryPoint.c_str()); @@ -1758,13 +1849,13 @@ int run_klee(int argc, char **argv, char **envp) { replayPath); } + paths.reset(); + // Free all the args. for (unsigned i = 0; i < InputArgv.size() + 1; i++) delete[] pArgv[i]; delete[] pArgv; - delete interpreter; - uint64_t queries = *theStatisticManager->getStatisticByName("Queries"); uint64_t queriesValid = *theStatisticManager->getStatisticByName("QueriesValid"); @@ -1817,8 +1908,5 @@ int run_klee(int argc, char **argv, char **envp) { llvm::errs().resetColor(); handler->getInfoStream() << stats.str(); - - delete handler; - return 0; } diff --git a/lib/Solver/ConcretizationManager.cpp b/lib/Solver/ConcretizationManager.cpp index de800700d9..323b54169c 100644 --- a/lib/Solver/ConcretizationManager.cpp +++ b/lib/Solver/ConcretizationManager.cpp @@ -9,6 +9,7 @@ using namespace klee; Assignment ConcretizationManager::get(const ConstraintSet &set, ref query) { + query = Expr::createIsZero(Expr::createIsZero(query)); if (simplifyExprs) { query = ConstraintManager::simplifyExpr(set, query); } @@ -25,6 +26,7 @@ Assignment ConcretizationManager::get(const ConstraintSet &set, bool ConcretizationManager::contains(const ConstraintSet &set, ref query) { + query = Expr::createIsZero(Expr::createIsZero(query)); if (simplifyExprs) { query = ConstraintManager::simplifyExpr(set, query); } @@ -34,10 +36,12 @@ bool ConcretizationManager::contains(const ConstraintSet &set, } void ConcretizationManager::add(const Query &query, const Assignment &assign) { - ref expr = query.expr; + ref expr = Expr::createIsZero(Expr::createIsZero(query.expr)); if (simplifyExprs) { expr = ConstraintManager::simplifyExpr(query.constraints, expr); } - CacheEntry ce(query.constraints, query.expr); + CacheEntry ce(query.constraints, 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 bbd3a4996a..bcb0ebf130 100644 --- a/lib/Solver/ConcretizingSolver.cpp +++ b/lib/Solver/ConcretizingSolver.cpp @@ -267,6 +267,13 @@ bool ConcretizingSolver::computeValidity( concretizationManager->add( query, negatedQueryInvalidResponse->initialValuesFor(objects)); } + } else { + concretizationManager->add( + query.negateExpr(), + cast(queryResult)->initialValuesFor(objects)); + concretizationManager->add( + query, + cast(negatedQueryResult)->initialValuesFor(objects)); } return true; @@ -308,7 +315,14 @@ char *ConcretizingSolver::getConstraintLog(const Query &query) { bool ConcretizingSolver::computeTruth(const Query &query, bool &isValid) { if (!query.containsSymcretes()) { - return solver->impl->computeTruth(query, isValid); + if (solver->impl->computeTruth(query, isValid)) { + if (!isValid) { + concretizationManager->add(query.negateExpr(), + query.constraints.getConcretization()); + } + return true; + } + return false; } auto assign = query.constraints.getConcretization(); @@ -377,9 +391,9 @@ bool ConcretizingSolver::computeValidityCore(const Query &query, if (ref resultInvalidResponse = dyn_cast(result)) { assign = resultInvalidResponse->initialValuesFor(assign.keys()); + isValid = false; } else { assert(result->tryGetValidityCore(validityCore)); - isValid = false; } } @@ -444,6 +458,8 @@ bool ConcretizingSolver::computeInitialValues( constructConcretizedQuery(query, assign), objects, values, hasSolution); } + } else { + concretizationManager->add(query.negateExpr(), assign); } return true; diff --git a/lib/Solver/Solver.cpp b/lib/Solver/Solver.cpp index ce92b17974..6a71a6a57d 100644 --- a/lib/Solver/Solver.cpp +++ b/lib/Solver/Solver.cpp @@ -49,6 +49,17 @@ bool Solver::evaluate(const Query &query, Validity &result) { return impl->computeValidity(query, result); } +Solver::PartialValidity Solver::evaluate(const Query &query) { + assert(query.expr->getWidth() == Expr::Bool && "Invalid expression type!"); + + if (ConstantExpr *CE = dyn_cast(query.expr)) { + return CE->isTrue() ? PartialValidity::MustBeTrue + : PartialValidity::MustBeFalse; + } + + return impl->computePartialValidity(query); +} + bool Solver::mustBeTrue(const Query &query, bool &result) { assert(query.expr->getWidth() == Expr::Bool && "Invalid expression type!"); diff --git a/lib/Solver/SolverImpl.cpp b/lib/Solver/SolverImpl.cpp index f726d8f765..93774feabf 100644 --- a/lib/Solver/SolverImpl.cpp +++ b/lib/Solver/SolverImpl.cpp @@ -217,6 +217,29 @@ bool SolverImpl::computeMinimalUnsignedValue(const Query &query, return true; } +Solver::PartialValidity SolverImpl::computePartialValidity(const Query &query) { + bool isTrue, isFalse; + bool trueSuccess, falseSuccess; + trueSuccess = computeTruth(query, isTrue); + if (trueSuccess && isTrue) { + return Solver::PartialValidity::MustBeTrue; + } + falseSuccess = computeTruth(query.negateExpr(), isFalse); + if (falseSuccess && isFalse) { + return Solver::PartialValidity::MustBeFalse; + } + if (trueSuccess && falseSuccess) { + return Solver::PartialValidity::TrueOrFalse; + } + if (trueSuccess && !falseSuccess) { + return Solver::PartialValidity::MayBeFalse; + } + if (!trueSuccess && falseSuccess) { + return Solver::PartialValidity::MayBeTrue; + } + return Solver::PartialValidity::None; +} + const char *SolverImpl::getOperationStatusString(SolverRunStatus statusCode) { switch (statusCode) { case SOLVER_RUN_STATUS_SUCCESS_SOLVABLE: diff --git a/lib/Solver/Z3Solver.cpp b/lib/Solver/Z3Solver.cpp index bc1ce143b5..eb0d4d0f26 100644 --- a/lib/Solver/Z3Solver.cpp +++ b/lib/Solver/Z3Solver.cpp @@ -425,8 +425,17 @@ bool Z3SolverImpl::internalRunSolver( exprs.insert(sideConstraint); } + std::vector arrays = query.gatherArrays(); + bool forceTactic = true; + for (const Array *array : arrays) { + if (isa(array->source)) { + forceTactic = false; + break; + } + } + Z3_solver theSolver; - if (Z3_probe_apply(builder->ctx, probe, goal)) { + if (forceTactic && Z3_probe_apply(builder->ctx, probe, goal)) { theSolver = Z3_mk_solver_for_logic( builder->ctx, Z3_mk_string_symbol(builder->ctx, "QF_AUFBV")); } else { @@ -645,6 +654,9 @@ SolverImpl::SolverRunStatus Z3SolverImpl::handleSolverResponse( if (strcmp(reason, "unknown") == 0) { return SolverImpl::SOLVER_RUN_STATUS_FAILURE; } + if (strcmp(reason, "smt tactic failed to show goal to be sat/unsat") == 0) { + return SolverImpl::SOLVER_RUN_STATUS_FAILURE; + } if (strcmp(reason, "interrupted from keyboard") == 0) { return SolverImpl::SOLVER_RUN_STATUS_INTERRUPTED; } diff --git a/optional b/optional new file mode 160000 index 0000000000..0723abd03b --- /dev/null +++ b/optional @@ -0,0 +1 @@ +Subproject commit 0723abd03b25fb11997fcb6cff6471408fc33072 diff --git a/scripts/convert_to_sarif.py b/scripts/convert_to_sarif.py new file mode 100755 index 0000000000..faba558014 --- /dev/null +++ b/scripts/convert_to_sarif.py @@ -0,0 +1,73 @@ +import argparse +from collections import defaultdict +import json +import os + + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument('--input', type=str, required=True) + parser.add_argument('--output_dir', type=str, required=True) + return parser.parse_args() + + +def convert_error(error): + converted_error = {} + locations = [] + codeFlows = [{"threadFlows": [{"locations": locations}]}] + converted_error["codeFlows"] = codeFlows + for trace in error["trace"]: + traceLocation = trace["location"] + physicalLocation = { + "artifactLocation": { + "uri": traceLocation["file"] + }, + "region": { + "startLine": traceLocation["reportLine"], + "startColumn": traceLocation.get("column") + } + } + location = { + "physicalLocation": physicalLocation + } + locations.append({"location": location}) + + trace = error["trace"][-1] + if trace["event"]["kind"] == "Error": + converted_error["ruleId"] = trace["event"]["error"]["sort"] + else: + converted_error["ruleId"] = "Reached" + converted_error["locations"] = [locations[-1]["location"]] + + + return converted_error + + +def convert_file(input: str, output_dir: str): + with open(input, 'r') as f: + input_report = json.load(f) + + output_report = {} + output_report["runs"] = [defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: defaultdict(lambda: 0)))))] + run = output_report["runs"][0] + run["tool"]["driver"]["name"] = "huawei" + run["results"] = [] + results = run["results"] + + for error in input_report["errors"]: + results.append(convert_error(error)) + + with open(os.path.join(output_dir, os.path.basename(input)), 'w') as f: + json.dump(output_report, f, indent=4) + +def main(): + args = get_args() + if os.path.isdir(args.input): + for f in os.listdir(args.input): + if f.endswith('.json'): + convert_file(os.path.join(args.input, f), args.output_dir) + else: + convert_file(args.input, args.output_dir) + +if __name__ == "__main__": + main() diff --git a/scripts/filter_locations.py b/scripts/filter_locations.py new file mode 100644 index 0000000000..e17dca63a3 --- /dev/null +++ b/scripts/filter_locations.py @@ -0,0 +1,45 @@ +import argparse +import json + + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument('--input', type=str, required=True) + parser.add_argument('--output', type=str, required=True) + return parser.parse_args() + +def prepare_cppcheck(input: str, output: str): + with open(input, 'r') as file: + input_report = json.load(file) + run = input_report["runs"][0] + for i, result in enumerate(run["results"]): + result["id"] = i + if "codeFlows" in result: + locations = result["codeFlows"][0]["threadFlows"][0]["locations"] + new_locs = [] + prev = -1 + for i, loc in enumerate(locations): + if prev == -1: + new_locs.append(loc) + prev = i + else: + if loc["location"]["physicalLocation"]["artifactLocation"]["uri"] == locations[prev]["location"]["physicalLocation"]["artifactLocation"]["uri"] and loc["location"]["physicalLocation"]["region"]["startLine"] == locations[prev]["location"]["physicalLocation"]["region"]["startLine"]: + if "startColumn" in loc["location"]["physicalLocation"]["region"]: + if loc["location"]["physicalLocation"]["region"]["startColumn"] != locations[prev]["location"]["physicalLocation"]["region"]["startColumn"]: + new_locs.append(loc) + prev = i + else: + new_locs.append(loc) + prev = i + + result["codeFlows"][0]["threadFlows"][0]["locations"] = new_locs + with open(output, 'w') as f: + json.dump({"runs": [run]}, f, indent=4) + + +def main(): + args = get_args() + prepare_cppcheck(args.input, args.output) + +if __name__ == "__main__": + main() diff --git a/scripts/merge_sarif.py b/scripts/merge_sarif.py new file mode 100644 index 0000000000..1b69fdbb26 --- /dev/null +++ b/scripts/merge_sarif.py @@ -0,0 +1,33 @@ +import argparse +import json +import os + + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument('--input_dir', type=str, required=True) + parser.add_argument('--output', type=str, required=True) + return parser.parse_args() + +def merge_dir(input_dir: str, output: str): + out = {"results": []} + for f in os.listdir(input_dir): + if f.endswith('.sarif'): + with open(os.path.join(input_dir, f), 'r') as file: + input_report = json.load(file) + run = input_report["runs"][0] + if "tool" not in out: + out["tool"] = run["tool"] + else: + assert(out["tool"]["driver"]["name"] == run["tool"]["driver"]["name"]) + out["results"].extend(run["results"]) + with open(output, 'w') as f: + json.dump({"runs": [out]}, f, indent=4) + + +def main(): + args = get_args() + merge_dir(args.input_dir, args.output) + +if __name__ == "__main__": + main() diff --git a/test/Feature/FunctionPointer.c b/test/Feature/FunctionPointer.c index e8c51d22dd..070d1ac91d 100644 --- a/test/Feature/FunctionPointer.c +++ b/test/Feature/FunctionPointer.c @@ -1,6 +1,6 @@ // RUN: %clang %s -emit-llvm -g -c -o %t.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --write-no-tests --exit-on-error %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --write-no-tests --exit-on-error %t.bc 2>&1 | FileCheck %s #include "klee/klee.h" diff --git a/test/Feature/LazyInitialization/LazyInitialization.c b/test/Feature/LazyInitialization/LazyInitialization.c index 31eb53bede..74356f31b3 100644 --- a/test/Feature/LazyInitialization/LazyInitialization.c +++ b/test/Feature/LazyInitialization/LazyInitialization.c @@ -1,7 +1,7 @@ // REQUIRES: z3 // RUN: %clang %s -emit-llvm %O0opt -c -o %t.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out -solver-backend=z3 --skip-not-symbolic-objects --use-timestamps=false --use-guided-search=false %t.bc > %t.log +// RUN: %klee --output-dir=%t.klee-out --solver-backend=z3 --skip-not-symbolic-objects --use-timestamps=false --use-guided-search=none %t.bc > %t.log // RUN: FileCheck %s -input-file=%t.log struct Node { diff --git a/test/Feature/MakeConcreteSymbolic.c b/test/Feature/MakeConcreteSymbolic.c index 36ed808079..0e172b6b49 100644 --- a/test/Feature/MakeConcreteSymbolic.c +++ b/test/Feature/MakeConcreteSymbolic.c @@ -4,7 +4,7 @@ // RUN: grep "done: total queries = 0" %t.klee-out/info // RUN: rm -rf %t.klee-out -// RUN: %klee --optimize=false --output-dir=%t.klee-out --make-concrete-symbolic=1 --exit-on-error %t1.bc +// RUN: %klee --optimize=false --output-dir=%t.klee-out --make-concrete-symbolic=1 --use-guided-search=none --exit-on-error %t1.bc // RUN: grep "done: total queries = 2" %t.klee-out/info #include diff --git a/test/Feature/MaxStaticForkPct.c b/test/Feature/MaxStaticForkPct.c index 42a51958f1..056744053b 100644 --- a/test/Feature/MaxStaticForkPct.c +++ b/test/Feature/MaxStaticForkPct.c @@ -1,12 +1,12 @@ // RUN: %clang %s -emit-llvm %O0opt -c -g -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee -max-static-fork-pct=0.2 --use-guided-search=false -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-pt2 %s +// RUN: %klee -max-static-fork-pct=0.2 --use-guided-search=none -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-pt2 %s // RUN: rm -rf %t.klee-out -// RUN: %klee -max-static-fork-pct=0.5 --use-guided-search=false -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-pt5 %s +// RUN: %klee -max-static-fork-pct=0.5 --use-guided-search=none -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-pt5 %s // RUN: rm -rf %t.klee-out -// RUN: %klee -max-static-fork-pct=0.8 --use-guided-search=false -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-pt8 %s +// RUN: %klee -max-static-fork-pct=0.8 --use-guided-search=none -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-pt8 %s // RUN: rm -rf %t.klee-out -// RUN: %klee -max-static-cpfork-pct=0.5 --use-guided-search=false -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-cppt5 %s +// RUN: %klee -max-static-cpfork-pct=0.5 --use-guided-search=none -max-static-pct-check-delay=1 --output-dir=%t.klee-out %t1.bc 2>&1 | FileCheck --check-prefix=CHECK-cppt5 %s #include "klee/klee.h" #include diff --git a/test/Feature/MultipleFreeResolution.c b/test/Feature/MultipleFreeResolution.c index d62cdeb9b2..a938e87ce0 100644 --- a/test/Feature/MultipleFreeResolution.c +++ b/test/Feature/MultipleFreeResolution.c @@ -1,6 +1,6 @@ // RUN: %clang %s -g -emit-llvm %O0opt -c -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --emit-all-errors %t1.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --emit-all-errors %t1.bc 2>&1 | FileCheck %s // RUN: ls %t.klee-out/ | grep .ktest | wc -l | grep 4 // RUN: ls %t.klee-out/ | grep .err | wc -l | grep 3 diff --git a/test/Industry/.clang-format b/test/Industry/.clang-format new file mode 100644 index 0000000000..a43d914ec3 --- /dev/null +++ b/test/Industry/.clang-format @@ -0,0 +1,2 @@ +DisableFormat: true +SortIncludes: false \ No newline at end of file diff --git a/test/Industry/AssignNull_Scene_BadCase02.c b/test/Industry/AssignNull_Scene_BadCase02.c new file mode 100644 index 0000000000..5223d67fc3 --- /dev/null +++ b/test/Industry/AssignNull_Scene_BadCase02.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * @description FORWARD_NULL, 如果字符串或者指针作为函数参数,为了防止空指针引用错误,在引用前必须确保该参数不为NULL, + * 如果上层调用者已经保证了该参数不可能为NULL,在调用本函数时,在函数开始处可以加ASSERT进行校验。 + * + * @bad TestBad5; + * + * @CWE 476; + * + * @secureCoding + * + * @csec + * + * @tool Coverity:FORWARD_NULL;SecBrella:SecB_ForwardNull; + * + * @author xwx356597; m00489032 + * + */ + +#include +#include +#include +//#include "securec.h" + +struct TEACHER { + unsigned int id; + char *name; +}; + +struct STU { + unsigned int id; + char *name; + struct TEACHER *teacher; +}; + +/* + * @mainScene 开始事件:变量赋值NULL,结束事件:指针解引用运算 + * @subScene 指针是结构体类型,直接赋值为{0},之后访问成员变量 + */ +void TestBad5(struct STU *pdev, const char *buf, unsigned int count) +{ + struct TEACHER *tea; + // 开始事件,assign_zero - 结构体变量被赋予 {0} 值 + struct STU *stu = 0; + /* POTENTIAL FLAW: 结束事件,var_deref_op - null 指针解引用运算,访问成员变量 */ + tea = stu->teacher; // CHECK: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 + unsigned int teacherID = tea->id; + printf("teacher id is %ud", teacherID); +} + +// 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 --location-accuracy --mock-external-calls --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s +// CHECK: KLEE: WARNING: 100.00% NullPointerException False Positive at trace 2 \ No newline at end of file diff --git a/test/Industry/AssignNull_Scene_BadCase02.c.json b/test/Industry/AssignNull_Scene_BadCase02.c.json new file mode 100644 index 0000000000..7028cd09b0 --- /dev/null +++ b/test/Industry/AssignNull_Scene_BadCase02.c.json @@ -0,0 +1,117 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/AssignNull_Scene_BadCase02.c" + }, + "region": { + "startLine": 45, + "startColumn": 17 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/AssignNull_Scene_BadCase02.c" + }, + "region": { + "startLine": 47, + "startColumn": 16 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/AssignNull_Scene_BadCase02.c" + }, + "region": { + "startLine": 47, + "startColumn": 16 + } + } + } + ] + }, + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/AssignNull_Scene_BadCase02.c" + }, + "region": { + "startLine": 45, + "startColumn": 17 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/AssignNull_Scene_BadCase02.c" + }, + "region": { + "startLine": 47, + "startColumn": 11 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/AssignNull_Scene_BadCase02.c" + }, + "region": { + "startLine": 47, + "startColumn": 11 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/AssignNull_Scene_BadCase04.c b/test/Industry/AssignNull_Scene_BadCase04.c new file mode 100644 index 0000000000..c3ad59d202 --- /dev/null +++ b/test/Industry/AssignNull_Scene_BadCase04.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * @description FORWARD_NULL, 如果字符串或者指针作为函数参数,为了防止空指针引用错误,在引用前必须确保该参数不为NULL, + * 如果上层调用者已经保证了该参数不可能为NULL,在调用本函数时,在函数开始处可以加ASSERT进行校验。 + * + * @bad TestBad7; + * + * @CWE 476; + * + * @secureCoding + * + * @csec + * + * @tool Coverity:FORWARD_NULL;SecBrella:SecB_ForwardNull; + * + * @author xwx356597; m00489032 + * + */ + +#include +#include +#include +#include "securec.h" + +void *HelpTestBad7(void *dev, unsigned int count) +{ + if (count > 0) { + dev = malloc(count); + if (dev == NULL) { + return NULL; + } + memset_s((char *)dev, count, 0, count); + } + return dev; // 这里必须返回入参,如果直接返回NULL,Coverity检查不出来 +} + +/* + * @mainScene 开始事件:变量赋值NULL,结束事件:指针解引用运算 + * @subScene 通过函数出参可能返回NULL,解引用使用数组访问方式 + */ +int TestBad7(char *arg, unsigned int count) +{ + // 开始事件,assign_zero - 变量被赋予 NULL 值 + char **dev = NULL; + // 中间事件,identity_transfer - 函数的返回值为 NULL,因为函数的其中一个参数可能为 null 并且未经修改即返回。 + dev = HelpTestBad7(dev, count); + /* POTENTIAL FLAW: 结束事件,var_deref_op - null 指针解引用运算,使用数组访问方式 */ + dev[0] = arg; // 当前SecBrella漏报 // CHECK: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 + return 1; +} + +// 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 --check-out-of-memory --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --smart-resolve-entry-function --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/AssignNull_Scene_BadCase04.c.json b/test/Industry/AssignNull_Scene_BadCase04.c.json new file mode 100644 index 0000000000..35f43f7cc4 --- /dev/null +++ b/test/Industry/AssignNull_Scene_BadCase04.c.json @@ -0,0 +1,78 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/AssignNull_Scene_BadCase04.c" + }, + "region": { + "startLine": 31, + "startColumn": 13 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/AssignNull_Scene_BadCase04.c" + }, + "region": { + "startLine": 47, + "startColumn": 11 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/AssignNull_Scene_BadCase04.c" + }, + "region": { + "startLine": 49, + "startColumn": 12 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/AssignNull_Scene_BadCase04.c" + }, + "region": { + "startLine": 49, + "startColumn": 12 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/BadCase01_SecB_ForwardNull.c b/test/Industry/BadCase01_SecB_ForwardNull.c new file mode 100644 index 0000000000..e84beee7d2 --- /dev/null +++ b/test/Industry/BadCase01_SecB_ForwardNull.c @@ -0,0 +1,148 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * @description 空指针解引用 验收失败 + * + */ +#include +#include +#include "securec.h" +typedef unsigned int UINT32; +#define MAX_SHIFT_COUNT 64 +#define OK 1 +#define ERROR 0 + +typedef struct { + UINT32 offset; +} StructInfo; + +typedef struct { + UINT32 dataLen; + char *dataBuff; +} DataInfo; + +typedef struct { + UINT32 offset; + UINT32 dataLen; + char *dataBuff; +} SendDataInfo; + +typedef struct { + StructInfo *info; +} DataInfoStruct; + + +void GetDataBuffInfo(DataInfo *dataBuffInfo, UINT32 inputNum) +{ + if (inputNum != MAX_SHIFT_COUNT) { + dataBuffInfo->dataBuff = (char *)malloc(MAX_SHIFT_COUNT); + if (dataBuffInfo->dataBuff == NULL) { + return; + } + dataBuffInfo->dataLen = MAX_SHIFT_COUNT; + } +} + + +void PrintBuf(SendDataInfo *dataBuffInfo) +{ + for (UINT32 i = 0; i < dataBuffInfo->dataLen; i++) { + printf("buf data: %c", *(dataBuffInfo->dataBuff + i)); + } +} + +void SaveBufInfo(DataInfo *dataBuffInfo) +{ + SendDataInfo *sendData = NULL; + sendData = (SendDataInfo *)malloc(sizeof(SendDataInfo)); + if (sendData == NULL) { + return; + } + + sendData->offset = MAX_SHIFT_COUNT; + sendData->dataBuff = dataBuffInfo->dataBuff; + sendData->dataLen = dataBuffInfo->dataLen; + PrintBuf(sendData); + free(sendData); +} + +void NotifyBuff(DataInfo *dataBuffInfo, UINT32 inputNum2) +{ + switch (inputNum2) { + case MAX_SHIFT_COUNT: + SaveBufInfo(dataBuffInfo); + break; + default: + break; + } + + if (dataBuffInfo->dataBuff != NULL) { + free(dataBuffInfo->dataBuff); + } +} +#if 0 + +DataInfo g_secInfo = { 0 }; +UINT32 GetSecInfo(DataInfo *secInfo) +{ + if (secInfo == NULL) { + return ERROR; + } + (void)memcpy_s(secInfo, sizeof(DataInfo), &g_secInfo, sizeof(DataInfo)); + return OK; +} + + +// FN: DTS2022061984858, https://portal.edevops.huawei.com/cloudreq-obp/project/gdf7ba016c8b34e3c80a19f8796b330f9/productSpace/bugList/2028184310/addbug/2029643728 +void WB_BadCase01(DataInfoStruct *input, UINT32 flag) +{ + StructInfo *tempInfo = NULL; + (void)memset_s(tempInfo, sizeof(StructInfo), 0, sizeof(StructInfo)); + if ((input == NULL) || (flag == 1)) { + goto EXIT; + } + tempInfo = input->info; + +EXIT: + if (tempInfo->offset > 100) { + return; + } + (void)memset_s(tempInfo, sizeof(StructInfo), 0, sizeof(StructInfo)); +} +#endif + +// FN: DTS2022061984853, https://portal.edevops.huawei.com/cloudreq-obp/project/gdf7ba016c8b34e3c80a19f8796b330f9/productSpace/bugList/2028184310/addbug/2029643723 +void WB_BadCase02(UINT32 inputNum1, UINT32 inputNum2) +{ + DataInfo dataBuffInfo = {0}; + + GetDataBuffInfo(&dataBuffInfo, inputNum1); + NotifyBuff(&dataBuffInfo, inputNum2); +} + +#if 0 +// FN: DTS2022061984852, https://portal.edevops.huawei.com/cloudreq-obp/project/gdf7ba016c8b34e3c80a19f8796b330f9/productSpace/bugList/2028184310/addbug/2029643722 +void WB_BadCase03(UINT32 *output, UINT32 *input, UINT32 flag) +{ + DataInfo secInfo = {0}; + char *data = NULL; + UINT32 ret = GetSecInfo(&secInfo); + if (ret == ERROR) { + return; + } + data = secInfo.dataBuff; + printf("data: %c", *data); +} + +void badbad(char *ptr) +{ + ptr = NULL; + *ptr = 'a'; +} +#endif + +// 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 --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --check-out-of-memory --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s +// CHECK: KLEE: WARNING: No targets found in error-guided mode \ No newline at end of file diff --git a/test/Industry/BadCase01_SecB_ForwardNull.c.json b/test/Industry/BadCase01_SecB_ForwardNull.c.json new file mode 100644 index 0000000000..25a74add14 --- /dev/null +++ b/test/Industry/BadCase01_SecB_ForwardNull.c.json @@ -0,0 +1,12 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [] + } + ] +} \ No newline at end of file diff --git a/test/Industry/CheckNull_Scene_BadCase02.c b/test/Industry/CheckNull_Scene_BadCase02.c new file mode 100644 index 0000000000..fb34228958 --- /dev/null +++ b/test/Industry/CheckNull_Scene_BadCase02.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * @description FORWARD_NULL, 如果字符串或者指针作为函数参数,为了防止空指针引用错误,在引用前必须确保该参数不为NULL, + * 如果上层调用者已经保证了该参数不可能为NULL,在调用本函数时,在函数开始处可以加ASSERT进行校验。 + * + * @bad TestBad2; + * + * @CWE 476; + * + * @secureCoding + * + * @csec + * + * @tool Coverity:FORWARD_NULL;SecBrella:SecB_ForwardNull; + * + * @author xwx356597; m00489032 + * + */ + +#include +#include +#include +#include "securec.h" + +#define BUFFERSIZE 100 + +/* + * @mainScene 开始事件:指针解引用之前判空,结束事件:在函数中对可能为null的指针进行解引用 + * @subScene 指针来自动态分配,在为空路径下作为实参传入对其解引用的函数调用 + */ +void TestBad2() +{ + char *errMsg = (char *)malloc(BUFFERSIZE); + // 开始事件,var_compare_op - 在执行null指针解引用之前将变量与null进行比较 + if (errMsg == NULL) { + printf("malloc fail"); + } + /* POTENTIAL FLAW: var_deref_model - 在该函数中对可能为null的指针进行解引用 */ + strcpy(errMsg, "No errors yet."); // CHECK: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 + free(errMsg); +} + +// 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 --check-out-of-memory --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/CheckNull_Scene_BadCase02.c.json b/test/Industry/CheckNull_Scene_BadCase02.c.json new file mode 100644 index 0000000000..c1a3a11681 --- /dev/null +++ b/test/Industry/CheckNull_Scene_BadCase02.c.json @@ -0,0 +1,78 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/CheckNull_Scene_BadCase02.c" + }, + "region": { + "startLine": 36, + "startColumn": 16 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/CheckNull_Scene_BadCase02.c" + }, + "region": { + "startLine": 40, + "startColumn": 12 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/CheckNull_Scene_BadCase02.c" + }, + "region": { + "startLine": 40, + "startColumn": 5 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/CheckNull_Scene_BadCase02.c" + }, + "region": { + "startLine": 40, + "startColumn": 5 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/CheckNull_Scene_BadCase04.c b/test/Industry/CheckNull_Scene_BadCase04.c new file mode 100644 index 0000000000..e7efbd7ed5 --- /dev/null +++ b/test/Industry/CheckNull_Scene_BadCase04.c @@ -0,0 +1,63 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * @description FORWARD_NULL, 如果字符串或者指针作为函数参数,为了防止空指针引用错误,在引用前必须确保该参数不为NULL, + * 如果上层调用者已经保证了该参数不可能为NULL,在调用本函数时,在函数开始处可以加ASSERT进行校验。 + * + * @bad TestBad12; + * + * @CWE 476; + * + * @secureCoding + * + * @csec + * + * @tool Coverity:FORWARD_NULL;SecBrella:SecB_ForwardNull; + * + * @author xwx356597; m00489032 + * + */ + +#include +#include +#include +#include "securec.h" + +#define BUFFERSIZE 100 + +/* + * @mainScene 开始事件:指针解引用之前判空,结束事件:在函数中对可能为null的指针进行解引用 + * @subScene 指针来自动态分配,在为空路径下作为实参传入对其解引用的函数调用 + */ +void TestBad12(int cond1, int cond2) +{ + // returned_null: "malloc" returns "NULL" (checked 1 out of 7 times) + // var_assigned: Assigning: "buf" = "NULL" return value from "malloc" + char *buf1 = (char *)malloc(BUFFERSIZE); + char *buf2 = (char *)malloc(BUFFERSIZE); + if (buf1 == NULL) { + unsigned int i = 0; + + if(cond1 > 0 ){ + i = 1; + } + if(cond2 < 0){ + i = 2; + } + if((cond1+cond2) > 0){ + if(buf2 == NULL) { + /* POTENTIAL FLAW: var_deref_model - 在该函数中对可能为null的指针进行解引用 */ + strcpy(buf2, "0"); // CHECK-DAG: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 + free(buf2); + } + return; + } + free(buf1); + } +} + +// 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 --check-out-of-memory --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/CheckNull_Scene_BadCase04.c.json b/test/Industry/CheckNull_Scene_BadCase04.c.json new file mode 100644 index 0000000000..ac5fbac4c1 --- /dev/null +++ b/test/Industry/CheckNull_Scene_BadCase04.c.json @@ -0,0 +1,78 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/CheckNull_Scene_BadCase04.c" + }, + "region": { + "startLine": 48, + "startColumn": 12 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/CheckNull_Scene_BadCase04.c" + }, + "region": { + "startLine": 50, + "startColumn": 12 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/CheckNull_Scene_BadCase04.c" + }, + "region": { + "startLine": 50, + "startColumn": 5 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/CheckNull_Scene_BadCase04.c" + }, + "region": { + "startLine": 50, + "startColumn": 5 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/FN_SecB_ForwardNull_filed.c b/test/Industry/FN_SecB_ForwardNull_filed.c new file mode 100644 index 0000000000..8348752770 --- /dev/null +++ b/test/Industry/FN_SecB_ForwardNull_filed.c @@ -0,0 +1,51 @@ +// 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 --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * @description 空指针解引用 验收失败 + */ +#include +#include +#include "securec.h" +typedef unsigned int UINT32; +#define MAX_SHIFT_COUNT 64 +#define OK 1 +#define ERROR 0 + +typedef struct { + UINT32 offset; +} StructInfo; + +typedef struct { + UINT32 dataLen; + char *dataBuff; +} DataInfo; + +typedef struct { + UINT32 offset; + UINT32 dataLen; + char *dataBuff; +} SendDataInfo; + +typedef struct { + StructInfo *info; +} DataInfoStruct; + +void WB_BadCase_Field(UINT32 inputNum1, UINT32 inputNum2) +{ + DataInfo dataBuffInfo = {0}; + dataBuffInfo.dataBuff = NULL; + *dataBuffInfo.dataBuff = 'c'; +} + +void WB_BadCase_field2(DataInfo *data) +{ + data->dataBuff = NULL; + *data->dataBuff = 'c'; // CHECK: KLEE: WARNING: 100.00% NullPointerException False Negative at: {{.*}}test/Industry/FN_SecB_ForwardNull_filed.c:47 19 + + char *ptr = NULL; + *ptr = 'c'; // CHECK: KLEE: WARNING: 100.00% NullPointerException False Positive at trace 1 +} diff --git a/test/Industry/FN_SecB_ForwardNull_filed.c.json b/test/Industry/FN_SecB_ForwardNull_filed.c.json new file mode 100644 index 0000000000..fbe047e10a --- /dev/null +++ b/test/Industry/FN_SecB_ForwardNull_filed.c.json @@ -0,0 +1,65 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/forward_null/./FN_SecB_ForwardNull_filed.c" + }, + "region": { + "startLine": 49, + "startColumn": null + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/forward_null/./FN_SecB_ForwardNull_filed.c" + }, + "region": { + "startLine": 50, + "startColumn": null + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/forward_null/./FN_SecB_ForwardNull_filed.c" + }, + "region": { + "startLine": 50, + "startColumn": null + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file 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 new file mode 100644 index 0000000000..84fb46cd5d --- /dev/null +++ b/test/Industry/MemoryLeak/LocalVar_Alloc_in_Loop_Exit_in_Loop_BadCase01.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * @description RESOURCE_LEAK 查找程序没有尽快释放系统资源的情况 + * + * @good + * + * @bad ResourceLeak_bad01 + * + * @tool Coverity:RESOURCE_LEAK;SecBrella:SecB_MemoryLeak; + * + *@CWE 404 + * + * @author r00369861 + * + */ + +#include +#include + +#define MEM_SIZE 10 + +// @scene 循环中本地分配的内存,在条件分支中提前返回,返回前没有释放内存 +int ResourceLeak_bad01(int c) +{ + /* POTENTIAL FLAW: "ppParamId" and "ppParamId[0] - ppParamId[i-1]" is leaked */ + char **ppParamId = (char **)malloc(MEM_SIZE * sizeof(char *)); + if (ppParamId == NULL) { + return -1; + } + + int i = 0; + for (; i < MEM_SIZE; i++) { + ppParamId[i] = (char *)malloc(sizeof(char)); + if (ppParamId[i] == NULL) { + /* leak here: "ppParamId" and "ppParamId[0] - ppParamId[i-1]" is leaked */ + return -1; + } + } + + /* do something */ + int j = 0; + for (; j < MEM_SIZE; j++) { + free(ppParamId[j]); + } + + free(ppParamId); + return 0; +} + +void call_func(int num) +{ + ResourceLeak_bad01(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 --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/MemoryLeak/LocalVar_Alloc_in_Loop_Exit_in_Loop_BadCase01.c.json b/test/Industry/MemoryLeak/LocalVar_Alloc_in_Loop_Exit_in_Loop_BadCase01.c.json new file mode 100644 index 0000000000..6c5d3c1da0 --- /dev/null +++ b/test/Industry/MemoryLeak/LocalVar_Alloc_in_Loop_Exit_in_Loop_BadCase01.c.json @@ -0,0 +1,90 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_MemoryLeak/BadCase/LocalVar_Alloc_in_Loop_Exit_in_Loop_BadCase01.c" + }, + "region": { + "startLine": 34, + "startColumn": 32 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_MemoryLeak/BadCase/LocalVar_Alloc_in_Loop_Exit_in_Loop_BadCase01.c" + }, + "region": { + "startLine": 33, + "startColumn": 5 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_MemoryLeak/BadCase/LocalVar_Alloc_in_Loop_Exit_in_Loop_BadCase01.c" + }, + "region": { + "startLine": 43, + "startColumn": 5 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_MemoryLeak/BadCase/LocalVar_Alloc_in_Loop_Exit_in_Loop_BadCase01.c" + }, + "region": { + "startLine": 44, + "startColumn": 9 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_MemoryLeak/BadCase/LocalVar_Alloc_in_Loop_Exit_in_Loop_BadCase01.c" + }, + "region": { + "startLine": 44, + "startColumn": 9 + } + } + } + } + ] + } + ] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/NullReturn_BadCase_WhiteBox01.c b/test/Industry/NullReturn_BadCase_WhiteBox01.c new file mode 100644 index 0000000000..8ab5187fa6 --- /dev/null +++ b/test/Industry/NullReturn_BadCase_WhiteBox01.c @@ -0,0 +1,70 @@ +// 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 --smart-resolve-entry-function --use-guided-search=error --mock-external-calls --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved. + * + * @description 可能返回空指针的函数返回值,在解引用之前应该判空 + * + * @bad NullReturn_BadCase_WhiteBox01; + * + * @CWE 476; + * + * @secureCoding + * + * @csec + * + * @tool Coverity:NULL_RETURNS;SecBrella:SecB_NullReturns; + * + * @author x00452482 验收失败 + */ + +#include "NullReturn_WhiteBox.h" + +#define IS_USRID_INVALID(usrId) ((usrId) >= MAX_USER_ID) +#define IS_ULSCH_SCHINDEX_INVALID(schIndex) ((schIndex) >= MAX_SCH_INDEX) + +GlobalInfoStru *g_ulschUsrInfo; + +static inline GlobalInfoStru *GetUserInst(UINT16 usrId, UINT32 schIndex) +{ + if (IS_USRID_INVALID(usrId) || IS_ULSCH_SCHINDEX_INVALID(schIndex)) { + return NULL_PTR; + } + + UINT32 index = schIndex + usrId; + return &g_ulschUsrInfo[index]; +} + +static inline SchrltStru *GetResult(UINT16 usrId, UINT32 schIndex) +{ + GlobalInfoStru *usrGlbInfo = GetUserInst(usrId, schIndex); + if (IS_PTR_INVALID(usrGlbInfo)) { + return NULL_PTR; // (1) + } + + return usrGlbInfo->result; +} + +void SendMsg(UINT8 index, UINT16 usrId, SchrltStru *resultInfo) +{ + if ((index + usrId) > MAX_SCHED_USER_NUM_PER_TTI) { + resultInfo->userType = 1; // CHECK-DAG: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 2 + } else { + resultInfo->userType = 0; // CHECK-DAG: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 + } +} + +void NullReturn_BadCase_WhiteBox01(UINT8 index, SchedHarqStru *harqInfo) +{ + UINT32 schedUserNum = min(harqInfo->schedHarqNum, MAX_SCHED_USER_NUM_PER_TTI); + + for (UINT32 schedUserIndex = 0; schedUserIndex < schedUserNum; schedUserIndex++) { + UINT16 usrId = harqInfo->schedUsrId[schedUserIndex]; + SchrltStru *resultInfo = GetResult(usrId, harqInfo->schIndex); // (2) + // POTENTIAL FLAW: 指针未判空,直接下后续流程中进行解引用处理,引发空指针异常 + SendMsg(index, usrId, resultInfo); // (3) + } +} diff --git a/test/Industry/NullReturn_BadCase_WhiteBox01.c.json b/test/Industry/NullReturn_BadCase_WhiteBox01.c.json new file mode 100644 index 0000000000..0f5ab76e55 --- /dev/null +++ b/test/Industry/NullReturn_BadCase_WhiteBox01.c.json @@ -0,0 +1,169 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/NullReturn_BadCase_WhiteBox01.c" + }, + "region": { + "startLine": 45, + "startColumn": 9 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/NullReturn_BadCase_WhiteBox01.c" + }, + "region": { + "startLine": 66, + "startColumn": 34 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/NullReturn_BadCase_WhiteBox01.c" + }, + "region": { + "startLine": 68, + "startColumn": 9 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/NullReturn_BadCase_WhiteBox01.c" + }, + "region": { + "startLine": 56, + "startColumn": 30 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/NullReturn_BadCase_WhiteBox01.c" + }, + "region": { + "startLine": 56, + "startColumn": 30 + } + } + } + ] + }, + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/NullReturn_BadCase_WhiteBox01.c" + }, + "region": { + "startLine": 45, + "startColumn": 9 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/NullReturn_BadCase_WhiteBox01.c" + }, + "region": { + "startLine": 66, + "startColumn": 34 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/NullReturn_BadCase_WhiteBox01.c" + }, + "region": { + "startLine": 68, + "startColumn": 9 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/NullReturn_BadCase_WhiteBox01.c" + }, + "region": { + "startLine": 54, + "startColumn": 30 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/NullReturn_BadCase_WhiteBox01.c" + }, + "region": { + "startLine": 54, + "startColumn": 30 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/NullReturn_Scene_BadCase01.c b/test/Industry/NullReturn_Scene_BadCase01.c new file mode 100644 index 0000000000..16cd77cda3 --- /dev/null +++ b/test/Industry/NullReturn_Scene_BadCase01.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * @description 可能返回空指针的函数返回值,在解引用之前应该判空 + * + * @bad TestBad1; + * + * @CWE 476; + * + * @secureCoding + * + * @csec + * + * @tool Coverity:NULL_RETURNS;SecBrella:SecB_NullReturns; + * + * @author m00489032 + * + */ + +#include +#include +#include +#include +#include "securec.h" + +#define BUFFERSIZE 100 + +/* + * @scene malloc函数调用结果未判空,在sprintf_s中解引用 + */ +void TestBad1() +{ + // returned_null: "malloc" returns "NULL" (checked 1 out of 7 times) + // var_assigned: Assigning: "buf" = "NULL" return value from "malloc" + char *buf = (char *)malloc(BUFFERSIZE); + + /* POTENTIAL FLAW: dereference: Dereferencing a pointer that might be "NULL" "buf" when calling "strcpy" */ + strcpy(buf, "0"); // CHECK-DAG: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 + free(buf); +} + +// 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 --external-calls=all --check-out-of-memory --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/NullReturn_Scene_BadCase01.c.json b/test/Industry/NullReturn_Scene_BadCase01.c.json new file mode 100644 index 0000000000..f01a1e59a5 --- /dev/null +++ b/test/Industry/NullReturn_Scene_BadCase01.c.json @@ -0,0 +1,78 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase01.c" + }, + "region": { + "startLine": 35, + "startColumn": 25 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase01.c" + }, + "region": { + "startLine": 38, + "startColumn": 12 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase01.c" + }, + "region": { + "startLine": 38, + "startColumn": 5 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase01.c" + }, + "region": { + "startLine": 38, + "startColumn": 5 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/NullReturn_Scene_BadCase02.c b/test/Industry/NullReturn_Scene_BadCase02.c new file mode 100644 index 0000000000..a9fc727653 --- /dev/null +++ b/test/Industry/NullReturn_Scene_BadCase02.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * @description 可能返回空指针的函数返回值,在解引用之前应该判空 + * + * @bad TestBad2; + * + * @CWE 476; + * + * @secureCoding + * + * @csec + * + * @tool Coverity:NULL_RETURNS;SecBrella:SecB_NullReturns; + * + * @author m00489032 + * + */ + +#include +#include +#include +#include +#include "securec.h" + +/* + * @scene localtime函数调用结果未判空,访问成员解引用 + */ +void TestBad2() +{ + time_t rawtime; + + time(&rawtime); + // returned_null: "localtime" returns "NULL" (checked 0 out of 1 times) + // var_assigned: Assigning: "info" = "NULL" return value from "localtime" + struct tm *info = localtime(&rawtime); + /* POTENTIAL FLAW: dereference: Dereferencing "info", which is known to be "NULL" */ + printf("The second is %d", info->tm_sec); // CHECK: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 +} + +// 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 --location-accuracy --mock-external-calls --check-out-of-memory --skip-not-symbolic-objects --skip-not-lazy-initialized --extern-calls-can-return-null --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/NullReturn_Scene_BadCase02.c.json b/test/Industry/NullReturn_Scene_BadCase02.c.json new file mode 100644 index 0000000000..1bfd6f953f --- /dev/null +++ b/test/Industry/NullReturn_Scene_BadCase02.c.json @@ -0,0 +1,65 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase02.c" + }, + "region": { + "startLine": 36, + "startColumn": 23 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase02.c" + }, + "region": { + "startLine": 38, + "startColumn": 38 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase02.c" + }, + "region": { + "startLine": 38, + "startColumn": 32 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/NullReturn_Scene_BadCase03.c b/test/Industry/NullReturn_Scene_BadCase03.c new file mode 100644 index 0000000000..7d6c4fe5ef --- /dev/null +++ b/test/Industry/NullReturn_Scene_BadCase03.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * @description 可能返回空指针的函数返回值,在解引用之前应该判空 + * + * @bad TestBad3; + * + * @CWE 476; + * + * @secureCoding + * + * @csec + * + * @tool Coverity:NULL_RETURNS;SecBrella:SecB_NullReturns; + * + * @author m00489032 + * + */ + +#include +#include +#include +#include +#include "securec.h" + +#define BUFFERSIZE 100 + +/* + * @scene malloc函数调用结果未判空,通过指针别名在sprintf_s中解引用 + */ +void TestBad3() +{ + // returned_null: "malloc" returns "NULL" (checked 1 out of 7 times) + // var_assigned: Assigning: "buf" = "NULL" return value from "malloc" + char *buf = (char *)malloc(BUFFERSIZE); + // alias: Assigning: "bufNew" = "buf". Both pointer are now "NULL" + char *bufNew = buf; + /* POTENTIAL FLAW: dereference: Dereferencing a pointer that might be "NULL" "buf" when calling "strcpy" */ + strcpy(bufNew, "0"); // CHECK-DAG: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 + free(buf); +} + +// 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 --check-out-of-memory --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/NullReturn_Scene_BadCase03.c.json b/test/Industry/NullReturn_Scene_BadCase03.c.json new file mode 100644 index 0000000000..1108377f93 --- /dev/null +++ b/test/Industry/NullReturn_Scene_BadCase03.c.json @@ -0,0 +1,78 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase03.c" + }, + "region": { + "startLine": 35, + "startColumn": 25 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase03.c" + }, + "region": { + "startLine": 39, + "startColumn": 12 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase03.c" + }, + "region": { + "startLine": 39, + "startColumn": 5 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase03.c" + }, + "region": { + "startLine": 39, + "startColumn": 5 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/NullReturn_Scene_BadCase04.c b/test/Industry/NullReturn_Scene_BadCase04.c new file mode 100644 index 0000000000..0e20fe9412 --- /dev/null +++ b/test/Industry/NullReturn_Scene_BadCase04.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * @description 可能返回空指针的函数返回值,在解引用之前应该判空 + * + * @bad TestBad4; + * + * @CWE 476; + * + * @secureCoding + * + * @csec + * + * @tool Coverity:NULL_RETURNS;SecBrella:SecB_NullReturns; + * + * @author m00489032 + * + */ + +#include +#include +#include +#include +// #include "securec.h" + +#define BUFFERSIZE 100 + +void HelpTestBad4(char *buf) +{ + // deref_param_in_call: Function "strcpy" dereferences "buf" + /* POTENTIAL FLAW: dereference: Dereferencing a pointer that might be "NULL" "buf" when calling "HelpBad" */ + strcpy(buf, "0"); // CHECK: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 2 +} + +/* + * @scene malloc函数调用结果未判空,在函数调用中(跨过程),通过strcpy_s解引用 + */ +void TestBad4() +{ + // returned_null: "malloc" returns "NULL" (checked 1 out of 7 times) + // var_assigned: Assigning: "buf" = "NULL" return value from "malloc" + char *buf = (char *)malloc(BUFFERSIZE); + HelpTestBad4(buf); + free(buf); +} + +// 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 --check-out-of-memory --mock-external-calls --libc=klee --external-calls=all --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s +// CHEK: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 \ No newline at end of file diff --git a/test/Industry/NullReturn_Scene_BadCase04.c.json b/test/Industry/NullReturn_Scene_BadCase04.c.json new file mode 100644 index 0000000000..fcca266dc6 --- /dev/null +++ b/test/Industry/NullReturn_Scene_BadCase04.c.json @@ -0,0 +1,169 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/NullReturn_Scene_BadCase04.c" + }, + "region": { + "startLine": 42, + "startColumn": 25 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/NullReturn_Scene_BadCase04.c" + }, + "region": { + "startLine": 43, + "startColumn": 18 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/NullReturn_Scene_BadCase04.c" + }, + "region": { + "startLine": 32, + "startColumn": 12 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/NullReturn_Scene_BadCase04.c" + }, + "region": { + "startLine": 32, + "startColumn": 12 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/NullReturn_Scene_BadCase04.c" + }, + "region": { + "startLine": 32, + "startColumn": 12 + } + } + } + ] + }, + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/NullReturn_Scene_BadCase04.c" + }, + "region": { + "startLine": 42, + "startColumn": 25 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/NullReturn_Scene_BadCase04.c" + }, + "region": { + "startLine": 43, + "startColumn": 18 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/NullReturn_Scene_BadCase04.c" + }, + "region": { + "startLine": 32, + "startColumn": 12 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/NullReturn_Scene_BadCase04.c" + }, + "region": { + "startLine": 32, + "startColumn": 5 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/NullReturn_Scene_BadCase04.c" + }, + "region": { + "startLine": 32, + "startColumn": 5 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/NullReturn_Scene_BadCase06.c b/test/Industry/NullReturn_Scene_BadCase06.c new file mode 100644 index 0000000000..36ceb8d522 --- /dev/null +++ b/test/Industry/NullReturn_Scene_BadCase06.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * @description 可能返回空指针的函数返回值,在解引用之前应该判空 + * + * @bad TestBad6; + * + * @CWE 476; + * + * @secureCoding + * + * @csec + * + * @tool Coverity:NULL_RETURNS;SecBrella:SecB_NullReturns; + * + * @author m00489032 + * + */ + +#include +#include +#include +#include +#include "securec.h" + +#define BUFFERSIZE 100 + +char *HelpTestBad6(char *dev, unsigned int count) +{ + if (count > 0) { + int ret = memset_s(dev, count, 0, count); + if (ret != 0) { + return dev; + } + } + // return_param: Returning parameter "dev" + return dev; +} + +/* + * @scene malloc函数调用结果未判空,作为入参传入函数调用未作修改直接返回,通过数组下表访问解引用 + */ +void TestBad6(unsigned int count) +{ + // returned_null: "malloc" returns "NULL" (checked 1 out of 7 times) + // var_assigned: Assigning: "buf" = "NULL" return value from "malloc" + char *buf = (char *)malloc(BUFFERSIZE); + // identity_transfer: Passing "buf" as argument 1 to function "HelpTestBad7", which returns that argument + buf = HelpTestBad6(buf, count); + /* POTENTIAL FLAW: dereference: Dereferencing "buf", which is known to be "NULL" */ + buf[0] = 'a'; + free(buf); +} + +// 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 --check-out-of-memory --skip-not-symbolic-objects --skip-not-lazy-initialized --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 diff --git a/test/Industry/NullReturn_Scene_BadCase06.c.json b/test/Industry/NullReturn_Scene_BadCase06.c.json new file mode 100644 index 0000000000..40059129d3 --- /dev/null +++ b/test/Industry/NullReturn_Scene_BadCase06.c.json @@ -0,0 +1,169 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase06.c" + }, + "region": { + "startLine": 47, + "startColumn": 25 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase06.c" + }, + "region": { + "startLine": 49, + "startColumn": 24 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase06.c" + }, + "region": { + "startLine": 49, + "startColumn": 11 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase06.c" + }, + "region": { + "startLine": 51, + "startColumn": 5 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase06.c" + }, + "region": { + "startLine": 51, + "startColumn": 5 + } + } + } + ] + }, + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase06.c" + }, + "region": { + "startLine": 47, + "startColumn": 25 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase06.c" + }, + "region": { + "startLine": 49, + "startColumn": 24 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase06.c" + }, + "region": { + "startLine": 49, + "startColumn": 11 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase06.c" + }, + "region": { + "startLine": 51, + "startColumn": 5 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase06.c" + }, + "region": { + "startLine": 51, + "startColumn": 5 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/NullReturn_Scene_BadCase08.cpp b/test/Industry/NullReturn_Scene_BadCase08.cpp new file mode 100644 index 0000000000..3151cc0c67 --- /dev/null +++ b/test/Industry/NullReturn_Scene_BadCase08.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * @description 可能返回空指针的函数返回值,在解引用之前应该判空 + * + * @bad TestBad9; + * + * @CWE 476; + * + * @secureCoding + * + * @csec + * + * @tool Coverity:NULL_RETURNS;SecBrella:SecB_NullReturns; + * + * @author m00489032 + * + */ + +#include +#include +#include +#include +#include +#include + +/* + * @mainScene 开始事件:new,结束事件:指针解引用运算 + * @subScene new一个int数组后,未校验直接解引用 + */ +void TestBad9() +{ + // 开始事件,new返回NULL。到指针类型的动态转换可能是有意返回 NULL。 + // 中间事件,alias_transfer - 变量被赋予可能为 null 的指针 + int* p = new (std::nothrow) int[16]; + /* POTENTIAL FLAW: var_deref_op - null 指针解引用运算,new nothrow的返回值没有判空 */ + printf("the current integer is: %d", *p); // CHECK: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 +} + +// REQUIRES: z3 +// RUN: %clangxx %s -emit-llvm %O0opt -c -g -o %t1.bc +// RUN: rm -rf %t.klee-out +// RUN: %klee --output-dir=%t.klee-out --write-kqueries --use-guided-search=error --location-accuracy --mock-external-calls --check-out-of-memory --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s +// CHECK: KLEE: WARNING: 100.00% NullPointerException False Positive at trace 2 \ No newline at end of file diff --git a/test/Industry/NullReturn_Scene_BadCase08.cpp.json b/test/Industry/NullReturn_Scene_BadCase08.cpp.json new file mode 100644 index 0000000000..f45e1f283e --- /dev/null +++ b/test/Industry/NullReturn_Scene_BadCase08.cpp.json @@ -0,0 +1,117 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase08.cpp" + }, + "region": { + "startLine": 35, + "startColumn": 14 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase08.cpp" + }, + "region": { + "startLine": 37, + "startColumn": 39 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase08.cpp" + }, + "region": { + "startLine": 37, + "startColumn": 39 + } + } + } + ] + }, + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase08.cpp" + }, + "region": { + "startLine": 35, + "startColumn": 14 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase08.cpp" + }, + "region": { + "startLine": 37, + "startColumn": 40 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_NullReturns/BadCase/NullReturn_Scene_BadCase08.cpp" + }, + "region": { + "startLine": 37, + "startColumn": 40 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/NullReturn_WhiteBox.h b/test/Industry/NullReturn_WhiteBox.h new file mode 100644 index 0000000000..f998f0f041 --- /dev/null +++ b/test/Industry/NullReturn_WhiteBox.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved. + * Description: NULL_RETURNS规则公共头文件 + * Author: 徐西建 x00452482 + * Create: 2022-1-17 + */ + +#ifndef NULLRETURN_BADCASE_WHITEBOX_H +#define NULLRETURN_BADCASE_WHITEBOX_H + +#include +#include "base_type.h" + +#define MAX_SCHED_USER_NUM_PER_TTI 64 +#define MAX_USER_ID 32 +#define MAX_SCH_INDEX 32 +#define NULL_PTR NULL +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define IS_PTR_INVALID(x) ((x) == NULL_PTR) + +typedef struct { + UINT8 schedHarqNum; + UINT16 schedUsrId[MAX_SCHED_USER_NUM_PER_TTI]; + UINT8 schIndex; +} SchedHarqStru; + +typedef struct { + int userType; +} SchrltStru; + +typedef struct { + SchrltStru *result; +} GlobalInfoStru; + +typedef struct { + UINT8 usrNum; + SchrltStru usrList[MAX_USER_ID]; +} PeriodSchUsrListStru; + +#ifdef __cpluscplus +extern "C" { +#endif + +#ifdef __cpluscplus +} +#endif +#endif \ No newline at end of file diff --git a/test/Industry/SecB_ForwardNull.c b/test/Industry/SecB_ForwardNull.c new file mode 100644 index 0000000000..85a7723d6e --- /dev/null +++ b/test/Industry/SecB_ForwardNull.c @@ -0,0 +1,135 @@ +// 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 --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * @description 空指针解引用 验收失败 + */ +#include +#include +#include "securec.h" +typedef unsigned int UINT32; +#define MAX_SHIFT_COUNT 64 +#define OK 1 +#define ERROR 0 + +typedef struct { + UINT32 offset; +} StructInfo; + +typedef struct { + UINT32 dataLen; + char *dataBuff; +} DataInfo; + +typedef struct { + UINT32 offset; + UINT32 dataLen; + char *dataBuff; +} SendDataInfo; + +typedef struct { + StructInfo *info; +} DataInfoStruct; + +void GetDataBuffInfo(DataInfo *dataBuffInfo, UINT32 inputNum) +{ + if (inputNum != MAX_SHIFT_COUNT) { + dataBuffInfo->dataBuff = (char *)malloc(MAX_SHIFT_COUNT); + if (dataBuffInfo->dataBuff == NULL) { + return; + } + dataBuffInfo->dataLen = MAX_SHIFT_COUNT; + } +} + +void PrintBuf(SendDataInfo *dataBuffInfo) +{ + for (UINT32 i = 0; i < dataBuffInfo->dataLen; i++) { + printf("buf data: %c", *(dataBuffInfo->dataBuff + i)); + } +} + +void SaveBufInfo(DataInfo *dataBuffInfo) +{ + SendDataInfo *sendData = NULL; + sendData = (SendDataInfo *)malloc(sizeof(SendDataInfo)); + if (sendData == NULL) { + return; + } + + sendData->offset = MAX_SHIFT_COUNT; + sendData->dataBuff = dataBuffInfo->dataBuff; + sendData->dataLen = dataBuffInfo->dataLen; + PrintBuf(sendData); + free(sendData); +} + +void NotifyBuff(DataInfo *dataBuffInfo, UINT32 inputNum2) +{ + switch (inputNum2) { + case MAX_SHIFT_COUNT: + SaveBufInfo(dataBuffInfo); + break; + default: + break; + } + + if (dataBuffInfo->dataBuff != NULL) { + free(dataBuffInfo->dataBuff); + } +} + +DataInfo g_secInfo = { 0 }; +UINT32 GetSecInfo(DataInfo *secInfo) +{ + if (secInfo == NULL) { + return ERROR; + } + (void)memcpy_s(secInfo, sizeof(DataInfo), &g_secInfo, sizeof(DataInfo)); + return OK; +} + +// FN: DTS2022061984858, https://portal.edevops.huawei.com/cloudreq-obp/project/gdf7ba016c8b34e3c80a19f8796b330f9/productSpace/bugList/2028184310/addbug/2029643728 +void WB_BadCase01(DataInfoStruct *input, UINT32 flag) +{ + StructInfo *tempInfo = NULL; + + if ((input == NULL) || (flag == 1)) { + goto EXIT; + } + tempInfo = input->info; + +EXIT: + (void)memset_s(tempInfo, sizeof(StructInfo), 0, sizeof(StructInfo)); +} + +// FN: DTS2022061984853, https://portal.edevops.huawei.com/cloudreq-obp/project/gdf7ba016c8b34e3c80a19f8796b330f9/productSpace/bugList/2028184310/addbug/2029643723 +void WB_BadCase02(UINT32 inputNum1, UINT32 inputNum2) +{ + DataInfo dataBuffInfo = {0}; + + GetDataBuffInfo(&dataBuffInfo, inputNum1); + NotifyBuff(&dataBuffInfo, inputNum2); +} + +// FN: DTS2022061984852, https://portal.edevops.huawei.com/cloudreq-obp/project/gdf7ba016c8b34e3c80a19f8796b330f9/productSpace/bugList/2028184310/addbug/2029643722 +void WB_BadCase03(UINT32 *output, UINT32 *input, UINT32 flag) +{ + DataInfo secInfo = {0}; + char *data = NULL; + UINT32 ret = GetSecInfo(&secInfo); + if (ret == ERROR) { + return; + } + data = secInfo.dataBuff; + printf("data: %c", *data); +} + +void badbad(char *ptr) +{ + ptr = NULL; + *ptr = 'a'; // CHECK: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 +} diff --git a/test/Industry/SecB_ForwardNull.c.json b/test/Industry/SecB_ForwardNull.c.json new file mode 100644 index 0000000000..746f5aee1f --- /dev/null +++ b/test/Industry/SecB_ForwardNull.c.json @@ -0,0 +1,65 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/forward_null/SecB_ForwardNull.c" + }, + "region": { + "startLine": 133, + "startColumn": null + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/forward_null/SecB_ForwardNull.c" + }, + "region": { + "startLine": 134, + "startColumn": null + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/forward_null/SecB_ForwardNull.c" + }, + "region": { + "startLine": 134, + "startColumn": null + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/UseAfterFree/Double_Free_BadCase01.c b/test/Industry/UseAfterFree/Double_Free_BadCase01.c new file mode 100644 index 0000000000..34c1e71f04 --- /dev/null +++ b/test/Industry/UseAfterFree/Double_Free_BadCase01.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * + * @description 资源释放后,对应的变量应该立即赋予新值,防止后续又被重新引用 + * + * @good + * + * @bad DoubleFreeBad01 + * + * @tool Coverity:USE_AFTER_FREE;SecBrella:SecB_UseAfterFree + * + * @cwe 416 + * + * @secureCoding 《华为C语言编程规范V5.1》 3.15-G.MEM.05 不要访问已释放的内存 + * + * @author xwx356597;x00407107 + * + */ +#include +#include + +//@scene 指针释放后未置空导致双重释放 +void DoubleFreeBad01() +{ + /* POTENTIAL FLAW: 指针释放后未置空导致双重释放 */ + char *p = (char *)malloc(100); + if(p == NULL) + return; + + /* do something */ + + /* 第一次释放 */ + free(p); + + /* double free */ + free(p); // CHECK: KLEE: WARNING: 100.00% DoubleFree True Positive at trace 1 +} + +// 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 --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/UseAfterFree/Double_Free_BadCase01.c.json b/test/Industry/UseAfterFree/Double_Free_BadCase01.c.json new file mode 100644 index 0000000000..0c8cda8d0b --- /dev/null +++ b/test/Industry/UseAfterFree/Double_Free_BadCase01.c.json @@ -0,0 +1,78 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Double_Free_BadCase01.c" + }, + "region": { + "startLine": 26, + "startColumn": 23 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Double_Free_BadCase01.c" + }, + "region": { + "startLine": 33, + "startColumn": 5 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Double_Free_BadCase01.c" + }, + "region": { + "startLine": 36, + "startColumn": 5 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "DoubleFree", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Double_Free_BadCase01.c" + }, + "region": { + "startLine": 36, + "startColumn": 5 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/UseAfterFree/Double_Free_BadCase02.c b/test/Industry/UseAfterFree/Double_Free_BadCase02.c new file mode 100644 index 0000000000..fc3b7f2870 --- /dev/null +++ b/test/Industry/UseAfterFree/Double_Free_BadCase02.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * + * @description 资源释放后,对应的变量应该立即赋予新值,防止后续又被重新引用 + * + * @good + * + * @bad DoubleFreeBad02 + * + * @tool Coverity:USE_AFTER_FREE;SecBrella:SecB_UseAfterFree + * + * @cwe 416 + * + * @secureCoding 《华为C语言编程规范V5.1》 3.15-G.MEM.05 不要访问已释放的内存 + * + * @author xwx356597;x00407107 + * + */ +#include +#include + +//@scene 指针释放后未置空,有条件地再再次释放导致双重释放 +void DoubleFreeBad02(int flag) +{ + /* POTENTIAL FLAW: 指针释放后未置空导致双重释放 */ + char *p = (char *)malloc(100); + if(p == NULL) + return; + + /* do something */ + + /* 第一次释放 */ + free(p); + + if(flag == 0) + { + /* double free */ + free(p); // CHECK: KLEE: WARNING: 100.00% DoubleFree True Positive at trace 1 + } +} + +// 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 --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/UseAfterFree/Double_Free_BadCase02.c.json b/test/Industry/UseAfterFree/Double_Free_BadCase02.c.json new file mode 100644 index 0000000000..6ca107a75c --- /dev/null +++ b/test/Industry/UseAfterFree/Double_Free_BadCase02.c.json @@ -0,0 +1,91 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Double_Free_BadCase02.c" + }, + "region": { + "startLine": 26, + "startColumn": 23 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Double_Free_BadCase02.c" + }, + "region": { + "startLine": 33, + "startColumn": 5 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Double_Free_BadCase02.c" + }, + "region": { + "startLine": 35, + "startColumn": 8 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Double_Free_BadCase02.c" + }, + "region": { + "startLine": 38, + "startColumn": 9 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "DoubleFree", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Double_Free_BadCase02.c" + }, + "region": { + "startLine": 38, + "startColumn": 9 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/UseAfterFree/Free_Not_Set_Null_BadCase02.cpp b/test/Industry/UseAfterFree/Free_Not_Set_Null_BadCase02.cpp new file mode 100644 index 0000000000..39d5c81965 --- /dev/null +++ b/test/Industry/UseAfterFree/Free_Not_Set_Null_BadCase02.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2021. All rights reserved. + * + * @description 指针释放后未置空 + * + * @good + * + * @bad test_bad_02 + * + * @tool Coverity:USE_AFTER_FREE;SecBrella:SecB_UseAfterFree; + * + * @cwe 416 + * + * @secureCoding 《华为C语言编程规范V5.1》 3.15-G.MEM.05 不要访问已释放的内存 + * + * @author xwx356597;x00407107 + * + */ + +#include +#include +#include + +class STU{ +public: + STU(); + ~STU(); + +private: + char *msg; +}; + +typedef struct { + int a; + char *pname; +}Data; +//全局变量 分支结束前要重新赋值 +char *MSG = (char*)malloc(1000); + +void VOS_Free(void* p){} + +void func(void* p){} + +// @scene 析构函数中释放内存后继续使用指针变量 +STU::~STU() +{ + char *tmp; + delete msg; + /* uaf */ + tmp = msg; +} + +// @scene 指针指向的内存释放之后,直接继续使用这个指针变量 +void test_bad_02(int a) +{ + int num = 0; + char *msg; + char *tmp; + /*POTENTIAL FLAW:msg is reuse after free*/ + msg = (char*)malloc(10); + if (msg = NULL) + return; + if (a < 0) + { + free(msg); + /*POTENTIAL FLAW:msg is not set NULL after free*/ + tmp = msg; //error + return; //分支结束 + } + num = 10; +} + +int main() +{ + return 0; +} + +// 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 --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s + +// CHECK: KLEE: WARNING: 100.00% UseAfterFree False Positive at trace 1 diff --git a/test/Industry/UseAfterFree/Free_Not_Set_Null_BadCase02.cpp.json b/test/Industry/UseAfterFree/Free_Not_Set_Null_BadCase02.cpp.json new file mode 100644 index 0000000000..60a832c540 --- /dev/null +++ b/test/Industry/UseAfterFree/Free_Not_Set_Null_BadCase02.cpp.json @@ -0,0 +1,78 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Free_Not_Set_Null_BadCase02.cpp" + }, + "region": { + "startLine": 60, + "startColumn": 18 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Free_Not_Set_Null_BadCase02.cpp" + }, + "region": { + "startLine": 65, + "startColumn": 9 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Free_Not_Set_Null_BadCase02.cpp" + }, + "region": { + "startLine": 67, + "startColumn": 13 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "UseAfterFree", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Free_Not_Set_Null_BadCase02.cpp" + }, + "region": { + "startLine": 67, + "startColumn": 13 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/UseAfterFree/Prod_Dereference_After_Free_BadCase01.c b/test/Industry/UseAfterFree/Prod_Dereference_After_Free_BadCase01.c new file mode 100644 index 0000000000..ae2d3e6058 --- /dev/null +++ b/test/Industry/UseAfterFree/Prod_Dereference_After_Free_BadCase01.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2019-2022. All rights reserved. + * + * @description 资源释放后,对应的变量应该立即赋予新值,防止后续又被重新引用 + * + * @good + * + * @bad UseAfterFree + * + * @tool Coverity:USE_AFTER_FREE;SecBrella:SecB_UseAfterFree + * + * @cwe 416 + * + * @secureCoding 《华为C语言编程规范V5.1》 3.15-G.MEM.05 不要访问已释放的内存 + * + * @author x00407107 + * + */ +#include + +#define ARR_SIZE 10 +// @scene 指针释放后直接解引用 +void UseAfterFree() +{ + /* POTENTIAL FLAW: 指针释放后未置空导致双重释放 */ + int *msg = (int *)malloc(ARR_SIZE * sizeof(int)); + if (msg == NULL) { + return; + } + free(msg); + /* uaf */ + *msg = 1; // CHECK: KLEE: WARNING: 100.00% UseAfterFree True Positive at trace 1 + return; +} + +// 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 --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/UseAfterFree/Prod_Dereference_After_Free_BadCase01.c.json b/test/Industry/UseAfterFree/Prod_Dereference_After_Free_BadCase01.c.json new file mode 100644 index 0000000000..cb1df288c3 --- /dev/null +++ b/test/Industry/UseAfterFree/Prod_Dereference_After_Free_BadCase01.c.json @@ -0,0 +1,78 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Prod_Dereference_After_Free_BadCase01.c" + }, + "region": { + "startLine": 26, + "startColumn": 23 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Prod_Dereference_After_Free_BadCase01.c" + }, + "region": { + "startLine": 30, + "startColumn": 5 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Prod_Dereference_After_Free_BadCase01.c" + }, + "region": { + "startLine": 32, + "startColumn": 10 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "UseAfterFree", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/klee_verifier_scripts/tests/secb_suit/src/SecB_UseAfterFree/BadCase/Prod_Dereference_After_Free_BadCase01.c" + }, + "region": { + "startLine": 32, + "startColumn": 10 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/ZeroDeref_Scene_BadCase02.c b/test/Industry/ZeroDeref_Scene_BadCase02.c new file mode 100644 index 0000000000..f8fc065d05 --- /dev/null +++ b/test/Industry/ZeroDeref_Scene_BadCase02.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * @description FORWARD_NULL, 如果字符串或者指针作为函数参数,为了防止空指针引用错误,在引用前必须确保该参数不为NULL, + * 如果上层调用者已经保证了该参数不可能为NULL,在调用本函数时,在函数开始处可以加ASSERT进行校验。 + * + * @bad TestBad9; + * + * @CWE 476; + * + * @secureCoding + * + * @csec + * + * @tool Coverity:FORWARD_NULL;SecBrella:SecB_ForwardNull; + * + * @author xwx356597; m00489032 + * + */ + +#include +#include +#include +#include "securec.h" + +#define BUFFERSIZE 100 + +char pDest[BUFFERSIZE] = {0}; + +/* + * @mainScene 针对NULL常量的检查,需要开启coverity扫描参数FORWARD_NULL:deref_zero_errors:true + * @subScene 危险函数中直接使用NULL常量 + */ +void TestBad9() +{ + char *p = NULL; + /* POTENTIAL FLAW: var_deref_model - 在该函数中对可能为null的指针进行解引用 */ + memcpy(pDest, p, BUFFERSIZE); // CHECK-DAG: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 +} + +// 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 --check-out-of-memory --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/ZeroDeref_Scene_BadCase02.c.json b/test/Industry/ZeroDeref_Scene_BadCase02.c.json new file mode 100644 index 0000000000..ffa8cbb0f2 --- /dev/null +++ b/test/Industry/ZeroDeref_Scene_BadCase02.c.json @@ -0,0 +1,78 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/ZeroDeref_Scene_BadCase02.c" + }, + "region": { + "startLine": 36, + "startColumn": 11 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/ZeroDeref_Scene_BadCase02.c" + }, + "region": { + "startLine": 38, + "startColumn": 19 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/ZeroDeref_Scene_BadCase02.c" + }, + "region": { + "startLine": 38, + "startColumn": 5 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/ZeroDeref_Scene_BadCase02.c" + }, + "region": { + "startLine": 38, + "startColumn": 5 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/ZeroDeref_Scene_BadCase05.c b/test/Industry/ZeroDeref_Scene_BadCase05.c new file mode 100644 index 0000000000..97f2edfab1 --- /dev/null +++ b/test/Industry/ZeroDeref_Scene_BadCase05.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved. + * + * @description FORWARD_NULL, 如果字符串或者指针作为函数参数,为了防止空指针引用错误,在引用前必须确保该参数不为NULL, + * 如果上层调用者已经保证了该参数不可能为NULL,在调用本函数时,在函数开始处可以加ASSERT进行校验。 + * + * @bad TestBad18; + * + * @CWE 476; + * + * @secureCoding + * + * @csec + * + * @tool Coverity:FORWARD_NULL;SecBrella:SecB_ForwardNull; + * + * @author xwx356597; m00489032 + * + */ + +#include +#include +#include +#include "securec.h" + +struct TEACHER { + unsigned int id; + char *name; +}; + +struct STU { + unsigned int id; + char *name; + struct TEACHER *teacher; +}; + +/* + * @mainScene 开始事件:入参传入NULL + * @subScene 函数内在不同的条件下对该参数解引用 + */ +struct STU *HelpBadTest2(); + +void HelpBadTest1(struct STU *stu) +{ + struct STU *localStu = HelpBadTest2(); + if (localStu == NULL) { + localStu = stu; + // sink: deref + localStu->id; + } + // sink: deref + localStu->id; + if (localStu == NULL) { + return; + } + unsigned int id = localStu->id; +} + +void TestBad18(struct STU *stu) +{ + // POTENTIAL FLAW: transfer NULL + HelpBadTest1(NULL); +} + +// 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 --check-out-of-memory --mock-external-calls --libc=klee --external-calls=all --skip-not-symbolic-objects --skip-not-lazy-initialized --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 2 +// CHECK: KLEE: WARNING: 100.00% NullPointerException False Positive at trace 1 \ No newline at end of file diff --git a/test/Industry/ZeroDeref_Scene_BadCase05.c.json b/test/Industry/ZeroDeref_Scene_BadCase05.c.json new file mode 100644 index 0000000000..856f07102a --- /dev/null +++ b/test/Industry/ZeroDeref_Scene_BadCase05.c.json @@ -0,0 +1,117 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/ZeroDeref_Scene_BadCase05.c" + }, + "region": { + "startLine": 62, + "startColumn": 2 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/ZeroDeref_Scene_BadCase05.c" + }, + "region": { + "startLine": 52, + "startColumn": 2 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/ZeroDeref_Scene_BadCase05.c" + }, + "region": { + "startLine": 52, + "startColumn": 2 + } + } + } + ] + }, + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/ZeroDeref_Scene_BadCase05.c" + }, + "region": { + "startLine": 62, + "startColumn": 2 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/ZeroDeref_Scene_BadCase05.c" + }, + "region": { + "startLine": 49, + "startColumn": 13 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/dev/klee-test/src-bc-reports/sources/secbrella/SecB_ForwardNull/BadCase/ZeroDeref_Scene_BadCase05.c" + }, + "region": { + "startLine": 49, + "startColumn": 13 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/base_type.h b/test/Industry/base_type.h new file mode 100644 index 0000000000..64b5e87e31 --- /dev/null +++ b/test/Industry/base_type.h @@ -0,0 +1,47 @@ +#ifndef __BASE_TYPE_H_ +#define __BASE_TYPE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef signed char INT8; +typedef unsigned char UINT8; +typedef signed short INT16; +typedef unsigned short UINT16; +typedef signed int INT32; +typedef unsigned int UINT32; +typedef signed long long INT64; +typedef unsigned long long UINT64; +typedef signed long LONG; +typedef unsigned long ULONG; +typedef void VOID; +typedef float FLOAT; +typedef double DOUBLE; +typedef long double LDOUBLE; +#if defined(_WIN64) || defined(WIN64) || defined(__LP64__) || defined(_LP64) +typedef INT64 SSIZE_T; +typedef UINT64 SIZE_T; +#else +typedef INT32 SSIZE_T; +typedef UINT32 SIZE_T; +#endif + +typedef struct TLV{ + UINT32 type; + UINT32 length; + UINT8 data[0]; +}TLV; + +typedef struct LV{ + UINT32 length; + UINT8 data[0]; +}LV; + + + + +#ifdef __cplusplus +} +#endif +#endif //__BASE_TYPE_H_ \ No newline at end of file diff --git a/test/Industry/fn_reverse_null.c b/test/Industry/fn_reverse_null.c new file mode 100644 index 0000000000..7642a916f6 --- /dev/null +++ b/test/Industry/fn_reverse_null.c @@ -0,0 +1,60 @@ +#define NULL 0 +#define OK 0 +#define ERR 1 +typedef struct Tree { + struct Tree *next; + int value; +} TreeNode; +extern void BusinessFunc(void); +extern int CheckValid(int value); + +// case1: 普通的先解引用后判空场景 +void TestErr1(TreeNode *stru) +{ + stru->value = 0; + if (stru == NULL) { // CHECK-DAG: KLEE: WARNING: 100.00% NullCheckAfterDerefException True Positive at trace 3 + return; + } +} + +void TestErr2(TreeNode *stru, int *flag) +{ + if (CheckValid(stru->value) != OK) { + return; + } + if (flag == NULL || stru == NULL) { // CHECK-DAG: KLEE: WARNING: 100.00% NullCheckAfterDerefException True Positive at trace 1 + return; + } + BusinessFunc(); + return; +} + +// case2: 逻辑运算中,先解引用后判空 + +int TestErr3(int *arr) +{ + if (arr[0] == 0 || arr == NULL) { // CHECK-DAG: KLEE: WARNING: 100.00% NullCheckAfterDerefException True Positive at trace 2 + return ERR; + } + return OK; +} + +// case3: for循环头,初始语句中解引用,循环条件语句中判空 +void TestErr4(TreeNode *head) +{ + TreeNode *node = NULL; + TreeNode *nextNode = NULL; + if (head == NULL) { + return; + } + for (node = head->next, nextNode =node->next; node != NULL; node = nextNode, nextNode = node->next) { + BusinessFunc(); + } + return; +} + +// 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 --external-calls=all --mock-external-calls --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --check-out-of-memory --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/fn_reverse_null.c.json b/test/Industry/fn_reverse_null.c.json new file mode 100644 index 0000000000..4d1b569c45 --- /dev/null +++ b/test/Industry/fn_reverse_null.c.json @@ -0,0 +1,169 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/reverse_null/fn_reverse_null.c" + }, + "region": { + "startLine": 22, + "startColumn": 26 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/reverse_null/fn_reverse_null.c" + }, + "region": { + "startLine": 25, + "startColumn": 30 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "CheckAfterDeref", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/reverse_null/fn_reverse_null.c" + }, + "region": { + "startLine": 25, + "startColumn": 30 + } + } + } + ] + }, + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/reverse_null/fn_reverse_null.c" + }, + "region": { + "startLine": 36, + "startColumn": 9 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/reverse_null/fn_reverse_null.c" + }, + "region": { + "startLine": 36, + "startColumn": 28 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "CheckAfterDeref", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/reverse_null/fn_reverse_null.c" + }, + "region": { + "startLine": 36, + "startColumn": 28 + } + } + } + ] + }, + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/reverse_null/fn_reverse_null.c" + }, + "region": { + "startLine": 14, + "startColumn": 17 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/reverse_null/fn_reverse_null.c" + }, + "region": { + "startLine": 15, + "startColumn": 14 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "CheckAfterDeref", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/reverse_null/fn_reverse_null.c" + }, + "region": { + "startLine": 15, + "startColumn": 14 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/fp_forward_null_address.c b/test/Industry/fp_forward_null_address.c new file mode 100644 index 0000000000..5caf953777 --- /dev/null +++ b/test/Industry/fp_forward_null_address.c @@ -0,0 +1,29 @@ +#include + +#define MAX_LENGTH 100 +typedef unsigned int U32; + +int g_arr[MAX_LENGTH]; + +void foo() +{ + int *p = NULL; + U32 loopCntMax = MAX_LENGTH; + U32 i; + for (i = 0; i < loopCntMax; i++) { + if (g_arr[i] > 10) { + p = &g_arr[i]; + break; + } + } + if (i >= loopCntMax) { + return; + } + int v = *p; // CHECK: KLEE: WARNING: 100.00% NullPointerException False Positive at trace 1 +} + +// 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 --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/fp_forward_null_address.c.json b/test/Industry/fp_forward_null_address.c.json new file mode 100644 index 0000000000..40b989ed52 --- /dev/null +++ b/test/Industry/fp_forward_null_address.c.json @@ -0,0 +1,65 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/forward_null/fp_forward_null_address.c" + }, + "region": { + "startLine": 10, + "startColumn": null + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/forward_null/fp_forward_null_address.c" + }, + "region": { + "startLine": 22, + "startColumn": null + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/forward_null/fp_forward_null_address.c" + }, + "region": { + "startLine": 22, + "startColumn": null + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/fp_null_returns_self_define.c b/test/Industry/fp_null_returns_self_define.c new file mode 100644 index 0000000000..a3214ca17d --- /dev/null +++ b/test/Industry/fp_null_returns_self_define.c @@ -0,0 +1,26 @@ +// 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 --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s +#include + +unsigned int *g_status; +unsigned int *MyValue(unsigned short index) +{ + if (index > 5) { + return NULL; // (1) + } + return g_status; +} + +void TEST_NullReturns004(unsigned short index) +{ + if (index > 5) { + return; + } + unsigned int *value = MyValue(index); // (2) + if (*value == 0) { /* expect NULL_RETURNS */ // CHECK: KLEE: WARNING: 100.00% NullPointerException False Positive at trace 1 + return; + } +} diff --git a/test/Industry/fp_null_returns_self_define.c.json b/test/Industry/fp_null_returns_self_define.c.json new file mode 100644 index 0000000000..7623c3a49f --- /dev/null +++ b/test/Industry/fp_null_returns_self_define.c.json @@ -0,0 +1,78 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/fp_null_returns_self_define.c" + }, + "region": { + "startLine": 22, + "startColumn": null + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/fp_null_returns_self_define.c" + }, + "region": { + "startLine": 12, + "startColumn": null + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/fp_null_returns_self_define.c" + }, + "region": { + "startLine": 23, + "startColumn": null + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/null_returns/fp_null_returns_self_define.c" + }, + "region": { + "startLine": 23, + "startColumn": null + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/fp_null_returns_self_define2.c b/test/Industry/fp_null_returns_self_define2.c new file mode 100644 index 0000000000..af179795d9 --- /dev/null +++ b/test/Industry/fp_null_returns_self_define2.c @@ -0,0 +1,33 @@ +#include + + +unsigned int *g_status; +unsigned int *MyValue(unsigned short index) +{ + if (index > 5) { + return NULL; // (1) + } + return g_status; +} + +void sink(unsigned int *arg) +{ + if (*arg== 0) { /* expect NULL_RETURNS */ // CHECK: KLEE: WARNING: 100.00% NullPointerException False Positive at trace 1 + return; + } +} + +void TEST_NullReturns004(unsigned short index) +{ + if (index > 5) { + return; + } + unsigned int *value = MyValue(index); // (2) + sink(value); +} + +// 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 --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s diff --git a/test/Industry/fp_null_returns_self_define2.c.json b/test/Industry/fp_null_returns_self_define2.c.json new file mode 100644 index 0000000000..dc0a8d19cd --- /dev/null +++ b/test/Industry/fp_null_returns_self_define2.c.json @@ -0,0 +1,91 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/root/test/fp_null_returns_self_define2.c" + }, + "region": { + "startLine": 25, + "startColumn": null + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/root/test/fp_null_returns_self_define2.c" + }, + "region": { + "startLine": 8, + "startColumn": null + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/root/test/fp_null_returns_self_define2.c" + }, + "region": { + "startLine": 26, + "startColumn": null + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/root/test/fp_null_returns_self_define2.c" + }, + "region": { + "startLine": 15, + "startColumn": null + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/root/test/fp_null_returns_self_define2.c" + }, + "region": { + "startLine": 15, + "startColumn": null + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/if2.c b/test/Industry/if2.c new file mode 100644 index 0000000000..049f744b05 --- /dev/null +++ b/test/Industry/if2.c @@ -0,0 +1,16 @@ +int main(int x) { + int *p = 0; + if (x) { + p = &x; + } else { + klee_sleep(); + } + return *p; +} + +// 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 --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 diff --git a/test/Industry/if2.c.json b/test/Industry/if2.c.json new file mode 100644 index 0000000000..c744be5574 --- /dev/null +++ b/test/Industry/if2.c.json @@ -0,0 +1,65 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "if2.c" + }, + "region": { + "startLine": 2, + "startColumn": 8 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "if2.c" + }, + "region": { + "startLine": 8, + "startColumn": 10 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "if2.c" + }, + "region": { + "startLine": 8, + "startColumn": 10 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/securec.h b/test/Industry/securec.h new file mode 100644 index 0000000000..801601a56b --- /dev/null +++ b/test/Industry/securec.h @@ -0,0 +1,289 @@ +/******************************************************************************* +* Copyright @ Huawei Technologies Co., Ltd. 1998-2014. All rights reserved. +* File name: securec.h +* Decription: +* the user of this secure c library should include this header file +* in you source code. This header file declare all supported API +* prototype of the library, such as memcpy_s, strcpy_s, wcscpy_s, +* strcat_s, strncat_s, sprintf_s, scanf_s, and so on. +* History: +* 1. Date: +* Author: +* Modification: +******************************************************************************** +*/ + +#ifndef __SECUREC_H__5D13A042_DC3F_4ED9_A8D1_882811274C27 +#define __SECUREC_H__5D13A042_DC3F_4ED9_A8D1_882811274C27 + +/* If you need high performance, enable the WITH_PERFORMANCE_ADDONS macro! */ +#define WITH_PERFORMANCE_ADDONS + +#include "securectype.h" /*lint !e537*/ +#include + +/* If stack size on some embedded platform is limited, you can define the following macro +* which will put some variables on heap instead of stack. +#define STACK_SIZE_LESS_THAN_1K +*/ + +/* for performance consideration, the following macro will call the corresponding API +* of libC for memcpy, memmove and memset +*/ +#define CALL_LIBC_COR_API + +/* codes should run under the macro COMPATIBLE_LINUX_FORMAT in unknow system on default, + and strtold. The function + strtold is referenced first at ISO9899:1999(C99), and some old compilers can + not support these functions. Here provides a macro to open these functions: + + SECUREC_SUPPORT_STRTOLD -- if defined, strtold will be used +*/ + +/*define error code*/ +#ifndef errno_t +typedef int errno_t; +#endif + +/* success */ +#define EOK (0) + +/* invalid parameter */ +#ifdef EINVAL +#undef EINVAL +#endif +#define EINVAL (22) +#define EINVAL_AND_RESET (22 | 0X80) +/* invalid parameter range */ +#ifdef ERANGE +#undef ERANGE /* to avoid redefinition */ +#endif +#define ERANGE (34) +#define ERANGE_AND_RESET (34 | 0X80) + +#ifdef EOVERLAP_AND_RESET +#undef EOVERLAP_AND_RESET +#endif +/*Once the buffer overlap is detected, the dest buffer must be reseted! */ +#define EOVERLAP_AND_RESET (54 | 0X80) + +/*if you need export the function of this library in Win32 dll, use __declspec(dllexport) */ + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + + /* return SecureC Version */ + void getHwSecureCVersion(char* verStr, int bufSize, unsigned short* verNumber); + + /* wmemcpy */ + errno_t wmemcpy_s(wchar_t* dest, size_t destMax, const wchar_t* src, size_t count); + + /* memmove */ + errno_t memmove_s(void* dest, size_t destMax, const void* src, size_t count); + + errno_t wmemmove_s(wchar_t* dest, size_t destMax, const wchar_t* src, size_t count); + + errno_t wcscpy_s(wchar_t* strDest, size_t destMax, const wchar_t* strSrc); + + errno_t wcsncpy_s(wchar_t* strDest, size_t destMax, const wchar_t* strSrc, size_t count); + + errno_t wcscat_s(wchar_t* strDest, size_t destMax, const wchar_t* strSrc); + + errno_t wcsncat_s(wchar_t* strDest, size_t destMax, const wchar_t* strSrc, size_t count); + + /* strtok */ + char* strtok_s(char* strToken, const char* strDelimit, char** context); + + wchar_t* wcstok_s(wchar_t* strToken, const wchar_t* strDelimit, wchar_t** context); + + /* sprintf */ + int sprintf_s(char* strDest, size_t destMax, const char* format, ...) SECUREC_ATTRIBUTE(3,4); + + int swprintf_s(wchar_t* strDest, size_t destMax, const wchar_t* format, ...); + + /* vsprintf */ + int vsprintf_s(char* strDest, size_t destMax, const char* format, va_list argptr) SECUREC_ATTRIBUTE(3,0); + + int vswprintf_s(wchar_t* strDest, size_t destMax, const wchar_t* format, va_list argptr); + + int vsnprintf_s(char* strDest, size_t destMax, size_t count, const char* format, va_list arglist) SECUREC_ATTRIBUTE(4,0); + + /* snprintf */ + int snprintf_s(char* strDest, size_t destMax, size_t count, const char* format, ...) SECUREC_ATTRIBUTE(4,5); + + /* scanf */ + int scanf_s(const char* format, ...); + + int wscanf_s(const wchar_t* format, ...); + + /* vscanf */ + int vscanf_s(const char* format, va_list arglist); + + int vwscanf_s(const wchar_t* format, va_list arglist); + + /* fscanf */ + int fscanf_s(FILE* stream, const char* format, ...); + + int fwscanf_s(FILE* stream, const wchar_t* format, ...); + + /* vfscanf */ + int vfscanf_s(FILE* stream, const char* format, va_list arglist); + + int vfwscanf_s(FILE* stream, const wchar_t* format, va_list arglist); + + /* sscanf */ + int sscanf_s(const char* buffer, const char* format, ...); + + int swscanf_s(const wchar_t* buffer, const wchar_t* format, ...); + + /* vsscanf */ + int vsscanf_s(const char* buffer, const char* format, va_list argptr); + + int vswscanf_s(const wchar_t* buffer, const wchar_t* format, va_list arglist); + + /* gets */ + char* gets_s(char* buffer, size_t destMax); + + /* memset function*/ + errno_t memset_s(void* dest, size_t destMax, int c, size_t count); + /* memcpy function*/ + errno_t memcpy_s(void* dest, size_t destMax, const void* src, size_t count); + + /* strcpy */ + errno_t strcpy_s(char* strDest, size_t destMax, const char* strSrc); + /* strncpy */ + errno_t strncpy_s(char* strDest, size_t destMax, const char* strSrc, size_t count); + + /* strcat */ + errno_t strcat_s(char* strDest, size_t destMax, const char* strSrc); + /* strncat */ + errno_t strncat_s(char* strDest, size_t destMax, const char* strSrc, size_t count); + + errno_t strncpy_error(char* strDest, size_t destMax, const char* strSrc, size_t count); + errno_t strcpy_error(char* strDest, size_t destMax, const char* strSrc); + +#if defined(WITH_PERFORMANCE_ADDONS) + /* those functions are used by macro */ + errno_t memset_sOptAsm(void* dest, size_t destMax, int c, size_t count); + errno_t memset_sOptTc(void* dest, size_t destMax, int c, size_t count); + errno_t memcpy_sOptAsm(void* dest, size_t destMax, const void* src, size_t count); + errno_t memcpy_sOptTc(void* dest, size_t destMax, const void* src, size_t count); + + /* strcpy_sp is a macro, NOT a function in performance optimization mode. */ +#define strcpy_sp(dest, destMax, src) /*lint -save -e506 -e1055 -e650 -e778 -e802 */ (( __builtin_constant_p((destMax)) && __builtin_constant_p((src))) ? \ + STRCPY_SM((dest), (destMax), (src)) : strcpy_s((dest), (destMax), (src)) ) /*lint -restore */ + + /* strncpy_sp is a macro, NOT a function in performance optimization mode. */ +#define strncpy_sp(dest, destMax, src, count) /*lint -save -e506 -e1055 -e666 -e650 -e778 -e802 */ ((__builtin_constant_p((count)) && __builtin_constant_p((destMax)) && __builtin_constant_p((src))) ? \ + STRNCPY_SM((dest), (destMax), (src), (count)) : strncpy_s((dest), (destMax), (src), (count)) ) /*lint -restore */ + + /* strcat_sp is a macro, NOT a function in performance optimization mode. */ +#define strcat_sp(dest, destMax, src) /*lint -save -e506 -e1055 -e650 -e778 -e802 */ (( __builtin_constant_p((destMax)) && __builtin_constant_p((src))) ? \ + STRCAT_SM((dest), (destMax), (src)) : strcat_s((dest), (destMax), (src)) ) /*lint -restore */ + + /* strncat_sp is a macro, NOT a function in performance optimization mode. */ +#define strncat_sp(dest, destMax, src, count) /*lint -save -e506 -e1055 -e666 -e650 -e778 -e802 */ ((__builtin_constant_p((count)) && __builtin_constant_p((destMax)) && __builtin_constant_p((src))) ? \ + STRNCAT_SM((dest), (destMax), (src), (count)) : strncat_s((dest), (destMax), (src), (count)) ) /*lint -restore */ + + /* memcpy_sp is a macro, NOT a function in performance optimization mode. */ +#define memcpy_sp(dest, destMax, src, count) /*lint -save -e506 -e1055 -e650 -e778 -e802 */ (__builtin_constant_p((count)) ? (MEMCPY_SM((dest), (destMax), (src), (count))) : \ + (__builtin_constant_p((destMax)) ? (((size_t)(destMax) > 0 && (((UINT64T)(destMax) & (UINT64T)(-2)) < SECUREC_MEM_MAX_LEN)) ? memcpy_sOptTc((dest), (destMax), (src), (count)) : ERANGE ) : memcpy_sOptAsm((dest), (destMax), (src), (count)))) /*lint -restore */ + + /* memset_sp is a macro, NOT a function in performance optimization mode. */ +#define memset_sp(dest, destMax, c, count) /*lint -save -e506 -e1055 -e650 -e778 -e802 */ (__builtin_constant_p((count)) ? (MEMSET_SM((dest), (destMax), (c), (count))) : \ + (__builtin_constant_p((destMax)) ? (((size_t)(destMax) > 0 && (((UINT64T)(destMax) & (UINT64T)(-2)) < SECUREC_MEM_MAX_LEN)) ? memset_sOptTc((dest), (destMax), (c), (count)) : ERANGE ) : memset_sOptAsm((dest), (destMax), (c), (count)))) /*lint -restore */ + +#endif + + + /************************/ + /*��ȫ�ļ��������������*/ + /************************/ + /************************************************************************** + * �������ƣ� fscanf_s_op + * ���������� �Ӱ�ȫ�ļ����ж�ȡ��ʽ��������Ϣ + * ����˵���� (IN) + * pSecStr ��ȫ�ļ��� + * format ��ʽ���ַ��� + * ... + * (OUT) + * + * �� �� ֵ�� �ɹ���ȡ��ʽ�����ݵĸ��������Ϊ�����������쳣 + * ����˵���� + **************************************************************************/ + int fscanf_s_op(SEC_FILE_STREAM *pfStr, const char* format, ...); + + /************************************************************************** + * �������ƣ� fwscanf_s_sp + * ���������� �Ӱ�ȫ�ļ����ж�ȡ��ʽ��������Ϣ��Unicode�ַ��� + * ����˵���� (IN) + * pSecStr ��ȫ�ļ��� + * format ��ʽ���ַ��� + * ... + * (OUT) + * + * �� �� ֵ�� �ɹ���ȡ��ʽ�����ݵĸ��������Ϊ�����������쳣 + * ����˵���� + **************************************************************************/ + int fwscanf_s_op(SEC_FILE_STREAM *pfStr, const char* format, ...); + + /************************************************************************** + * �������ƣ� sec_fopen_op + * ���������� �򿪰�ȫ�ļ��� + * ����˵���� (IN) + * pfilePath �ļ����� + * (OUT) + * + * �� �� ֵ�� ��ȫ�ļ������������ʧ�ܣ�����NULL + * ����˵���� ��ȫ�ļ���ֻ��Ϊֻ�����޷��������д������ + * ����ʾ���� + **************************************** + * int a; + * SEC_FILE_STREAM *file = sec_fopen_op("t.txt"); + * fscanf_s_op(file, "%d", &a); + * sec_fclose_op(file); + * file = NULL; + **************************************************************************/ + SEC_FILE_STREAM *sec_fopen_op(char *pfilePath); + + + /************************************************************************** + * �������ƣ� sec_fopen_op_ex + * ���������� �����ļ���������ȫ�ļ��� + * ����˵���� (IN) + fp �ļ����� + * (OUT) + * + * �� �� ֵ�� ��ȫ�ļ��������������ʧ�ܣ�����NULL + * ����˵���� + * �޸���ʷ�� + 1������; + **************************************************************************/ + SEC_FILE_STREAM *sec_fopen_op_ex(FILE *fp); + + + /************************************************************************** + * �������ƣ� sec_fclose_op + * ���������� �ͷŰ�ȫ�ļ������� + * ����˵���� (IN) + * pSecStr ��ȫ�ļ��� + * (OUT) + * + * �� �� ֵ�� �� + * ����˵���� + **************************************************************************/ + void sec_fclose_op(SEC_FILE_STREAM *pfStr); + void sec_fclose_op_ex(SEC_FILE_STREAM *pfStr); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif/* __SECUREC_H__5D13A042_DC3F_4ED9_A8D1_882811274C27 */ + + diff --git a/test/Industry/securectype.h b/test/Industry/securectype.h new file mode 100644 index 0000000000..0eff7bf50f --- /dev/null +++ b/test/Industry/securectype.h @@ -0,0 +1,305 @@ +/******************************************************************************* +* Copyright @ Huawei Technologies Co., Ltd. 1998-2014. All rights reserved. +* File name: securectype.h +* Decription: +* define internal used macro and data type. The marco of SECUREC_ON_64BITS +* will be determined in this header file, which is a switch for part +* of code. Some macro are used to supress warning by MS compiler. +*Note: +* user can change the value of SECUREC_STRING_MAX_LEN and SECUREC_MEM_MAX_LEN +* macro to meet their special need. +* History: +******************************************************************************** +*/ + +#ifndef __SECURECTYPE_H__A7BBB686_AADA_451B_B9F9_44DACDAE18A7 +#define __SECURECTYPE_H__A7BBB686_AADA_451B_B9F9_44DACDAE18A7 + +/*Shielding VC symbol redefinition warning*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1400) +#ifdef __STDC_WANT_SECURE_LIB__ + #undef __STDC_WANT_SECURE_LIB__ +#endif + #define __STDC_WANT_SECURE_LIB__ 0 +#ifdef _CRTIMP_ALTERNATIVE + #undef _CRTIMP_ALTERNATIVE +#endif + #define _CRTIMP_ALTERNATIVE //comment microsoft *_s function +#endif + +#include +#include +#include +/* #include this file is used to define some macros, such as INT_MAX and SIZE_MAX */ + +/*if enable COMPATIBLE_WIN_FORMAT, the output format will be compatible to Windows.*/ +#if (defined(_WIN32) || defined(_WIN64) || defined(_MSC_VER)) +#define COMPATIBLE_WIN_FORMAT +#endif +#if defined(COMPATIBLE_WIN_FORMAT) +/* in windows platform, can't use optimized function for there is no __builtin_constant_p like function */ +/* If need optimized macro, can define this: #define __builtin_constant_p(x) 1 */ +#ifdef WITH_PERFORMANCE_ADDONS +#undef WITH_PERFORMANCE_ADDONS +#endif +#endif + +#if (defined(__VXWORKS__) || defined(__vxworks) || defined(__VXWORKS) || defined(_VXWORKS_PLATFORM_) || defined(SECUREC_VXWORKS_VERSION_5_4)) +#if !defined(SECUREC_VXWORKS_PLATFORM) +#define SECUREC_VXWORKS_PLATFORM +#endif +#endif + +#ifdef SECUREC_VXWORKS_PLATFORM +#include +#endif + +/*if enable COMPATIBLE_LINUX_FORMAT, the output format will be compatible to Linux.*/ +#if !(defined(COMPATIBLE_WIN_FORMAT) || defined(SECUREC_VXWORKS_PLATFORM)) +#define COMPATIBLE_LINUX_FORMAT +#endif +#ifdef COMPATIBLE_LINUX_FORMAT +#include +#endif + +#if defined(__GNUC__) && !defined(WIN32) && !defined(SECUREC_PCLINT) +#define SECUREC_ATTRIBUTE(x,y) __attribute__((format(printf, (x), (y) ))) +#else +#define SECUREC_ATTRIBUTE(x,y) +#endif + +#if defined(__GNUC__) && ((__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3 /*above 3.4*/ )) ) + long __builtin_expect(long exp, long c); +#define LIKELY(x) __builtin_expect(!!(x), 1) +#define UNLIKELY(x) __builtin_expect(!!(x), 0) +#else +#define LIKELY(x) (x) +#define UNLIKELY(x) (x) +#endif + +#ifndef TWO_MIN +#define TWO_MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#define WCHAR_SIZE sizeof(wchar_t) + +/*ref //sourceforge.net/p/predef/wiki/OperatingSystems/ +#if !(defined(__hpux) || defined(_AIX) || defined(__VXWORKS__) || defined(__vxworks) ||defined(__ANDROID__) || defined(__WRLINUX__)|| defined(_TYPE_uint8_t)) +typedef unsigned char unit8_t; +#endif +*/ +typedef signed char INT8T; +typedef unsigned char UINT8T; + +#if defined(COMPATIBLE_WIN_FORMAT) || defined(__ARMCC_VERSION) +typedef __int64 INT64T; +typedef unsigned __int64 UINT64T; +#if defined(__ARMCC_VERSION) +typedef int INT32T; +typedef unsigned int UINT32T; +#else +typedef __int32 INT32T; +typedef unsigned __int32 UINT32T; +#endif +#else +typedef int INT32T; +typedef unsigned int UINT32T; +typedef long long INT64T; +typedef unsigned long long UINT64T; +#endif + +/*ȫļ*/ +typedef struct +{ + int _cnt; /* the size of buffered string in bytes*/ + char* _ptr; /* the pointer to next read position */ + char* _base; /*the pointer to the header of buffered string*/ + int _flag; /* mark the properties of input stream*/ + FILE* pf; /*the file pointer*/ + int fileRealRead; + long oriFilePos; /*the original position of file offset when fscanf is called*/ + unsigned int lastChar; /*the char code of last input*/ + int fUnget; /*the boolean flag of pushing a char back to read stream*/ +} SEC_FILE_STREAM; + +/*ȫļʼ*/ +#define INIT_SEC_FILE_STREAM {0, NULL, NULL, 0, NULL, 0, 0, 0, 0 } + +/* define the max length of the string */ +#define SECUREC_STRING_MAX_LEN (0x7fffffffUL) +#define SECUREC_WCHAR_STRING_MAX_LEN (SECUREC_STRING_MAX_LEN / WCHAR_SIZE) + +/* add SECUREC_MEM_MAX_LEN for memcpy and memmove*/ +#define SECUREC_MEM_MAX_LEN (0x7fffffffUL) +#define SECUREC_WCHAR_MEM_MAX_LEN (SECUREC_MEM_MAX_LEN / WCHAR_SIZE) + +#if SECUREC_STRING_MAX_LEN > 0x7fffffff +#error "max string is 2G, or you may remove this macro" +#endif + +#if (defined(__GNUC__ ) && defined(__SIZEOF_POINTER__ )) +#if (__SIZEOF_POINTER__ != 4) && (__SIZEOF_POINTER__ != 8) +#error "unsupported system, contact Security Design Technology Department of 2012 Labs" +#endif +#endif + +#define IN_REGISTER register + +#define SECC_MALLOC(x) malloc((size_t)(x)) +#define SECC_FREE(x) free((void *)(x)) + +#if defined(_WIN64) || defined(WIN64) || defined(__LP64__) || defined(_LP64) +#define SECUREC_ON_64BITS +#endif + +#if (!defined(SECUREC_ON_64BITS) && defined(__GNUC__ ) && defined(__SIZEOF_POINTER__ )) +#if __SIZEOF_POINTER__ == 8 +#define SECUREC_ON_64BITS +#endif +#endif + +#if defined(__SVR4) || defined(__svr4__) +#define __SOLARIS +#endif + +#if (defined(__hpux) || defined(_AIX) || defined(__SOLARIS)) +#define __UNIX +#endif + +#if ((!defined(SECUREC_SUPPORT_STRTOLD)) && defined(COMPATIBLE_LINUX_FORMAT)) +#if defined(__USE_ISOC99) \ + || (defined(_AIX) && defined(_ISOC99_SOURCE)) \ + || (defined(__hpux) && defined(__ia64)) \ + || (defined(__SOLARIS) && (!defined(_STRICT_STDC) && !defined(__XOPEN_OR_POSIX)) || defined(_STDC_C99) || defined(__EXTENSIONS__)) +#define SECUREC_SUPPORT_STRTOLD +#endif +#endif +#if ((defined(SECUREC_WRLINUX_BELOW4) || defined(_WRLINUX_BELOW4_)) && defined(SECUREC_SUPPORT_STRTOLD)) +#undef SECUREC_SUPPORT_STRTOLD +#endif + +#if defined(WITH_PERFORMANCE_ADDONS) + /* for strncpy_s performance optimization */ +#define STRNCPY_SM(dest, destMax, src, count) \ + ((NULL != (void*)dest && NULL != (void*)src && (size_t)destMax >0 && (((UINT64T)(destMax) & (UINT64T)(-2)) < SECUREC_STRING_MAX_LEN) && (TWO_MIN(count , strlen(src)) + 1) <= (size_t)destMax ) ? ( (count < strlen(src))? (memcpy(dest, src, count), *((char*)dest + count) = '\0', EOK) :( memcpy(dest, src, strlen(src) + 1), EOK ) ) :(strncpy_error(dest, destMax, src, count)) ) + +#define STRCPY_SM(dest, destMax, src) \ + (( NULL != (void*)dest && NULL != (void*)src && (size_t)destMax >0 && (((UINT64T)(destMax) & (UINT64T)(-2)) < SECUREC_STRING_MAX_LEN) && ( strlen(src) + 1) <= (size_t)destMax )? (memcpy(dest, src, strlen(src) + 1), EOK) :( strcpy_error(dest, destMax, src))) + + /* for strcat_s performance optimization */ +#if defined(__GNUC__) +#define STRCAT_SM(dest, destMax, src) \ + ({ int catRet =EOK;\ + if ( NULL != (void*)dest && NULL != (void*)src && (size_t)(destMax) >0 && (((UINT64T)(destMax) & (UINT64T)(-2)) < SECUREC_STRING_MAX_LEN) ) {\ + char* pCatTmpDst = (dest);\ + size_t catRestSz = (destMax);\ + do{\ + while(catRestSz > 0 && *pCatTmpDst) {\ + ++pCatTmpDst;\ + --catRestSz;\ + }\ + if (catRestSz == 0) {\ + catRet = EINVAL;\ + break;\ + }\ + if ( ( strlen(src) + 1) <= catRestSz ) {\ + memcpy(pCatTmpDst, (src), strlen(src) + 1);\ + catRet = EOK;\ + }else{\ + catRet = ERANGE;\ + }\ + }while(0);\ + if ( EOK != catRet) catRet = strcat_s((dest), (destMax), (src));\ + }else{\ + catRet = strcat_s((dest), (destMax), (src));\ + }\ + catRet;}) +#else +#define STRCAT_SM(dest, destMax, src) strcat_s(dest, destMax, src) +#endif + + /*for strncat_s performance optimization*/ +#if defined(__GNUC__) +#define STRNCAT_SM(dest, destMax, src, count) \ + ({ int ncatRet = EOK;\ + if (NULL != (void*)dest && NULL != (void*)src && (size_t)destMax > 0 && (((UINT64T)(destMax) & (UINT64T)(-2)) < SECUREC_STRING_MAX_LEN) && (((UINT64T)(count) & (UINT64T)(-2)) < SECUREC_STRING_MAX_LEN)) {\ + char* pCatTmpDest = (dest);\ + size_t ncatRestSz = (destMax);\ + do{\ + while(ncatRestSz > 0 && *pCatTmpDest) {\ + ++pCatTmpDest;\ + --ncatRestSz;\ + }\ + if (ncatRestSz == 0) {\ + ncatRet = EINVAL;\ + break;\ + }\ + if ( (TWO_MIN((count) , strlen(src)) + 1) <= ncatRestSz ) {\ + if ((count) < strlen(src)) {\ + memcpy(pCatTmpDest, (src), (count));\ + *(pCatTmpDest + (count)) = '\0';\ + }else {\ + memcpy(pCatTmpDest, (src), strlen(src) + 1);\ + }\ + }else{\ + ncatRet = ERANGE;\ + }\ + }while(0);\ + if ( EOK != ncatRet) ncatRet = strncat_s((dest), (destMax), (src), (count));\ + }else{\ + ncatRet = strncat_s((dest), (destMax), (src), (count));\ + }\ + ncatRet;}) +#else +#define STRNCAT_SM(dest, destMax, src, count) strncat_s(dest, destMax, src, count) +#endif + + /* + MEMCPY_SM do NOT check buffer overlap by default, or you can add this check to improve security + condCheck = condCheck || (dest == src) || (dest > src && dest < (void*)((UINT8T*)src + count));\ + condCheck = condCheck || (src > dest && src < (void*)((UINT8T*)dest + count)); \ + */ + +#define MEMCPY_SM(dest, destMax, src, count)\ + (!(((size_t)destMax== 0 )||(((UINT64T)(destMax) & (UINT64T)(-2)) > SECUREC_MEM_MAX_LEN)||((size_t)count > (size_t)destMax) || (NULL == (void*)dest) || (NULL == (void*)src))? (memcpy(dest, src, count), EOK) : (memcpy_s(dest, destMax, src, count))) + +#define MEMSET_SM(dest, destMax, c, count)\ + (!(((size_t)destMax == 0 ) || (((UINT64T)(destMax) & (UINT64T)(-2)) > SECUREC_MEM_MAX_LEN) || (NULL == (void*)dest) || ((size_t)count > (size_t)destMax)) ? (memset(dest, c, count), EOK) : ( memset_s(dest, destMax, c, count))) + +#endif /* WITH_PERFORMANCE_ADDONS */ + +/* 20150105 For software and hardware decoupling,such as UMG*/ +#ifdef SECUREC_SYSAPI4VXWORKS +#ifdef feof +#undef feof +#endif +extern int feof(FILE *stream); + +#ifndef isspace +#define isspace(c) (((c) == ' ') || ((c) == '\t') || ((c) == '\r') || ((c) == '\n')) +#endif +#ifndef isascii +#define isascii(c) (((unsigned char)(c))<=0x7f) +#endif +#ifndef isupper +#define isupper(c) ((c) >= 'A' && (c) <= 'Z') +#endif +#ifndef islower +#define islower(c) ((c) >= 'a' && (c) <= 'z') +#endif +#ifndef isalpha +#define isalpha(c) (isupper(c) || (islower(c))) +#endif +#ifndef isdigit +#define isdigit(c) ((c) >= '0' && (c) <= '9') +#endif +#ifndef isxdigit +#define isxupper(c) ((c) >= 'A' && (c) <= 'F') +#define isxlower(c) ((c) >= 'a' && (c) <= 'f') +#define isxdigit(c) (isdigit(c) || isxupper(c) ||isxlower(c)) +#endif +#endif + +#endif /*__SECURECTYPE_H__A7BBB686_AADA_451B_B9F9_44DACDAE18A7*/ + + diff --git a/test/Industry/test.c b/test/Industry/test.c new file mode 100644 index 0000000000..e3073afa60 --- /dev/null +++ b/test/Industry/test.c @@ -0,0 +1,26 @@ +#include + +char *gDest; + +char *HelpTestBad8(int len) +{ +#if 0 + if (len > 0) { + return gDest; + } +#endif + return NULL; +} + +void TestBad8(int len) +{ + // char *buf = HelpTestBad8(len); + char *buf = (char *)malloc(10); + buf[0] = 'a'; // CHECK: KLEE: WARNING: 100.00% NullPointerException True Positive at trace 1 +} + +// 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 --libc=klee --skip-not-symbolic-objects --skip-not-lazy-initialized --check-out-of-memory --analysis-reproduce=%s.json %t1.bc +// RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s \ No newline at end of file diff --git a/test/Industry/test.c.json b/test/Industry/test.c.json new file mode 100644 index 0000000000..c748da5226 --- /dev/null +++ b/test/Industry/test.c.json @@ -0,0 +1,65 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/./test.c" + }, + "region": { + "startLine": 18, + "startColumn": null + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/./test.c" + }, + "region": { + "startLine": 19, + "startColumn": null + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "/mnt/d/wsl-ubuntu/test2/./test.c" + }, + "region": { + "startLine": 19, + "startColumn": null + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/while_true.c b/test/Industry/while_true.c new file mode 100644 index 0000000000..9b2b05d7be --- /dev/null +++ b/test/Industry/while_true.c @@ -0,0 +1,20 @@ +int main() { + int *p = 0; + klee_sleep(); + int s = 0; + while (s < 10) { + s = (s + 1) % 10; + } + return *p; +} + +// 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 --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: 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 --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 diff --git a/test/Industry/while_true.c.json b/test/Industry/while_true.c.json new file mode 100644 index 0000000000..e8d4811130 --- /dev/null +++ b/test/Industry/while_true.c.json @@ -0,0 +1,65 @@ +{ + "runs": [ + { + "tool": { + "driver": { + "name": "huawei" + } + }, + "results": [ + { + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "while_true.c" + }, + "region": { + "startLine": 2, + "startColumn": 8 + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "while_true.c" + }, + "region": { + "startLine": 8, + "startColumn": 10 + } + } + } + } + ] + } + ] + } + ], + "ruleId": "NullDereference", + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "while_true.c" + }, + "region": { + "startLine": 8, + "startColumn": 10 + } + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Merging/batching_break.c b/test/Merging/batching_break.c index 4384f618ac..11e94c8b50 100644 --- a/test/Merging/batching_break.c +++ b/test/Merging/batching_break.c @@ -1,6 +1,6 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // CHECK: close merge: diff --git a/test/Merging/easy_merge.c b/test/Merging/easy_merge.c index 64ba770d0b..38e0cadedc 100644 --- a/test/Merging/easy_merge.c +++ b/test/Merging/easy_merge.c @@ -1,16 +1,16 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=bfs --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=bfs --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=dfs --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=dfs --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // CHECK: close merge: diff --git a/test/Merging/incomplete_merge.c b/test/Merging/incomplete_merge.c index ccdeed0276..15e7bbe642 100644 --- a/test/Merging/incomplete_merge.c +++ b/test/Merging/incomplete_merge.c @@ -1,6 +1,6 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --use-incomplete-merge --debug-log-incomplete-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --use-incomplete-merge --debug-log-incomplete-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // 5 close merges diff --git a/test/Merging/indirect_value.c b/test/Merging/indirect_value.c index f073266e04..50ec733433 100644 --- a/test/Merging/indirect_value.c +++ b/test/Merging/indirect_value.c @@ -1,6 +1,6 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s // CHECK: generated tests = 2{{$}} diff --git a/test/Merging/loop_merge.c b/test/Merging/loop_merge.c index edf06c262f..d87f8b625e 100644 --- a/test/Merging/loop_merge.c +++ b/test/Merging/loop_merge.c @@ -1,12 +1,12 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=bfs %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=bfs %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=dfs %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=dfs %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // There will be 20 'close merge' statements. Only checking a few, the generated diff --git a/test/Merging/merge_fail.c b/test/Merging/merge_fail.c index fd2a5e5dd2..5d9463420b 100644 --- a/test/Merging/merge_fail.c +++ b/test/Merging/merge_fail.c @@ -1,12 +1,12 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=bfs %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=bfs %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=dfs %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=dfs %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // CHECK: generated tests = 2{{$}} diff --git a/test/Merging/nested_merge.c b/test/Merging/nested_merge.c index 8bc80a69a5..985beab041 100644 --- a/test/Merging/nested_merge.c +++ b/test/Merging/nested_merge.c @@ -1,14 +1,14 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=bfs --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=bfs --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=dfs --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=dfs --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // 5 close merges diff --git a/test/Merging/split_merge.c b/test/Merging/split_merge.c index 477180f224..d5d7dc2230 100644 --- a/test/Merging/split_merge.c +++ b/test/Merging/split_merge.c @@ -1,14 +1,14 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=bfs --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=bfs --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=dfs --use-batching-search %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=dfs --use-batching-search %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=nurs:covnew %t.bc 2>&1 | FileCheck %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=random-path %t.bc 2>&1 | FileCheck %s // CHECK: open merge: // CHECK: close merge: diff --git a/test/Merging/state_termination.c b/test/Merging/state_termination.c index 13ad43790e..2d1492ea7c 100644 --- a/test/Merging/state_termination.c +++ b/test/Merging/state_termination.c @@ -1,6 +1,6 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --debug-log-merge --search=dfs %t.bc +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --debug-log-merge --search=dfs %t.bc #include "klee/klee.h" diff --git a/test/Merging/unexpected_close.c b/test/Merging/unexpected_close.c index 5cc999741f..239101ec9b 100644 --- a/test/Merging/unexpected_close.c +++ b/test/Merging/unexpected_close.c @@ -1,6 +1,6 @@ // RUN: %clang -emit-llvm -g -c -o %t.bc %s // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=false --use-merge --search=nurs:covnew --max-time=2 %t.bc +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=none --use-merge --search=nurs:covnew --max-time=2 %t.bc // CHECK: ran into a close at // CHECK: generated tests = 2{{$}} diff --git a/test/Runtime/POSIX/DirConsistency.c b/test/Runtime/POSIX/DirConsistency.c index d92766f7ec..a2f8c07709 100644 --- a/test/Runtime/POSIX/DirConsistency.c +++ b/test/Runtime/POSIX/DirConsistency.c @@ -1,11 +1,11 @@ // RUN: %clang %s -emit-llvm %O0opt -c -g -o %t.bc // RUN: rm -rf %t.klee-out %t.klee-out-tmp // RUN: %gentmp %t.klee-out-tmp -// RUN: %klee --output-dir=%t.klee-out --run-in-dir=%t.klee-out-tmp --use-guided-search=false --search=random-state --libc=uclibc --posix-runtime --exit-on-error %t.bc --sym-files 1 1 > %t1.log +// RUN: %klee --output-dir=%t.klee-out --run-in-dir=%t.klee-out-tmp --use-guided-search=none --search=random-state --libc=uclibc --posix-runtime --exit-on-error %t.bc --sym-files 1 1 > %t1.log // RUN: %clang -D_FILE_OFFSET_BITS=64 %s -emit-llvm %O0opt -c -g -o %t.bc // RUN: rm -rf %t.klee-out %t.klee-out-tmp // RUN: %gentmp %t.klee-out-tmp -// RUN: %klee --output-dir=%t.klee-out --run-in-dir=%t.klee-out-tmp --use-guided-search=false --search=random-state --libc=uclibc --posix-runtime --exit-on-error %t.bc --sym-files 1 1 > %t2.log +// RUN: %klee --output-dir=%t.klee-out --run-in-dir=%t.klee-out-tmp --use-guided-search=none --search=random-state --libc=uclibc --posix-runtime --exit-on-error %t.bc --sym-files 1 1 > %t2.log // RUN: sort %t1.log %t2.log | uniq -c > %t3.log // RUN: grep -q "4 COUNT" %t3.log diff --git a/unittests/Searcher/SearcherTest.cpp b/unittests/Searcher/SearcherTest.cpp index 7cb976e51b..bdb6226de4 100644 --- a/unittests/Searcher/SearcherTest.cpp +++ b/unittests/Searcher/SearcherTest.cpp @@ -12,6 +12,7 @@ #include "gtest/gtest.h" #include "Core/ExecutionState.h" +#include "Core/PForest.h" #include "Core/PTree.h" #include "Core/Searcher.h" #include "klee/ADT/RNG.h" @@ -25,11 +26,14 @@ namespace { TEST(SearcherTest, RandomPath) { // First state ExecutionState es; - PTree processTree(&es); - es.ptreeNode = processTree.root.getPointer(); + PForest processForest = PForest(); + processForest.addRoot(&es); + es.ptreeNode = processForest.getPTrees() + .at(es.ptreeNode->getTreeID()) + ->root.getPointer(); RNG rng; - RandomPathSearcher rp(processTree, rng); + RandomPathSearcher rp(processForest, rng); EXPECT_TRUE(rp.empty()); rp.update(nullptr, {&es}, {}); @@ -38,7 +42,7 @@ TEST(SearcherTest, RandomPath) { // Two states ExecutionState es1(es); - processTree.attach(es.ptreeNode, &es1, &es, BranchType::NONE); + processForest.attach(es.ptreeNode, &es1, &es, BranchType::NONE); rp.update(&es, {&es1}, {}); // Random path seed dependant @@ -52,28 +56,31 @@ TEST(SearcherTest, RandomPath) { } rp.update(&es, {&es1}, {&es}); - processTree.remove(es.ptreeNode); + processForest.remove(es.ptreeNode); for (int i = 0; i < 100; i++) { EXPECT_EQ(&rp.selectState(), &es1); } rp.update(&es1, {}, {&es1}); - processTree.remove(es1.ptreeNode); + processForest.remove(es1.ptreeNode); EXPECT_TRUE(rp.empty()); } TEST(SearcherTest, TwoRandomPath) { // Root state ExecutionState root; - PTree processTree(&root); - root.ptreeNode = processTree.root.getPointer(); + PForest processForest = PForest(); + processForest.addRoot(&root); + root.ptreeNode = processForest.getPTrees() + .at(root.ptreeNode->getTreeID()) + ->root.getPointer(); ExecutionState es(root); - processTree.attach(root.ptreeNode, &es, &root, BranchType::NONE); + processForest.attach(root.ptreeNode, &es, &root, BranchType::NONE); RNG rng, rng1; - RandomPathSearcher rp(processTree, rng); - RandomPathSearcher rp1(processTree, rng1); + RandomPathSearcher rp(processForest, rng); + RandomPathSearcher rp1(processForest, rng1); EXPECT_TRUE(rp.empty()); EXPECT_TRUE(rp1.empty()); @@ -84,7 +91,7 @@ TEST(SearcherTest, TwoRandomPath) { // Two states ExecutionState es1(es); - processTree.attach(es.ptreeNode, &es1, &es, BranchType::NONE); + processForest.attach(es.ptreeNode, &es1, &es, BranchType::NONE); rp.update(&es, {}, {}); rp1.update(nullptr, {&es1}, {}); @@ -104,16 +111,16 @@ TEST(SearcherTest, TwoRandomPath) { } rp1.update(&es, {}, {&es}); - processTree.remove(es.ptreeNode); + processForest.remove(es.ptreeNode); EXPECT_TRUE(rp1.empty()); EXPECT_EQ(&rp.selectState(), &es1); rp.update(&es1, {}, {&es1}); - processTree.remove(es1.ptreeNode); + processForest.remove(es1.ptreeNode); EXPECT_TRUE(rp.empty()); EXPECT_TRUE(rp1.empty()); - processTree.remove(root.ptreeNode); + processForest.remove(root.ptreeNode); } TEST(SearcherTest, TwoRandomPathDot) { @@ -123,23 +130,26 @@ TEST(SearcherTest, TwoRandomPathDot) { // Root state ExecutionState root; - PTree processTree(&root); - root.ptreeNode = processTree.root.getPointer(); + PForest processForest = PForest(); + processForest.addRoot(&root); + root.ptreeNode = processForest.getPTrees() + .at(root.ptreeNode->getTreeID()) + ->root.getPointer(); rootPNode = root.ptreeNode; ExecutionState es(root); - processTree.attach(root.ptreeNode, &es, &root, BranchType::NONE); + processForest.attach(root.ptreeNode, &es, &root, BranchType::NONE); rightLeafPNode = root.ptreeNode; esParentPNode = es.ptreeNode; RNG rng; - RandomPathSearcher rp(processTree, rng); - RandomPathSearcher rp1(processTree, rng); + RandomPathSearcher rp(processForest, rng); + RandomPathSearcher rp1(processForest, rng); rp.update(nullptr, {&es}, {}); ExecutionState es1(es); - processTree.attach(es.ptreeNode, &es1, &es, BranchType::NONE); + processForest.attach(es.ptreeNode, &es1, &es, BranchType::NONE); esLeafPNode = es.ptreeNode; es1LeafPNode = es1.ptreeNode; @@ -168,14 +178,14 @@ TEST(SearcherTest, TwoRandomPathDot) { << "}\n"; std::string pTreeDot; llvm::raw_string_ostream pTreeDotStream(pTreeDot); - processTree.dump(pTreeDotStream); + processForest.dump(pTreeDotStream); EXPECT_EQ(modelPTreeDot.str(), pTreeDotStream.str()); rp.update(&es, {&es1}, {&es}); rp1.update(nullptr, {&es}, {&es1}); rp1.update(&es, {}, {&es}); - processTree.remove(es.ptreeNode); + processForest.remove(es.ptreeNode); modelPTreeDot.str(""); modelPTreeDot @@ -197,22 +207,25 @@ TEST(SearcherTest, TwoRandomPathDot) { << "}\n"; pTreeDot = ""; - processTree.dump(pTreeDotStream); + processForest.dump(pTreeDotStream); EXPECT_EQ(modelPTreeDot.str(), pTreeDotStream.str()); - processTree.remove(es1.ptreeNode); - processTree.remove(root.ptreeNode); + processForest.remove(es1.ptreeNode); + processForest.remove(root.ptreeNode); } TEST(SearcherDeathTest, TooManyRandomPaths) { // First state ExecutionState es; - PTree processTree(&es); - es.ptreeNode = processTree.root.getPointer(); - processTree.remove(es.ptreeNode); // Need to remove to avoid leaks + PForest processForest = PForest(); + processForest.addRoot(&es); + es.ptreeNode = processForest.getPTrees() + .at(es.ptreeNode->getTreeID()) + ->root.getPointer(); + processForest.remove(es.ptreeNode); // Need to remove to avoid leaks RNG rng; - RandomPathSearcher rp(processTree, rng); - RandomPathSearcher rp1(processTree, rng); - RandomPathSearcher rp2(processTree, rng); - ASSERT_DEATH({ RandomPathSearcher rp3(processTree, rng); }, ""); + RandomPathSearcher rp(processForest, rng); + RandomPathSearcher rp1(processForest, rng); + RandomPathSearcher rp2(processForest, rng); + ASSERT_DEATH({ RandomPathSearcher rp3(processForest, rng); }, ""); } } // namespace