Multi-phase work merged into one commit:
* M19: flow-sensitive guard tracking (stmtAlwaysExits, walkIf early-exit)
* M20–M27: non-null marking across deref, addr-of, callsite preconds,
if/while/switch guards, AddrOf-of-subexpr, CFG-block-scoped maps
* M30 Phase A: dialect-agnostic SourceLoc + LocationMapper + frontend/
boundary marker; ClangLocationMapper wired through SafetyObserver
* M30 Phase B v2: QueryTracer + 13 SafetyObserver hooks migrated to
ir::Expr / ir::Call / ir::MemberRef / ir::Symbol / ir::SourceLoc
* M30 Phase C/D/D2/D3: ir::Symbol, ir::Expr (with Kind), ir::Call,
ir::MemberRef opaque types; ir_expr_clang.cpp houses the clang
adapters; encodeExpr migrated to switch(kind) dispatch
Scout-rule extractions from encoder.cpp (2800 → 2371 lines):
* encoder_callee_predicates.{hpp,cpp} — isMallocLikeCall, isFreeCall,
isAssertCall as pure std::string_view predicates
* encoder_type_queries.{hpp,cpp} — typeSizeBytes, sourceWidth,
isUnsignedLike, byteWidthExpr
* encoder_callee_names.{hpp,cpp} — calleeName, qualifiedFunctionName,
qualifiedCalleeName (with internal-linkage @-suffix)
* encoder_coerce.{hpp,cpp} — coerceToBv, coerceToExactBv, coerceToBool,
coerceToType (BV-64 storage model documented at the boundary)
* encoder_reachability.{hpp,cpp} — ReachabilityCounter impl, markEncoded,
recordEncoded, recordBailedNode, public profiling glue
* encoder_summary_instantiate.cpp — SMT-LIB summary substitution
(instantiateSummaryExpr / Predicate / PredicateInContext +
applySummaryParamPost; kills the redundant *Impl pass-through layer)
137 lines
4.4 KiB
C++
137 lines
4.4 KiB
C++
#include <clang/Tooling/CompilationDatabase.h>
|
|
|
|
#include <catch2/catch_test_macros.hpp>
|
|
#include <chrono>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <nlohmann/json.hpp>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "fold/cli/scan_options.hpp"
|
|
#include "fold/cli/scan_runner.hpp"
|
|
#include "fold/detect/summary_store.hpp"
|
|
#include "fold/frontend/comp_db.hpp"
|
|
|
|
namespace {
|
|
|
|
struct TempDir {
|
|
std::filesystem::path path;
|
|
|
|
TempDir()
|
|
: path(std::filesystem::temp_directory_path() /
|
|
("fold-yams-integration-" +
|
|
std::to_string(std::chrono::steady_clock::now().time_since_epoch().count()))) {
|
|
std::filesystem::create_directories(path);
|
|
}
|
|
|
|
~TempDir() { std::filesystem::remove_all(path); }
|
|
};
|
|
|
|
void writeFile(const std::filesystem::path& path, const std::string& content) {
|
|
std::ofstream(path) << content;
|
|
}
|
|
|
|
void writeCompileDb(const std::filesystem::path& dir, const std::filesystem::path& src) {
|
|
nlohmann::json db = nlohmann::json::array();
|
|
db.push_back({
|
|
{"directory", dir.string()},
|
|
{"command", std::string("clang++ -std=c++20 -c ") + src.string()},
|
|
{"file", src.string()},
|
|
});
|
|
writeFile(dir / "compile_commands.json", db.dump());
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("yams taint integration: readString to system", "[integration][yams][taint]") {
|
|
TempDir dir;
|
|
const auto src = dir.path / "main.cpp";
|
|
|
|
writeFile(src, "namespace std {\n"
|
|
" class string {\n"
|
|
" public:\n"
|
|
" const char* c_str() const;\n"
|
|
" };\n"
|
|
"}\n"
|
|
"namespace yams::daemon {\n"
|
|
" struct BinaryDeserializer {\n"
|
|
" std::string readString();\n"
|
|
" };\n"
|
|
"}\n"
|
|
"extern \"C\" int system(const char*);\n"
|
|
"int run(yams::daemon::BinaryDeserializer& bd) {\n"
|
|
" std::string cmd = bd.readString();\n"
|
|
" return system(cmd.c_str());\n"
|
|
"}\n");
|
|
|
|
writeCompileDb(dir.path, src);
|
|
|
|
std::string err;
|
|
auto db = fold::loadCompilationDatabase(dir.path.string(), err);
|
|
REQUIRE(db);
|
|
|
|
fold::cli::ScanOptions opts;
|
|
opts.compile_db = dir.path.string();
|
|
opts.detectors = {"taint-track"};
|
|
|
|
auto result = fold::cli::runDetectorsOnShard(*db, opts, {src.string()},
|
|
/*include_clone_clustering=*/false);
|
|
|
|
bool saw_taint = false;
|
|
for (const auto& finding : result.findings) {
|
|
if (finding.kind == fold::FindingKind::TaintTrack) {
|
|
saw_taint = true;
|
|
}
|
|
}
|
|
REQUIRE(saw_taint);
|
|
}
|
|
|
|
TEST_CASE("yams taint integration: Result::value() propagation", "[integration][yams][taint]") {
|
|
TempDir dir;
|
|
const auto src = dir.path / "main.cpp";
|
|
|
|
writeFile(src, "namespace std {\n"
|
|
" class string {\n"
|
|
" public:\n"
|
|
" const char* c_str() const;\n"
|
|
" };\n"
|
|
"}\n"
|
|
"template<typename T>\n"
|
|
"struct Result {\n"
|
|
" T val;\n"
|
|
" T& value() { return val; }\n"
|
|
"};\n"
|
|
"namespace yams::daemon {\n"
|
|
" struct BinaryDeserializer {\n"
|
|
" std::string readString();\n"
|
|
" };\n"
|
|
"}\n"
|
|
"extern \"C\" int system(const char*);\n"
|
|
"int run(yams::daemon::BinaryDeserializer& bd) {\n"
|
|
" Result<std::string> cmd;\n"
|
|
" cmd.val = bd.readString();\n"
|
|
" return system(cmd.value().c_str());\n"
|
|
"}\n");
|
|
|
|
writeCompileDb(dir.path, src);
|
|
|
|
std::string err;
|
|
auto db = fold::loadCompilationDatabase(dir.path.string(), err);
|
|
REQUIRE(db);
|
|
|
|
fold::cli::ScanOptions opts;
|
|
opts.compile_db = dir.path.string();
|
|
opts.detectors = {"taint-track"};
|
|
|
|
auto result = fold::cli::runDetectorsOnShard(*db, opts, {src.string()},
|
|
/*include_clone_clustering=*/false);
|
|
|
|
bool saw_taint = false;
|
|
for (const auto& finding : result.findings) {
|
|
if (finding.kind == fold::FindingKind::TaintTrack) {
|
|
saw_taint = true;
|
|
}
|
|
}
|
|
REQUIRE(saw_taint);
|
|
}
|