From 14d8191fa63cafe2523d3181c7f284b333d9c742 Mon Sep 17 00:00:00 2001 From: Yurii Kostyukov Date: Tue, 22 Aug 2023 10:48:25 +0300 Subject: [PATCH] [feat] Cooddy NPE location handling --- include/klee/Core/TargetedExecutionReporter.h | 4 +- include/klee/Module/KInstruction.h | 1 + include/klee/Module/SarifReport.h | 220 +++++-- include/klee/Module/Target.h | 31 +- lib/Core/Executor.cpp | 2 +- lib/Core/TargetManager.cpp | 47 +- lib/Core/TargetedExecutionManager.cpp | 32 +- lib/Core/TargetedExecutionReporter.cpp | 5 +- lib/Module/SarifReport.cpp | 606 +++++++++++++++--- lib/Module/Target.cpp | 31 +- lib/Module/TargetForest.cpp | 2 +- test/Industry/MacroLocalization.c | 28 + test/Industry/MacroLocalization.c.sarif | 155 +++++ test/Industry/StrangeBugInIFDS.c | 27 + test/Industry/StrangeBugInIFDS.c.sarif | 173 +++++ test/Industry/if2.c | 2 +- 16 files changed, 1147 insertions(+), 219 deletions(-) create mode 100644 test/Industry/MacroLocalization.c create mode 100644 test/Industry/MacroLocalization.c.sarif create mode 100644 test/Industry/StrangeBugInIFDS.c create mode 100644 test/Industry/StrangeBugInIFDS.c.sarif diff --git a/include/klee/Core/TargetedExecutionReporter.h b/include/klee/Core/TargetedExecutionReporter.h index 01b87b4e80e..8b5f7fe3d5e 100644 --- a/include/klee/Core/TargetedExecutionReporter.h +++ b/include/klee/Core/TargetedExecutionReporter.h @@ -30,8 +30,8 @@ ty min(ty left, ty right); }; // namespace confidence void reportFalsePositive(confidence::ty confidence, - const std::vector &errors, - const std::string &id, std::string whatToIncrease); + const ReachWithErrors &errors, const std::string &id, + std::string whatToIncrease); } // namespace klee diff --git a/include/klee/Module/KInstruction.h b/include/klee/Module/KInstruction.h index 5c64de152c6..86dae99a179 100644 --- a/include/klee/Module/KInstruction.h +++ b/include/klee/Module/KInstruction.h @@ -30,6 +30,7 @@ class Instruction; namespace klee { class Executor; class KModule; +struct KFunction; struct KBlock; /// KInstruction - Intermediate instruction representation used diff --git a/include/klee/Module/SarifReport.h b/include/klee/Module/SarifReport.h index 09a59d4d251..fdc17d53135 100644 --- a/include/klee/Module/SarifReport.h +++ b/include/klee/Module/SarifReport.h @@ -16,6 +16,7 @@ #include #include "klee/ADT/Ref.h" +#include "llvm/IR/IntrinsicInst.h" #include #include @@ -52,9 +53,10 @@ enum ReachWithError { Reachable, None, }; +using ReachWithErrors = std::vector; const char *getErrorString(ReachWithError error); -std::string getErrorsString(const std::vector &errors); +std::string getErrorsString(const ReachWithErrors &errors); struct FunctionInfo; struct KBlock; @@ -63,11 +65,16 @@ struct ArtifactLocationJson { optional uri; }; +struct Message { + std::string text; +}; + struct RegionJson { optional startLine; optional endLine; optional startColumn; optional endColumn; + optional message; }; struct PhysicalLocationJson { @@ -92,10 +99,6 @@ struct CodeFlowJson { std::vector threadFlows; }; -struct Message { - std::string text; -}; - struct Fingerprints { std::string cooddy_uid; }; @@ -137,7 +140,7 @@ struct SarifReportJson { NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ArtifactLocationJson, uri) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(RegionJson, startLine, endLine, - startColumn, endColumn) + startColumn, endColumn, message) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(PhysicalLocationJson, artifactLocation, region) @@ -165,6 +168,150 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(RunJson, results, tool) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(SarifReportJson, runs) +enum class Precision { NotFound = 0, Line = 1, Column = 2, Instruction = 3 }; + +template struct WithPrecision { + T *ptr; + Precision precision; + + explicit WithPrecision(T *p, Precision pr) : ptr(p), precision(pr) {} + explicit WithPrecision(T *p) : WithPrecision(p, Precision::NotFound) {} + explicit WithPrecision() : WithPrecision(nullptr) {} + + void setNotFound() { precision = Precision::NotFound; } + bool isNotFound() const { return precision == Precision::NotFound; } +}; + +struct KBlock; +struct KInstruction; + +using BlockWithPrecision = WithPrecision; +using InstrWithPrecision = WithPrecision; + +inline size_t hash_combine2(std::size_t s, std::size_t v) { + return s ^ (v + 0x9e3779b9 + (s << 6) + (s >> 2)); +} + +template inline void hash_combine(std::size_t &s, const T &v) { + std::hash h; + s = hash_combine2(s, h(v)); +} + +enum class ReachWithoutError { + Reach = 0, + Return, + NPESource, + Free, + BranchFalse, + BranchTrue, + Call, + AfterCall +}; + +struct EventKind final { + const bool isError; + const ReachWithErrors kinds; + const ReachWithoutError kind; + size_t hashValue = 0; + void computeHash() { + hash_combine(hashValue, isError); + for (auto k : kinds) + hash_combine(hashValue, k); + hash_combine(hashValue, kind); + } + EventKind(ReachWithErrors &&kinds) + : isError(true), kinds(kinds), kind(ReachWithoutError::Reach) { + computeHash(); + } + EventKind(ReachWithoutError kind) : isError(false), kind(kind) { + computeHash(); + } +}; +} // namespace klee + +namespace std { +template <> struct hash { + size_t operator()(const klee::EventKind &k) const { return k.hashValue; } +}; +} // namespace std + +namespace klee { +enum class ToolName { Unknown = 0, SecB, clang, CppCheck, Infer, Cooddy }; + +class LineColumnRange; + +struct LocRange { + virtual LineColumnRange getRange() const = 0; + virtual ~LocRange() = default; + virtual Precision maxPrecision() const = 0; + virtual size_t hash() const = 0; + virtual std::string toString() const = 0; + bool hasInside(KInstruction *ki) const; + void hasInside(InstrWithPrecision &kp); + virtual void setRange(const KInstruction *ki) = 0; + +protected: + virtual bool hasInsideInternal(InstrWithPrecision &kp) const = 0; +}; + +class LineColumnRange final : public LocRange { + size_t startLine; + size_t startColumn; + size_t endLine; + size_t endColumn; + static const size_t empty = std::numeric_limits::max(); + + bool inline onlyLine() const { return startColumn == empty; } + +public: + explicit LineColumnRange(size_t startLine, size_t startColumn, size_t endLine, + size_t endColumn) + : startLine(startLine), startColumn(startColumn), endLine(endLine), + endColumn(endColumn) { + assert(startLine <= endLine); + assert(startLine != endLine || startColumn <= endColumn); + } + explicit LineColumnRange(size_t startLine, size_t endLine) + : LineColumnRange(startLine, empty, endLine, empty) {} + explicit LineColumnRange(const KInstruction *ki) { setRange(ki); } + + void setRange(const KInstruction *ki) final; + + LineColumnRange getRange() const final { return *this; } + + void clearColumns() { + startColumn = (endColumn = empty); + } + + Precision maxPrecision() const final { + return onlyLine() ? Precision::Line : Precision::Column; + } + + size_t hash() const final { + size_t hashValue = 0; + hashValue = hash_combine2(hashValue, startLine); + hashValue = hash_combine2(hashValue, endLine); + hashValue = hash_combine2(hashValue, startColumn); + return hash_combine2(hashValue, endColumn); + } + + std::string toString() const final { + if (onlyLine()) + return std::to_string(startLine) + "-" + std::to_string(endLine); + return std::to_string(startLine) + ":" + std::to_string(startColumn) + "-" + + std::to_string(endLine) + ":" + std::to_string(endColumn); + } + + bool hasInsideInternal(InstrWithPrecision &kp) const final; + + bool operator==(const LineColumnRange &p) const { + return startLine == p.startLine && endLine == p.endLine && + startColumn == p.startColumn && endColumn == p.endColumn; + } +}; + +using OpCode = unsigned; + struct Location { struct LocationHash { std::size_t operator()(const Location *l) const { return l->hash(); } @@ -184,35 +331,30 @@ struct Location { } }; std::string filename; - unsigned int startLine; - unsigned int endLine; - optional startColumn; - optional endColumn; + std::unique_ptr range; - static ref create(std::string filename_, unsigned int startLine_, + static ref create(std::string &&filename_, unsigned int startLine_, optional endLine_, optional startColumn_, - optional endColumn_); + optional endColumn_, + ToolName toolName, EventKind &kind); - ~Location(); - std::size_t hash() const { return hashValue; } + virtual ~Location(); + virtual 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 operator==(const Location &other) const; bool isInside(const std::string &name) const; using Instructions = std::unordered_map< unsigned int, - std::unordered_map>>; + std::unordered_map>>; - bool isInside(KBlock *block, const Instructions &origInsts) const; + void isInside(InstrWithPrecision &kp, const Instructions &origInsts) const; + void isInside(BlockWithPrecision &bp, const Instructions &origInsts) const; std::string toString() const; @@ -226,28 +368,20 @@ struct Location { 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); - } + void computeHash(EventKind &kind); - template inline void hash_combine(std::size_t &s, const T &v) { - std::hash h; - s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2); - } + static Location *createCooddy(std::string &&filename_, LineColumnRange &range, + EventKind &kind); - 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(); +protected: + Location(std::string &&filename_, std::unique_ptr range, + EventKind &kind) + : filename(std::move(filename_)), range(std::move(range)) { + computeHash(kind); } + + virtual void isInsideInternal(BlockWithPrecision &bp, + const Instructions &origInsts) const; }; struct RefLocationHash { @@ -262,9 +396,9 @@ struct RefLocationCmp { struct Result { std::vector> locations; - std::vector> metadatas; - std::string id; - std::vector errors; + const std::vector> metadatas; + const std::string id; + const ReachWithErrors errors; }; struct SarifReport { diff --git a/include/klee/Module/Target.h b/include/klee/Module/Target.h index 98eb3c900af..e54047dcdef 100644 --- a/include/klee/Module/Target.h +++ b/include/klee/Module/Target.h @@ -36,16 +36,6 @@ DISABLE_WARNING_POP namespace klee { using nonstd::optional; -struct ErrorLocation { - unsigned int startLine; - unsigned int endLine; - optional startColumn; - optional endColumn; - - ErrorLocation(const klee::ref &loc); - ErrorLocation(const KInstruction *ki); -}; - class ReproduceErrorTarget; class Target { @@ -174,23 +164,22 @@ class CoverBranchTarget : public Target { class ReproduceErrorTarget : public Target { private: - std::vector - errors; // None - if it is not terminated in error trace - std::string id; // "" - if it is not terminated in error trace - ErrorLocation loc; // TODO(): only for check in reportTruePositive + ReachWithErrors errors; // None - if it is not terminated in error trace + std::string id; // "" - if it is not terminated in error trace + LineColumnRange loc; // TODO(): only for check in reportTruePositive protected: - explicit ReproduceErrorTarget(const std::vector &_errors, - const std::string &_id, ErrorLocation _loc, + explicit ReproduceErrorTarget(const ReachWithErrors &_errors, + const std::string &_id, LineColumnRange _loc, KBlock *_block) - : Target(_block), errors(_errors), id(_id), loc(_loc) { + : Target(_block), errors(_errors), id(_id), loc(std::move(_loc)) { assert(errors.size() > 0); std::sort(errors.begin(), errors.end()); } public: - static ref create(const std::vector &_errors, - const std::string &_id, ErrorLocation _loc, + static ref create(const ReachWithErrors &_errors, + const std::string &_id, LineColumnRange _loc, KBlock *_block); Kind getKind() const override { return Kind::ReproduceError; } @@ -207,12 +196,12 @@ class ReproduceErrorTarget : public Target { bool isReported = false; - const std::vector &getErrors() const { return errors; } + const ReachWithErrors &getErrors() const { return errors; } bool isThatError(ReachWithError err) const { return std::find(errors.begin(), errors.end(), err) != errors.end(); } - bool isTheSameAsIn(const KInstruction *instr) const; + bool isTheSameAsIn(KInstruction *instr) const; }; } // namespace klee diff --git a/lib/Core/Executor.cpp b/lib/Core/Executor.cpp index fbb1cad4987..1c901b4d99e 100644 --- a/lib/Core/Executor.cpp +++ b/lib/Core/Executor.cpp @@ -6707,7 +6707,7 @@ void Executor::runFunctionAsMain(Function *f, int argc, char **argv, KBlock *kCallBlock = kfunction->entryKBlock; forest->add(ReproduceErrorTarget::create( {ReachWithError::Reachable}, "", - ErrorLocation(kCallBlock->getFirstInstruction()), kCallBlock)); + LineColumnRange(kCallBlock->getFirstInstruction()), kCallBlock)); prepTargets.emplace(kEntryFunction, forest); } diff --git a/lib/Core/TargetManager.cpp b/lib/Core/TargetManager.cpp index 9dcaedcd86f..1af337b4369 100644 --- a/lib/Core/TargetManager.cpp +++ b/lib/Core/TargetManager.cpp @@ -152,29 +152,34 @@ void TargetManager::updateTargets(ExecutionState &state) { return; } - auto stateTargets = targets(state); - auto &stateTargetForest = targetForest(state); - - for (auto target : stateTargets) { - if (!stateTargetForest.contains(target)) { - continue; - } + bool needRecheck; + do { + needRecheck = false; + auto stateTargets = targets(state); + auto &stateTargetForest = targetForest(state); + + for (auto target : stateTargets) { + if (!stateTargetForest.contains(target)) { + continue; + } - DistanceResult stateDistance = distance(state, target); - switch (stateDistance.result) { - case WeightResult::Continue: - updateContinue(state, target); - break; - case WeightResult::Miss: - updateMiss(state, target); - break; - case WeightResult::Done: - updateDone(state, target); - break; - default: - assert(0 && "unreachable"); + DistanceResult stateDistance = distance(state, target); + switch (stateDistance.result) { + case WeightResult::Continue: + updateContinue(state, target); + break; + case WeightResult::Miss: + updateMiss(state, target); + break; + case WeightResult::Done: + updateDone(state, target); + needRecheck = true; + break; + default: + assert(0 && "unreachable"); + } } - } + } while (needRecheck); } void TargetManager::update(ExecutionState *current, diff --git a/lib/Core/TargetedExecutionManager.cpp b/lib/Core/TargetedExecutionManager.cpp index a46a12e3c91..2b4e12a3fdd 100644 --- a/lib/Core/TargetedExecutionManager.cpp +++ b/lib/Core/TargetedExecutionManager.cpp @@ -182,6 +182,11 @@ cl::opt cl::desc("stop execution after visiting some basic block this " "amount of times (default=0)."), cl::init(0), cl::cat(TerminationCat)); + +cl::opt DebugLocalization( + "debug-localization", cl::init(false), + cl::desc("Debug error traces localization (default=false)."), + cl::cat(DebugCat)); } // namespace klee TargetedHaltsOnTraces::TargetedHaltsOnTraces(ref &forest) { @@ -327,6 +332,8 @@ TargetedExecutionManager::prepareAllLocations(KModule *kmodule, for (auto it = locations.begin(); it != locations.end(); ++it) { auto loc = *it; + auto precision = Precision::Line; + Blocks blocks; for (const auto &[fileName, origInstsInFile] : kmodule->origInstructions) { if (kmodule->origInstructions.count(fileName) == 0) { continue; @@ -341,16 +348,29 @@ TargetedExecutionManager::prepareAllLocations(KModule *kmodule, const auto kfunc = kmodule->functionMap[func]; for (const auto &kblock : kfunc->blocks) { - auto b = kblock.get(); - if (!loc->isInside(b, origInstsInFile)) { + BlockWithPrecision bp(kblock.get(), precision); + loc->isInside(bp, origInstsInFile); + if (bp.precision < precision) { continue; + } else if (bp.precision == precision) { + blocks.insert(bp.ptr); + } else { + blocks.clear(); + blocks.insert(bp.ptr); + precision = bp.precision; } - locToBlocks[loc].insert(b); } } } + if (DebugLocalization && !blocks.empty()) { + llvm::errs() << loc->toString() << " # " << (int)precision << " ~> "; + for (auto b : blocks) { + llvm::errs() << b->toString() << "; "; + } + llvm::errs() << "\n"; + } + locToBlocks.emplace(loc, std::move(blocks)); } - return locToBlocks; } @@ -417,7 +437,7 @@ bool TargetedExecutionManager::tryResolveLocations( } } resolvedLocations.push_back(location); - } else if (index == result.locations.size() - 1) { + } else if (index + 1 == result.locations.size()) { klee_warning( "Trace %s is malformed! %s at location %s, so skipping this trace.", result.id.c_str(), getErrorsString(result.errors).c_str(), @@ -519,6 +539,8 @@ TargetedExecutionManager::prepareTargets(KModule *kmodule, SarifReport paths) { whitelists[kf] = whitelist; } whitelists[kf]->addTrace(result, locToBlocks); + if (DebugLocalization) + whitelists[kf]->dump(); } return whitelists; diff --git a/lib/Core/TargetedExecutionReporter.cpp b/lib/Core/TargetedExecutionReporter.cpp index b45569336f9..56e5ee77e85 100644 --- a/lib/Core/TargetedExecutionReporter.cpp +++ b/lib/Core/TargetedExecutionReporter.cpp @@ -15,12 +15,11 @@ using namespace klee; void klee::reportFalsePositive(confidence::ty confidence, - const std::vector &errors, + const ReachWithErrors &errors, const std::string &id, std::string whatToIncrease) { std::ostringstream out; - std::vector errorsSet(errors.begin(), errors.end()); - out << getErrorsString(errorsSet) << " False Positive at trace " << id; + out << getErrorsString(errors) << " False Positive at trace " << id; if (!confidence::isConfident(confidence)) { out << ". Advice: " << "increase --" << whatToIncrease << " command line parameter value"; diff --git a/lib/Module/SarifReport.cpp b/lib/Module/SarifReport.cpp index 3923d32ead7..d1456ee50ae 100644 --- a/lib/Module/SarifReport.cpp +++ b/lib/Module/SarifReport.cpp @@ -7,11 +7,14 @@ // //===----------------------------------------------------------------------===// +#include + #include "klee/Module/SarifReport.h" #include "klee/Module/KInstruction.h" #include "klee/Module/KModule.h" #include "klee/Support/ErrorHandling.h" +#include "llvm/Support/CommandLine.h" #include "klee/Support/CompilerWarning.h" DISABLE_WARNING_PUSH @@ -25,34 +28,10 @@ using namespace klee; namespace { 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::vector -tryConvertRuleJson(const std::string &ruleId, const std::string &toolName, - const optional &errorMessage) { - if (toolName == "SecB") { +ReachWithErrors tryConvertRuleJson(const std::string &ruleId, ToolName toolName, + const optional &errorMessage) { + switch (toolName) { + case ToolName::SecB: if ("NullDereference" == ruleId) { return {ReachWithError::MustBeNullPointerException}; } else if ("CheckAfterDeref" == ruleId) { @@ -63,10 +42,9 @@ tryConvertRuleJson(const std::string &ruleId, const std::string &toolName, return {ReachWithError::UseAfterFree}; } else if ("Reached" == ruleId) { return {ReachWithError::Reachable}; - } else { - return {}; } - } else if (toolName == "clang") { + return {}; + case ToolName::clang: if ("core.NullDereference" == ruleId) { return {ReachWithError::MayBeNullPointerException, ReachWithError::MustBeNullPointerException}; @@ -84,28 +62,25 @@ tryConvertRuleJson(const std::string &ruleId, const std::string &toolName, } } else if ("core.Reach" == ruleId) { return {ReachWithError::Reachable}; - } else { - return {}; } - } else if (toolName == "CppCheck") { + return {}; + case ToolName::CppCheck: if ("nullPointer" == ruleId || "ctunullpointer" == ruleId) { return {ReachWithError::MayBeNullPointerException, ReachWithError::MustBeNullPointerException}; // TODO: check it out } else if ("doubleFree" == ruleId) { return {ReachWithError::DoubleFree}; - } else { - return {}; } - } else if (toolName == "Infer") { + return {}; + case ToolName::Infer: if ("NULL_DEREFERENCE" == ruleId || "NULLPTR_DEREFERENCE" == ruleId) { return {ReachWithError::MayBeNullPointerException, ReachWithError::MustBeNullPointerException}; // TODO: check it out } else if ("USE_AFTER_DELETE" == ruleId || "USE_AFTER_FREE" == ruleId) { return {ReachWithError::UseAfterFree, ReachWithError::DoubleFree}; - } else { - return {}; } - } else if (toolName == "Cooddy") { + return {}; + case ToolName::Cooddy: if ("NULL.DEREF" == ruleId || "NULL.UNTRUSTED.DEREF" == ruleId) { return {ReachWithError::MayBeNullPointerException, ReachWithError::MustBeNullPointerException}; @@ -113,30 +88,133 @@ tryConvertRuleJson(const std::string &ruleId, const std::string &toolName, return {ReachWithError::DoubleFree}; } else if ("MEM.USE.FREE" == ruleId) { return {ReachWithError::UseAfterFree}; - } else { - return {}; } - } else { + return {}; + case ToolName::Unknown: return {}; } } +bool startsWith(const std::string &s, const std::string &prefix) { + return s.rfind(prefix, 0) == 0; +} + +bool endsWith(const std::string &s, const std::string &suffix) { + ssize_t maybe_index = s.size() - suffix.size(); + return maybe_index > 0 && + (s.find(suffix, maybe_index) == (size_t)maybe_index); +} + +ReachWithoutError tryConvertCooddyNoErrorKind(const klee::Message &msg) { + const auto &message = msg.text; + if ("Assume return here" == message) + return ReachWithoutError::Return; + if (startsWith(message, "Null pointer returned as")) + return ReachWithoutError::AfterCall; + if (startsWith(message, "Use after free returned as")) + return ReachWithoutError::AfterCall; + if ("Null pointer source" == message) + return ReachWithoutError::NPESource; + if ("Free" == message) + return ReachWithoutError::Free; + if (startsWith(message, "Function ") && endsWith(message, " is executed")) + return ReachWithoutError::Call; + if (startsWith(message, "Null pointer passed as")) + return ReachWithoutError::Call; + if (startsWith(message, "Use after free passed as")) + return ReachWithoutError::Call; + if (endsWith(message, " is false")) + return ReachWithoutError::BranchFalse; + if (endsWith(message, " is true")) + return ReachWithoutError::BranchTrue; + return ReachWithoutError::Reach; +} + +ReachWithoutError tryConvertNoErrorKind(ToolName toolName, + const klee::Message &message) { + switch (toolName) { + case ToolName::Cooddy: + return tryConvertCooddyNoErrorKind(message); + default: + return ReachWithoutError::Reach; + } +} + +ReachWithErrors tryConvertErrorKind(ToolName toolName, + const optional &ruleId, + const optional &msg) { + if (!ruleId.has_value()) { + return {ReachWithError::Reachable}; + } else { + return tryConvertRuleJson(*ruleId, toolName, msg); + } +} + +EventKind convertKindJson(ToolName toolName, + const optional &ruleId, + const std::optional &mOpt) { + if (!mOpt.has_value()) + return EventKind(ReachWithoutError::Reach); + const auto &message = *mOpt; + auto noError = tryConvertNoErrorKind(toolName, message); + if (noError != ReachWithoutError::Reach) + return EventKind(noError); + return EventKind(tryConvertErrorKind(toolName, ruleId, mOpt)); +} + +optional, EventKind>> +tryConvertLocationJson(ToolName toolName, const optional &ruleId, + 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 ®ion = physicalLocation->region; + if (!region.has_value() || !region->startLine.has_value()) { + return nonstd::nullopt; + } + + auto kind = convertKindJson(toolName, ruleId, region->message); + auto filename = *(artifactLocation->uri); + + auto loc = Location::create(std::move(filename), *(region->startLine), + region->endLine, region->startColumn, + region->endColumn, toolName, kind); + return std::make_pair(loc, std::move(kind)); +} + +void recoverCallChain(std::vector> &locations, + std::vector &kinds) { + std::list> result; + auto lit = locations.begin(); + auto kit = kinds.begin(), kite = kinds.end(); + for (; kit != kite; kit++, lit++) { + if (kit->kind == ReachWithoutError::AfterCall) + result.push_front(*lit); + else + result.push_back(*lit); + } + locations.assign(result.begin(), result.end()); +} + optional tryConvertResultJson(const ResultJson &resultJson, - const std::string &toolName, + ToolName toolName, const std::string &id) { - std::vector errors = {}; - 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 %s result", id.c_str()); - return nonstd::nullopt; - } + auto errors = + tryConvertErrorKind(toolName, resultJson.ruleId, resultJson.message); + if (errors.size() == 0) { + klee_warning("undefined error in %s result", id.c_str()); + return nonstd::nullopt; } std::vector> locations; + std::vector kinds; std::vector> metadatas; if (resultJson.codeFlows.size() > 0) { @@ -149,17 +227,21 @@ optional tryConvertResultJson(const ResultJson &resultJson, return nonstd::nullopt; } - auto maybeLocation = tryConvertLocationJson(*threadFlowLocation.location); - if (maybeLocation.has_value()) { - locations.push_back(*maybeLocation); + auto mblk = tryConvertLocationJson(toolName, resultJson.ruleId, + *threadFlowLocation.location); + if (mblk.has_value()) { + locations.push_back(mblk->first); + kinds.push_back(mblk->second); 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); + auto mblk = tryConvertLocationJson(toolName, resultJson.ruleId, + resultJson.locations[0]); + if (mblk.has_value()) { + locations.push_back(mblk->first); + kinds.push_back(mblk->second); } } @@ -167,12 +249,18 @@ optional tryConvertResultJson(const ResultJson &resultJson, return nonstd::nullopt; } + recoverCallChain(locations, kinds); + return Result{std::move(locations), std::move(metadatas), id, std::move(errors)}; } } // anonymous namespace namespace klee { +llvm::cl::opt LocationAccuracy( + "location-accuracy", cl::init(false), + cl::desc("Check location with line and column accuracy (default=false)")); + static const char *ReachWithErrorNames[] = { "DoubleFree", "UseAfterFree", @@ -183,11 +271,39 @@ static const char *ReachWithErrorNames[] = { "None", }; +void LineColumnRange::setRange(const KInstruction *ki) { + startLine = (endLine = ki->getLine()); + startColumn = (endColumn = ki->getColumn()); +} + +bool LineColumnRange::hasInsideInternal(InstrWithPrecision &kp) const { + auto line = kp.ptr->getLine(); + if (!(startLine <= line && line <= endLine)) { + kp.setNotFound(); + return false; + } + if (onlyLine()) { + kp.precision = Precision::Line; + return false; + } + auto column = kp.ptr->getColumn(); + auto ok = true; + if (line == startLine) + ok = column >= startColumn; + if (line == endLine) + ok = ok && column <= endColumn; + if (ok) + kp.precision = Precision::Column; + else + kp.precision = Precision::Line; + return false; +} + const char *getErrorString(ReachWithError error) { return ReachWithErrorNames[error]; } -std::string getErrorsString(const std::vector &errors) { +std::string getErrorsString(const ReachWithErrors &errors) { if (errors.size() == 1) { return getErrorString(*errors.begin()); } @@ -239,24 +355,38 @@ class NumericTraceId : public TraceId { void getNextId(const klee::ResultJson &resultJson) override { id++; } }; -TraceId *createTraceId(const std::string &toolName, - const std::vector &results) { - if (toolName == "Cooddy") - return new CooddyTraceId(); +std::unique_ptr +createTraceId(ToolName toolName, const std::vector &results) { + if (toolName == ToolName::Cooddy) + return std::make_unique(); else if (results.size() > 0 && results[0].id.has_value()) - return new GetterTraceId(); - return new NumericTraceId(); + return std::make_unique(); + return std::make_unique(); } void setResultId(const ResultJson &resultJson, bool useProperId, unsigned &id) { if (useProperId) { - assert(resultJson.id.has_value() && "all results must have an proper id"); + assert(resultJson.id.has_value() && "all results must have a proper id"); id = resultJson.id.value(); } else { ++id; } } +ToolName convertToolName(const std::string &toolName) { + if ("SecB" == toolName) + return ToolName::SecB; + if ("clang" == toolName) + return ToolName::clang; + if ("CppCheck" == toolName) + return ToolName::CppCheck; + if ("Infer" == toolName) + return ToolName::Infer; + if ("Cooddy" == toolName) + return ToolName::Cooddy; + return ToolName::Unknown; +} + SarifReport convertAndFilterSarifJson(const SarifReportJson &reportJson) { SarifReport report; @@ -267,9 +397,9 @@ SarifReport convertAndFilterSarifJson(const SarifReportJson &reportJson) { assert(reportJson.runs.size() == 1); const RunJson &run = reportJson.runs[0]; - const std::string toolName = run.tool.driver.name; + auto toolName = convertToolName(run.tool.driver.name); - TraceId *id = createTraceId(toolName, run.results); + auto id = createTraceId(toolName, run.results); for (const auto &resultJson : run.results) { id->getNextId(resultJson); @@ -279,7 +409,6 @@ SarifReport convertAndFilterSarifJson(const SarifReportJson &reportJson) { report.results.push_back(*maybeResult); } } - delete id; return report; } @@ -287,12 +416,228 @@ SarifReport convertAndFilterSarifJson(const SarifReportJson &reportJson) { Location::EquivLocationHashSet Location::cachedLocations; Location::LocationHashSet Location::locations; -ref Location::create(std::string filename_, unsigned int startLine_, +bool LocRange::hasInside(KInstruction *ki) const { + if (isa(ki->inst)) + return false; + InstrWithPrecision kp(ki); + hasInsideInternal(kp); + auto suitable = maxPrecision(); + return kp.precision >= suitable; +} + +void LocRange::hasInside(InstrWithPrecision &kp) { + if (kp.precision > maxPrecision()) { + kp.setNotFound(); + return; + } + if (hasInsideInternal(kp)) + setRange(kp.ptr); +} + +class InstructionRange : public LocRange { + LineColumnRange range; + OpCode opCode; + +public: + InstructionRange(LineColumnRange &&range, OpCode opCode) + : range(std::move(range)), opCode(opCode) {} + + LineColumnRange getRange() const final { return range; } + + Precision maxPrecision() const final { return Precision::Instruction; } + + size_t hash() const final { return hash_combine2(range.hash(), opCode); } + + std::string toString() const final { + return range.toString() + " " + std::to_string(opCode); + } + + void setRange(const KInstruction *ki) final { range.setRange(ki); } + + bool hasInsideInternal(InstrWithPrecision &kp) const final { + range.hasInsideInternal(kp); + if (kp.isNotFound()) + return false; + if (kp.ptr->inst->getOpcode() != opCode) + return false; + if (hasInsidePrecise(kp.ptr)) { + kp.precision = Precision::Instruction; + return true; + } else { + kp.precision = std::min(kp.precision, Precision::Column); + return false; + } + } + +protected: + virtual bool hasInsidePrecise(const KInstruction *ki) const { return true; } +}; + +namespace Cooddy { +using namespace llvm; + +class StoreNullRange final : public InstructionRange { + using InstructionRange::InstructionRange; + +public: + StoreNullRange(LineColumnRange &&range) + : InstructionRange(std::move(range), Instruction::Store) {} + + bool hasInsidePrecise(const KInstruction *ki) const final { + auto stinst = dyn_cast(ki->inst); + if (!stinst) + return false; + auto value = dyn_cast(stinst->getValueOperand()); + if (!value) + return false; + return value->isNullValue(); + } +}; + +class BranchRange final : public InstructionRange { + using InstructionRange::InstructionRange; + +public: + BranchRange(LineColumnRange &&range) + : InstructionRange(std::move(range), Instruction::Br) {} + + bool hasInsidePrecise(const KInstruction *ki) const final { + return ki->inst->getNumSuccessors() == 2; + } +}; + +struct OpCodeLoc final : public Location { + using Location::Location; + OpCodeLoc(std::string &&filename_, LineColumnRange &&range, EventKind &kind, + OpCode opCode) + : Location(std::move(filename_), + std::make_unique(std::move(range), opCode), + kind) {} +}; + +struct AfterLoc : public Location { + using Location::Location; + void isInsideInternal(BlockWithPrecision &bp, + const Instructions &origInsts) const final; + +protected: + virtual void isInsideInternal(BlockWithPrecision &bp, + const InstrWithPrecision &afterInst) const = 0; +}; + +struct AfterBlockLoc final : public AfterLoc { + using AfterLoc::AfterLoc; + AfterBlockLoc(std::string &&filename_, std::unique_ptr range, + EventKind &kind, unsigned indexOfNext) + : AfterLoc(std::move(filename_), std::move(range), kind), + indexOfNext(indexOfNext) {} + void isInsideInternal(BlockWithPrecision &bp, + const InstrWithPrecision &afterInst) const final; + +private: + unsigned indexOfNext; +}; + +struct AfterInstLoc final : public AfterLoc { + using AfterLoc::AfterLoc; + AfterInstLoc(std::string &&filename_, std::unique_ptr range, + EventKind &kind, OpCode opCode) + : AfterLoc(std::move(filename_), std::move(range), kind), opCode(opCode) { + } + void isInsideInternal(BlockWithPrecision &bp, + const InstrWithPrecision &afterInst) const final; + +private: + OpCode opCode; +}; +} // namespace Cooddy + +LineColumnRange create(unsigned int startLine_, optional endLine_, + optional startColumn_, + optional endColumn_) { + auto endLine = endLine_.has_value() ? *endLine_ : startLine_; + if (LocationAccuracy && startColumn_.has_value()) { + auto endColumn = endColumn_.has_value() ? *endColumn_ : *startColumn_; + return LineColumnRange(startLine_, *startColumn_, endLine, endColumn); + } + return LineColumnRange(startLine_, endLine); +} + +bool Location::operator==(const Location &other) const { + return filename == other.filename && + range->getRange() == other.range->getRange(); +} + +void Location::computeHash(EventKind &kind) { + hash_combine(hashValue, filename); + hash_combine(hashValue, range->hash()); + hash_combine(hashValue, kind); +} + +Location *Location::createCooddy(std::string &&filename_, + LineColumnRange &range, EventKind &kind) { + if (kind.isError) { + if (kind.kinds.size() == 1) { + switch (kind.kinds[0]) { + case ReachWithError::DoubleFree: + return new Cooddy::OpCodeLoc(std::move(filename_), std::move(range), + kind, Instruction::Call); + default: + return nullptr; + } + } else if (std::find(kind.kinds.begin(), kind.kinds.end(), + ReachWithError::MustBeNullPointerException) != + kind.kinds.end()) { + // the thing that Cooddy reports is too complex, so we fallback to just lines + range.clearColumns(); + return nullptr; + // return new Cooddy::OpCodeLoc(std::move(filename_), std::move(range), kind, Instruction::Load); + } + return nullptr; + } + switch (kind.kind) { + case ReachWithoutError::Return: + return new Cooddy::OpCodeLoc(std::move(filename_), std::move(range), kind, + Instruction::Ret); + case ReachWithoutError::NPESource: + return new Location( + std::move(filename_), + std::make_unique(std::move(range)), kind); + case ReachWithoutError::BranchTrue: + case ReachWithoutError::BranchFalse: { + auto succ = kind.kind == ReachWithoutError::BranchTrue ? 0 : 1; + return new Cooddy::AfterBlockLoc( + std::move(filename_), + std::make_unique(std::move(range)), kind, succ); + } + case ReachWithoutError::Free: + case ReachWithoutError::Call: + case ReachWithoutError::AfterCall: + return new Cooddy::OpCodeLoc(std::move(filename_), std::move(range), kind, + Instruction::Call); + case ReachWithoutError::Reach: + return nullptr; + } +} + +ref Location::create(std::string &&filename_, unsigned int startLine_, optional endLine_, optional startColumn_, - optional endColumn_) { - Location *loc = - new Location(filename_, startLine_, endLine_, startColumn_, endColumn_); + optional endColumn_, + ToolName toolName, EventKind &kind) { + auto range = klee::create(startLine_, endLine_, startColumn_, endColumn_); + Location *loc = nullptr; + switch (toolName) { + case ToolName::Cooddy: + loc = createCooddy(std::move(filename_), range, kind); + break; + default: + break; + } + if (!loc) + loc = + new Location(std::move(filename_), + std::make_unique(std::move(range)), kind); std::pair success = cachedLocations.insert(loc); if (success.second) { @@ -325,32 +670,103 @@ bool Location::isInside(const std::string &name) const { : (m == -1 && isOSSeparator(filename[n]))); } -bool Location::isInside(KBlock *block, const Instructions &origInsts) const { - auto first = block->getFirstInstruction(); - auto last = block->getLastInstruction(); - if (!startColumn.has_value()) { - if (first->getLine() > endLine) - return false; - return startLine <= last->getLine(); // and `first <= line` from above - } else { - for (size_t i = 0; i < block->numInstructions; ++i) { - auto inst = block->instructions[i]; - auto opCode = block->instructions[i]->inst->getOpcode(); - if (!isa(block->instructions[i]->inst) && - inst->getLine() <= endLine && inst->getLine() >= startLine && - inst->getColumn() <= *endColumn && - inst->getColumn() >= *startColumn && - origInsts.at(inst->getLine()).at(inst->getColumn()).count(opCode) != - 0) { - return true; +void Location::isInside(InstrWithPrecision &kp, + const Instructions &origInsts) const { + auto ki = kp.ptr; + // TODO: exterminate origInsts! + auto it = origInsts.find(ki->getLine()); + if (it == origInsts.end()) { + kp.setNotFound(); + return; + } + auto it2 = it->second.find(ki->getColumn()); + if (it2 == it->second.end()) { + kp.setNotFound(); + return; + } + if (!it2->second.count(ki->inst->getOpcode())) { + kp.setNotFound(); + return; + } + range->hasInside(kp); +} + +void Location::isInside(BlockWithPrecision &bp, + const Instructions &origInsts) const { + if (bp.precision > range->maxPrecision()) + bp.setNotFound(); + else + isInsideInternal(bp, origInsts); +} + +void Location::isInsideInternal(BlockWithPrecision &bp, + const Instructions &origInsts) const { + bool found = false; + for (size_t i = 0; i < bp.ptr->numInstructions; ++i) { + InstrWithPrecision kp(bp.ptr->instructions[i], bp.precision); + isInside(kp, origInsts); + if (kp.precision >= bp.precision) { + bp.precision = kp.precision; + found = true; + if (kp.precision >= range->maxPrecision()) { + bp.ptr = kp.ptr->parent; + return; } } + } + if (!found) + bp.setNotFound(); +} - return false; +void Cooddy::AfterLoc::isInsideInternal(BlockWithPrecision &bp, + const Instructions &origInsts) const { + // if (x + y > z && aaa->bbb->ccc->ddd) + // ^^^^^^^^^^^^^^^^^ first, skip all this + // second skip this ^^^^^^^^ (where Cooddy event points) + // finally, get this ^ (real location of needed instruction) + auto inside = false; + InstrWithPrecision afterInst(nullptr, bp.precision); + for (size_t i = 0; i < bp.ptr->numInstructions; ++i) { + afterInst.ptr = bp.ptr->instructions[i]; + auto kp = afterInst; + Location::isInside(kp, origInsts); + if (kp.precision >= afterInst.precision) { // first: go until Cooddy event + afterInst.precision = kp.precision; + inside = true; // first: reached Cooddy event + } else if (inside) // second: skip until left Coody event + break; + } + if (!inside) { // no Cooddy event in this Block + bp.setNotFound(); + return; + } + isInsideInternal(bp, afterInst); +} + +void Cooddy::AfterBlockLoc::isInsideInternal( + BlockWithPrecision &bp, const InstrWithPrecision &afterInst) const { + if (afterInst.precision != Precision::Instruction) { + bp.setNotFound(); + return; + } + auto nextBlock = + bp.ptr->basicBlock->getTerminator()->getSuccessor(indexOfNext); + bp.ptr = bp.ptr->parent->blockMap.at(nextBlock); + bp.precision = afterInst.precision; + range->setRange(bp.ptr->getFirstInstruction()); +} + +void Cooddy::AfterInstLoc::isInsideInternal( + BlockWithPrecision &bp, const InstrWithPrecision &afterInst) const { + if (afterInst.ptr->inst->getOpcode() != opCode) { + bp.precision = std::min(afterInst.precision, Precision::Column); + return; } + bp.precision = Precision::Instruction; + range->setRange(afterInst.ptr); } std::string Location::toString() const { - return filename + ":" + std::to_string(startLine); + return filename + ":" + range->toString(); } } // namespace klee diff --git a/lib/Module/Target.cpp b/lib/Module/Target.cpp index d2dde07919c..02167f902ac 100644 --- a/lib/Module/Target.cpp +++ b/lib/Module/Target.cpp @@ -20,21 +20,6 @@ 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)")); -} - -ErrorLocation::ErrorLocation(const klee::ref &loc) - : startLine(loc->startLine), endLine(loc->endLine), - startColumn(loc->startColumn), endColumn(loc->endColumn) {} - -ErrorLocation::ErrorLocation(const KInstruction *ki) { - startLine = (endLine = ki->getLine()); - startColumn = (endColumn = ki->getLine()); -} - std::string ReproduceErrorTarget::toString() const { std::ostringstream repr; repr << "Target " << getId() << ": "; @@ -75,10 +60,9 @@ ref Target::createCachedTarget(ref target) { return (ref)*(success.first); } -ref -ReproduceErrorTarget::create(const std::vector &_errors, - const std::string &_id, ErrorLocation _loc, - KBlock *_block) { +ref ReproduceErrorTarget::create(const ReachWithErrors &_errors, + const std::string &_id, + LineColumnRange _loc, KBlock *_block) { ReproduceErrorTarget *target = new ReproduceErrorTarget(_errors, _id, _loc, _block); return createCachedTarget(target); @@ -100,13 +84,8 @@ ref CoverBranchTarget::create(KBlock *_block, unsigned _branchCase) { return createCachedTarget(target); } -bool ReproduceErrorTarget::isTheSameAsIn(const KInstruction *instr) const { - const auto &errLoc = loc; - return instr->getLine() >= errLoc.startLine && - instr->getLine() <= errLoc.endLine && - (!LocationAccuracy || !errLoc.startColumn.has_value() || - (instr->getColumn() >= *errLoc.startColumn && - instr->getColumn() <= *errLoc.endColumn)); +bool ReproduceErrorTarget::isTheSameAsIn(KInstruction *instr) const { + return loc.hasInside(instr); } int Target::compare(const Target &b) const { return internalCompare(b); } diff --git a/lib/Module/TargetForest.cpp b/lib/Module/TargetForest.cpp index cb2f8a907e9..655b83baf4f 100644 --- a/lib/Module/TargetForest.cpp +++ b/lib/Module/TargetForest.cpp @@ -118,7 +118,7 @@ void TargetForest::Layer::addTrace( ref target = nullptr; if (i == result.locations.size() - 1) { target = ReproduceErrorTarget::create(result.errors, result.id, - ErrorLocation(loc), block); + loc->range->getRange(), block); } else { target = ReachBlockTarget::create(block); } diff --git a/test/Industry/MacroLocalization.c b/test/Industry/MacroLocalization.c new file mode 100644 index 00000000000..4d23801291a --- /dev/null +++ b/test/Industry/MacroLocalization.c @@ -0,0 +1,28 @@ +#include + +#define RETURN_NULL_OTHERWISE(sth) if ((sth) != 42) { \ + return NULL; \ + } + +int *foo(int *status) { + int st = *status; + RETURN_NULL_OTHERWISE(st); + return status; +} + +int main(int x) { + int *result = foo(&x); + return *result; +} + +// 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 --debug-localization --location-accuracy --analysis-reproduce=%s.sarif %t1.bc 2>&1 | FileCheck %s + +// CHECK: Industry/MacroLocalization.c:15-15 # 1 +// CHECK: Industry/MacroLocalization.c:9:3-9:28 1 # 2 +// CHECK: Industry/MacroLocalization.c:9:3-9:3 33 # 3 +// CHECK: Industry/MacroLocalization.c:14:17-14:17 56 # 3 +// CHECK: Target bc7beead5f814c9deac02e8e9e6eef08: error in %9 +// CHECK: KLEE: WARNING: 100.00% NullPointerException True Positive at trace bc7beead5f814c9deac02e8e9e6eef08 diff --git a/test/Industry/MacroLocalization.c.sarif b/test/Industry/MacroLocalization.c.sarif new file mode 100644 index 00000000000..4e97a418888 --- /dev/null +++ b/test/Industry/MacroLocalization.c.sarif @@ -0,0 +1,155 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "Cooddy", + "version": "v1_101", + "properties": { + "revisionTag": "bbceeffcbd252fd8a86aa2ed68549f3d713505ac" + } + } + }, + "invocations": [ + { + "executionSuccessful": true, + "exitCode": 0, + "commandLine": "build/release/cooddy --scope /home/user/pool --reporter sarif", + "executableLocation": { + "uri": "file:///home/user/src/cooddy/build/release/cooddy" + }, + "arguments": [ + "--scope", + "/home/user/pool", + "--reporter", + "sarif" + ], + "properties": { + "profileUri": "file:///home/user/src/cooddy/build/release/.cooddy/default.profile.json", + "annotationUris": [ + "file:///home/user/src/cooddy/build/release/.cooddy/.annotations.json", + "file:///home/user/src/cooddy/build/release/.cooddy/.java-annotations.json", + "file:///home/user/src/cooddy/build/release/.cooddy/.tinyxml-annotations.json", + "file:///home/user/src/cooddy/build/release/.cooddy/.huawei-annotations.json" + ] + }, + "startTimeUtc": "2023-07-05T13:43:00.892Z", + "endTimeUtc": "2023-07-05T13:50:26.467Z" + } + ], + "columnKind": "unicodeCodePoints", + "results": [ + { + "message": { + "text": "Dereferencing of \"result\" which can be null" + }, + "level": "error", + "ruleId": "NULL.DEREF", + "fingerprints": { + "cooddy.uid": "bc7beead5f814c9deac02e8e9e6eef08" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Industry/MacroLocalization.c" + }, + "region": { + "startLine": 15, + "endLine": 15, + "startColumn": 11, + "endColumn": 17 + } + } + } + ], + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "Industry/MacroLocalization.c" + }, + "region": { + "startLine": 9, + "endLine": 9, + "startColumn": 3, + "endColumn": 28, + "message": { + "text": "Null pointer source" + } + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "Industry/MacroLocalization.c" + }, + "region": { + "startLine": 9, + "endLine": 9, + "startColumn": 3, + "endColumn": 28, + "message": { + "text": "Assume return here" + } + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "Industry/MacroLocalization.c" + }, + "region": { + "startLine": 14, + "endLine": 14, + "startColumn": 17, + "endColumn": 23, + "message": { + "text": "Null pointer returned as the result" + } + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "Industry/MacroLocalization.c" + }, + "region": { + "startLine": 15, + "endLine": 15, + "startColumn": 11, + "endColumn": 17, + "message": { + "text": "Dereferencing of \"result\" which can be null" + } + } + } + } + } + ] + } + ] + } + ], + "verdict": "TP" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/StrangeBugInIFDS.c b/test/Industry/StrangeBugInIFDS.c new file mode 100644 index 00000000000..5f055a7f9c7 --- /dev/null +++ b/test/Industry/StrangeBugInIFDS.c @@ -0,0 +1,27 @@ +#include +#include + +typedef struct { int magic; } Struct; + +void Exec(void **para) { + if (para == NULL) + return; // event 3 + *para = malloc(sizeof(Struct)); +} + +void* Alloc() { + void *buf = NULL; // event 1 + Exec(&buf); // event 2 + return buf; // event 4 +} + +void Init() { + Struct *p = Alloc(); + p->magic = 42; // event 5| +} +// 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 --analysis-reproduce=%s.sarif %t1.bc 2>&1 | FileCheck %s + +// CHECK: KLEE: WARNING: 100.00% (MayBeNullPointerException|NullPointerException) False Positive at trace somebigid123 diff --git a/test/Industry/StrangeBugInIFDS.c.sarif b/test/Industry/StrangeBugInIFDS.c.sarif new file mode 100644 index 00000000000..37a925b7600 --- /dev/null +++ b/test/Industry/StrangeBugInIFDS.c.sarif @@ -0,0 +1,173 @@ +{ + "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json", + "version": "2.1.0", + "runs": [ + { + "tool": { + "driver": { + "name": "Cooddy", + "version": "v1_101", + "properties": { + "revisionTag": "bbceeffcbd252fd8a86aa2ed68549f3d713505ac" + } + } + }, + "invocations": [ + { + "executionSuccessful": true, + "exitCode": 0, + "commandLine": "build/release/cooddy --scope /home/user/pool --reporter sarif", + "executableLocation": { + "uri": "file:///home/user/src/cooddy/build/release/cooddy" + }, + "arguments": [ + "--scope", + "/home/user/pool", + "--reporter", + "sarif" + ], + "properties": { + "profileUri": "file:///home/user/src/cooddy/build/release/.cooddy/default.profile.json", + "annotationUris": [ + "file:///home/user/src/cooddy/build/release/.cooddy/.annotations.json", + "file:///home/user/src/cooddy/build/release/.cooddy/.java-annotations.json", + "file:///home/user/src/cooddy/build/release/.cooddy/.tinyxml-annotations.json", + "file:///home/user/src/cooddy/build/release/.cooddy/.huawei-annotations.json" + ] + }, + "startTimeUtc": "2023-07-05T13:43:00.892Z", + "endTimeUtc": "2023-07-05T13:50:26.467Z" + } + ], + "columnKind": "unicodeCodePoints", + "results": [ + { + "message": { + "text": "Dereferencing of \"p\" which can be null" + }, + "level": "error", + "ruleId": "NULL.DEREF", + "fingerprints": { + "cooddy.uid": "somebigid123" + }, + "locations": [ + { + "physicalLocation": { + "artifactLocation": { + "uri": "Industry/StrangeBugInIFDS.c" + }, + "region": { + "startLine": 19, + "endLine": 19, + "startColumn": 3, + "endColumn": 4 + } + } + } + ], + "codeFlows": [ + { + "threadFlows": [ + { + "locations": [ + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "Industry/StrangeBugInIFDS.c" + }, + "region": { + "startLine": 6, + "endLine": 6, + "startColumn": 15, + "endColumn": 19, + "message": { + "text": "Null pointer source" + } + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "Industry/StrangeBugInIFDS.c" + }, + "region": { + "startLine": 7, + "endLine": 7, + "startColumn": 8, + "endColumn": 12, + "message": { + "text": "Null pointer passed as 1st argument" + } + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "Industry/StrangeBugInIFDS.c" + }, + "region": { + "startLine": 13, + "endLine": 13, + "startColumn": 5, + "endColumn": 11, + "message": { + "text": "Assume return here" + } + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "Industry/StrangeBugInIFDS.c" + }, + "region": { + "startLine": 8, + "endLine": 8, + "startColumn": 10, + "endColumn": 13, + "message": { + "text": "Null pointer returned as the result" + } + } + } + } + }, + { + "location": { + "physicalLocation": { + "artifactLocation": { + "uri": "Industry/StrangeBugInIFDS.c" + }, + "region": { + "startLine": 19, + "endLine": 19, + "startColumn": 3, + "endColumn": 4, + "message": { + "text": "Dereferencing of \"p\" which can be null" + } + } + } + } + } + ] + } + ] + } + ], + "verdict": "TP" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Industry/if2.c b/test/Industry/if2.c index d52edf529f9..55695c0edbb 100644 --- a/test/Industry/if2.c +++ b/test/Industry/if2.c @@ -11,7 +11,7 @@ int main(int x) { // REQUIRES: z3 // RUN: %clang %s -emit-llvm -c -g -O0 -Xclang -disable-O0-optnone -o %t1.bc // RUN: rm -rf %t.klee-out -// RUN: %klee --output-dir=%t.klee-out --use-guided-search=error --mock-external-calls --skip-not-symbolic-objects --skip-not-lazy-initialized --check-out-of-memory --search=bfs --max-stepped-instructions=20 --max-cycles-before-stuck=0 --use-lazy-initialization=only --analysis-reproduce=%s.json %t1.bc +// RUN: %klee --output-dir=%t.klee-out --use-guided-search=error --mock-external-calls --skip-not-symbolic-objects --skip-not-lazy-initialized --check-out-of-memory --search=bfs --max-stepped-instructions=19 --max-cycles-before-stuck=0 --location-accuracy --use-lazy-initialization=only --analysis-reproduce=%s.json %t1.bc // RUN: FileCheck -input-file=%t.klee-out/warnings.txt %s -check-prefix=CHECK-NONE // CHECK-NONE: KLEE: WARNING: 50.00% NullPointerException False Positive at trace 1 // RUN: FileCheck -input-file=%t.klee-out/messages.txt %s -check-prefix=CHECK-DISTANCE