#include #include #include "fold/fuzz/prediction_match.hpp" TEST_CASE("fuzz correlation selects the hazard compatible with the sanitizer crash", "[fuzz][prediction-match]") { fold::fuzz::AsanReport report; report.kind = "heap-buffer-overflow"; report.file = "/tmp/project/target.cpp"; report.line = 42; report.column = 7; std::vector predictions = { {"null-deref:/tmp/project/target.cpp:42:7", "target", fold::FindingKind::NullDeref, {"/tmp/project/target.cpp", 42, 7}}, {"oob-access:/tmp/project/target.cpp:42:7", "target", fold::FindingKind::OOBAccess, {"/tmp/project/target.cpp", 42, 7}}, }; auto matched = fold::fuzz::matchSanitizerReport(report, predictions); REQUIRE(matched == 1); } TEST_CASE("fuzz correlation rejects same-basename crashes from another directory", "[fuzz][prediction-match]") { fold::fuzz::AsanReport report; report.kind = "SEGV"; report.file = "/tmp/other/target.cpp"; report.line = 12; std::vector predictions = { {"null-deref:/tmp/project/target.cpp:12:0", "target", fold::FindingKind::NullDeref, {"/tmp/project/target.cpp", 12, 0}}, }; REQUIRE_FALSE(fold::fuzz::matchSanitizerReport(report, predictions)); } TEST_CASE("fuzz correlation accepts relative ASan source paths", "[fuzz][prediction-match]") { fold::fuzz::AsanReport report; report.kind = "SEGV"; report.file = "fixture.c"; report.line = 6; report.function = "leaky"; std::vector predictions = { {"null-deref:fixture.c:6:16", "leaky", fold::FindingKind::NullDeref, {"/tmp/project/fixture.c", 6, 16}}, }; REQUIRE(fold::fuzz::matchSanitizerReport(report, predictions) == 0); } TEST_CASE("fuzz correlation rejects incompatible concrete failure classes", "[fuzz][prediction-match]") { fold::fuzz::AsanReport report; report.kind = "heap-buffer-overflow"; report.file = "/tmp/project/target.cpp"; report.line = 9; std::vector predictions = { {"null-deref:/tmp/project/target.cpp:9:0", "target", fold::FindingKind::NullDeref, {"/tmp/project/target.cpp", 9, 0}}, }; REQUIRE_FALSE(fold::fuzz::matchSanitizerReport(report, predictions)); } TEST_CASE("fuzz correlation accepts a unique compatible function for locationless reports", "[fuzz][prediction-match]") { fold::fuzz::AsanReport report; report.kind = "SEGV"; report.function = "leaky+0x70"; std::vector predictions = { {"null-deref:fixture.c:6:16", "leaky", fold::FindingKind::NullDeref, {"/tmp/project/fixture.c", 6, 16}}, {"oob-access:fixture.c:9:3", "other", fold::FindingKind::OOBAccess, {"/tmp/project/fixture.c", 9, 3}}, }; REQUIRE(fold::fuzz::matchSanitizerReport(report, predictions) == 0); } TEST_CASE("fuzz correlation rejects ambiguous locationless reports", "[fuzz][prediction-match]") { fold::fuzz::AsanReport report; report.kind = "SEGV"; report.function = "leaky+0x70"; std::vector predictions = { {"null-deref:fixture.c:6:16", "leaky", fold::FindingKind::NullDeref, {"/tmp/project/fixture.c", 6, 16}}, {"null-deref:fixture.c:8:16", "leaky", fold::FindingKind::NullDeref, {"/tmp/project/fixture.c", 8, 16}}, }; REQUIRE_FALSE(fold::fuzz::matchSanitizerReport(report, predictions)); } TEST_CASE("fuzz correlation accepts duplicate seeds for the same finding", "[fuzz][prediction-match]") { fold::fuzz::AsanReport report; report.kind = "SEGV"; report.function = "leaky+0x70"; std::vector predictions = { {"null-deref:fixture.c:6:16", "leaky", fold::FindingKind::NullDeref, {"/tmp/project/fixture.c", 6, 16}}, {"null-deref:fixture.c:6:16", "leaky", fold::FindingKind::NullDeref, {"/tmp/project/fixture.c", 6, 16}}, }; REQUIRE(fold::fuzz::matchSanitizerReport(report, predictions) == 0); } TEST_CASE("fuzz correlation rejects a locationless report from another function", "[fuzz][prediction-match]") { fold::fuzz::AsanReport report; report.kind = "SEGV"; report.function = "leaky_more+0x70"; std::vector predictions = { {"null-deref:fixture.c:6:16", "leaky", fold::FindingKind::NullDeref, {"/tmp/project/fixture.c", 6, 16}}, }; REQUIRE_FALSE(fold::fuzz::matchSanitizerReport(report, predictions)); } TEST_CASE("unpredicted crash IDs are stable and artifact-specific", "[fuzz][crash-id]") { fold::fuzz::AsanReport report; report.kind = "SEGV"; report.function = "parse input+0x70"; const auto first = fold::fuzz::unpredictedCrashFindingId(report, "/tmp/run/crashes/crash-0123456789abcdef"); const auto repeated = fold::fuzz::unpredictedCrashFindingId(report, "/tmp/run/crashes/crash-0123456789abcdef"); const auto other = fold::fuzz::unpredictedCrashFindingId(report, "/tmp/run/crashes/crash-fedcba9876543210"); REQUIRE(first == repeated); REQUIRE(first != other); REQUIRE(first == "fuzz-crash:SEGV:parse-input+0x70:crash-0123456789abcdef"); REQUIRE(first.find("::0:0") == std::string::npos); }