Skip to content

Commit

Permalink
[feat] Cooddy NPE location handling
Browse files Browse the repository at this point in the history
  • Loading branch information
Columpio committed Oct 5, 2023
1 parent c8b06fd commit 8b7a91c
Show file tree
Hide file tree
Showing 16 changed files with 1,147 additions and 219 deletions.
4 changes: 2 additions & 2 deletions include/klee/Core/TargetedExecutionReporter.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ ty min(ty left, ty right);
}; // namespace confidence

void reportFalsePositive(confidence::ty confidence,
const std::vector<ReachWithError> &errors,
const std::string &id, std::string whatToIncrease);
const ReachWithErrors &errors, const std::string &id,
std::string whatToIncrease);

} // namespace klee

Expand Down
1 change: 1 addition & 0 deletions include/klee/Module/KInstruction.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class Instruction;
namespace klee {
class Executor;
class KModule;
struct KFunction;
struct KBlock;

/// KInstruction - Intermediate instruction representation used
Expand Down
218 changes: 175 additions & 43 deletions include/klee/Module/SarifReport.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <vector>

#include "klee/ADT/Ref.h"
#include "llvm/IR/IntrinsicInst.h"
#include <nlohmann/json.hpp>
#include <nonstd/optional.hpp>

Expand Down Expand Up @@ -52,9 +53,10 @@ enum ReachWithError {
Reachable,
None,
};
using ReachWithErrors = std::vector<ReachWithError>;

const char *getErrorString(ReachWithError error);
std::string getErrorsString(const std::vector<ReachWithError> &errors);
std::string getErrorsString(const ReachWithErrors &errors);

struct FunctionInfo;
struct KBlock;
Expand All @@ -63,11 +65,16 @@ struct ArtifactLocationJson {
optional<std::string> uri;
};

struct Message {
std::string text;
};

struct RegionJson {
optional<unsigned int> startLine;
optional<unsigned int> endLine;
optional<unsigned int> startColumn;
optional<unsigned int> endColumn;
optional<Message> message;
};

struct PhysicalLocationJson {
Expand All @@ -92,10 +99,6 @@ struct CodeFlowJson {
std::vector<ThreadFlowJson> threadFlows;
};

struct Message {
std::string text;
};

struct Fingerprints {
std::string cooddy_uid;
};
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -165,6 +168,148 @@ 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 <class T> 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<KBlock>;
using InstrWithPrecision = WithPrecision<KInstruction>;

inline size_t hash_combine2(std::size_t s, std::size_t v) {
return s ^ (v + 0x9e3779b9 + (s << 6) + (s >> 2));
}

template <class T> inline void hash_combine(std::size_t &s, const T &v) {
std::hash<T> 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<klee::EventKind> {
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<size_t>::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(); }
Expand All @@ -184,35 +329,30 @@ struct Location {
}
};
std::string filename;
unsigned int startLine;
unsigned int endLine;
optional<unsigned int> startColumn;
optional<unsigned int> endColumn;
std::unique_ptr<LocRange> range;

static ref<Location> create(std::string filename_, unsigned int startLine_,
static ref<Location> create(std::string &&filename_, unsigned int startLine_,
optional<unsigned int> endLine_,
optional<unsigned int> startColumn_,
optional<unsigned int> endColumn_);
optional<unsigned int> 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<unsigned int, std::unordered_set<unsigned int>>>;
std::unordered_map<unsigned int, std::unordered_set<OpCode>>>;

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;

Expand All @@ -226,28 +366,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 <class T> inline void hash_combine(std::size_t &s, const T &v) {
std::hash<T> 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<unsigned int> endLine_, optional<unsigned int> startColumn_,
optional<unsigned int> 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<LocRange> range,
EventKind &kind)
: filename(std::move(filename_)), range(std::move(range)) {
computeHash(kind);
}

virtual void isInsideInternal(BlockWithPrecision &bp,
const Instructions &origInsts) const;
};

struct RefLocationHash {
Expand All @@ -262,9 +394,9 @@ struct RefLocationCmp {

struct Result {
std::vector<ref<Location>> locations;
std::vector<optional<json>> metadatas;
std::string id;
std::vector<ReachWithError> errors;
const std::vector<optional<json>> metadatas;
const std::string id;
const ReachWithErrors errors;
};

struct SarifReport {
Expand Down
31 changes: 10 additions & 21 deletions include/klee/Module/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,6 @@ DISABLE_WARNING_POP
namespace klee {
using nonstd::optional;

struct ErrorLocation {
unsigned int startLine;
unsigned int endLine;
optional<unsigned int> startColumn;
optional<unsigned int> endColumn;

ErrorLocation(const klee::ref<klee::Location> &loc);
ErrorLocation(const KInstruction *ki);
};

class ReproduceErrorTarget;

class Target {
Expand Down Expand Up @@ -174,23 +164,22 @@ class CoverBranchTarget : public Target {

class ReproduceErrorTarget : public Target {
private:
std::vector<ReachWithError>
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<ReachWithError> &_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<Target> create(const std::vector<ReachWithError> &_errors,
const std::string &_id, ErrorLocation _loc,
static ref<Target> create(const ReachWithErrors &_errors,
const std::string &_id, LineColumnRange _loc,
KBlock *_block);

Kind getKind() const override { return Kind::ReproduceError; }
Expand All @@ -207,12 +196,12 @@ class ReproduceErrorTarget : public Target {

bool isReported = false;

const std::vector<ReachWithError> &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

Expand Down
2 changes: 1 addition & 1 deletion lib/Core/Executor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Loading

0 comments on commit 8b7a91c

Please sign in to comment.