From a84c6ceaf3c1bc180fa60345de1bca664752bb7b 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/Module/SarifReport.h | 35 +++++++- lib/Core/TargetedExecutionManager.cpp | 16 +++- lib/Module/SarifReport.cpp | 110 ++++++++++++++++++++++---- 3 files changed, 139 insertions(+), 22 deletions(-) diff --git a/include/klee/Module/SarifReport.h b/include/klee/Module/SarifReport.h index 09a59d4d25..5b107e01da 100644 --- a/include/klee/Module/SarifReport.h +++ b/include/klee/Module/SarifReport.h @@ -16,6 +16,7 @@ #include #include "klee/ADT/Ref.h" +#include "klee/Module/KInstruction.h" #include #include @@ -165,6 +166,29 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(RunJson, results, tool) NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(SarifReportJson, runs) +enum class Precision { + NotFound = 0, + LineLevel = 1, + ColumnLevel = 2 +}; + +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() { + ptr = nullptr; + precision = Precision::NotFound; + } +}; +using BlockWithPrecision = WithPrecision; +using InstrWithPrecision = WithPrecision; + struct Location { struct LocationHash { std::size_t operator()(const Location *l) const { return l->hash(); } @@ -194,7 +218,7 @@ struct Location { optional startColumn_, optional endColumn_); - ~Location(); + virtual ~Location(); std::size_t hash() const { return hashValue; } /// @brief Required by klee::ref-managed objects @@ -212,7 +236,9 @@ struct Location { unsigned int, std::unordered_map>>; - bool isInside(KBlock *block, const Instructions &origInsts) const; + void isInside(InstrWithPrecision &kp, const Instructions &origInsts) const; + + virtual void isInside(BlockWithPrecision &bp, const Instructions &origInsts); std::string toString() const; @@ -250,6 +276,11 @@ struct Location { } }; +struct CoodyNPELocation : public Location { + CoodyNPELocation(const Location &loc) : Location(loc) {} + void isInside(BlockWithPrecision &bp, const Instructions &origInsts) override; +}; + struct RefLocationHash { unsigned operator()(const ref &t) const { return t->hash(); } }; diff --git a/lib/Core/TargetedExecutionManager.cpp b/lib/Core/TargetedExecutionManager.cpp index 25357773ae..9b827931b2 100644 --- a/lib/Core/TargetedExecutionManager.cpp +++ b/lib/Core/TargetedExecutionManager.cpp @@ -321,6 +321,8 @@ TargetedExecutionManager::prepareAllLocations(KModule *kmodule, const auto &infos = kmodule->infos; for (auto it = locations.begin(); it != locations.end(); ++it) { auto loc = *it; + auto precision = Precision::LineLevel; + Blocks blocks; for (const auto &fileName : infos->getFilesNames()) { if (kmodule->origInfos.count(fileName) == 0) { continue; @@ -338,14 +340,22 @@ TargetedExecutionManager::prepareAllLocations(KModule *kmodule, const auto &origInstsInFile = kmodule->origInfos.at(fi.file); 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 (!blocks.empty()) + locToBlocks.emplace(loc, std::move(blocks)); } return locToBlocks; diff --git a/lib/Module/SarifReport.cpp b/lib/Module/SarifReport.cpp index 075ce32417..286ca32257 100644 --- a/lib/Module/SarifReport.cpp +++ b/lib/Module/SarifReport.cpp @@ -121,6 +121,21 @@ tryConvertRuleJson(const std::string &ruleId, const std::string &toolName, } } +void +tryConvertMessage(const std::string &toolName, const optional &errorMessage, ref &loc) { + if (toolName != "Cooddy" || !errorMessage.has_value()) + return; + std::string start = "Dereferencing of \""; + std::string end = "\" which can be null"; + auto &text = errorMessage->text; + if (text.substr(0, start.size()) == start && + text.substr(text.size() - end.size(), end.size()) == end) { + auto size = text.size() - end.size() - start.size(); + auto derefedExpr = text.substr(start.size(), size); + loc = new CoodyNPELocation(*loc); + } +} + optional tryConvertResultJson(const ResultJson &resultJson, const std::string &toolName, const std::string &id) { @@ -166,6 +181,7 @@ optional tryConvertResultJson(const ResultJson &resultJson, if (locations.empty()) { return nonstd::nullopt; } + tryConvertMessage(toolName, resultJson.message, locations.back()); return Result{std::move(locations), std::move(metadatas), id, std::move(errors)}; @@ -325,27 +341,87 @@ 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()->info; - auto last = block->getLastInstruction()->info; +void Location::isInside(InstrWithPrecision &kp, const Instructions &origInsts) const { + auto ki = kp.ptr; + auto inst = ki->info; + if (!isa(ki->inst) && startLine <= inst->line && inst->line <= endLine) { + auto opCode = ki->inst->getOpcode(); + if (*startColumn <= inst->column && inst->column <= *endColumn && + origInsts.at(inst->line).at(inst->column).count(opCode) != 0) + kp.precision = Precision::ColumnLevel; + else + kp.precision = Precision::LineLevel; + return; + } +} + +void Location::isInside(BlockWithPrecision &bp, const Instructions &origInsts) { 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; - } + auto first = bp.ptr->getFirstInstruction()->info; + auto last = bp.ptr->getLastInstruction()->info; + if (first->line <= endLine && startLine <= last->line) + bp.precision = Precision::LineLevel; + else + bp.setNotFound(); + return; + } + auto tmpBP = bp; + bp.setNotFound(); + for (size_t i = 0; i < tmpBP.ptr->numInstructions; ++i) { + InstrWithPrecision kp(tmpBP.ptr->instructions[i]); + isInside(kp, origInsts); + if (kp.precision >= tmpBP.precision) { + tmpBP.precision = kp.precision; + bp = tmpBP; } + } +} - return false; +void CoodyNPELocation::isInside(BlockWithPrecision &bp, const Instructions &origInsts) { + // 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 `Load` instruction) + auto block = bp.ptr; + size_t start = 0; + auto inside = false; + auto precision = bp.precision; + KInstruction *ki = nullptr; + for (; start < block->numInstructions; ++start) { + ki = block->instructions[start]; + InstrWithPrecision kp(ki); + Location::isInside(kp, origInsts); + if (kp.precision >= precision) { // first: go until Cooddy event + precision = kp.precision; + if (!inside) // first: reached Cooddy event + inside = true; + } else if (inside) // second: skip until left Coody event + break; + } + if (!inside) { // no Cooddy event in this Block + bp.setNotFound(); + return; + } + if (precision == Precision::LineLevel) { + bp.precision = precision; + return; + } + // finally: Load instruction + if (ki->inst->getOpcode() == Instruction::Load) { + // we got precise instruction, so redefine the location + startLine = (endLine = ki->info->line); + startColumn = (endColumn = ki->info->column); + bp.precision = Precision::ColumnLevel; + return; + } + // most probably Cooddy points to a macro call + for (size_t i = 0; i < start; i++) { + if (block->instructions[i]->inst->getOpcode() == Instruction::Load) { + bp.precision = Precision::LineLevel; + return; + } } + bp.setNotFound(); } std::string Location::toString() const {