fold/tests/unit/test_asan_parser.cpp

117 lines
4.1 KiB
C++

#include <catch2/catch_test_macros.hpp>
#include <string>
#include "fold/fuzz/asan_parser.hpp"
using fold::fuzz::parseAsanReport;
namespace {
constexpr const char* kHeapBufferOverflow =
R"(==12345==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000010
READ of size 4 at 0x602000000010 thread T0
#0 0x100abc in oobtest /tmp/oob.c:3:19
#1 0x100def in LLVMFuzzerTestOneInput harness.cc:14:5
SUMMARY: AddressSanitizer: heap-buffer-overflow /tmp/oob.c:3:19 in oobtest
)";
constexpr const char* kSegv = R"(AddressSanitizer:DEADLYSIGNAL
=================================================================
==99==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000
The signal is caused by a READ memory access.
Hint: address points to the zero page.
#0 0x7f leaky /tmp/null.c:5:16
SUMMARY: AddressSanitizer: SEGV /tmp/null.c:5:16 in leaky
)";
constexpr const char* kUbsanOverflow =
R"(/tmp/ov.c:8:14: runtime error: signed integer overflow: ...
SUMMARY: UndefinedBehaviorSanitizer: signed-integer-overflow /tmp/ov.c:8:14
)";
constexpr const char* kRealUbsanDivision =
R"(/tmp/div.c:7:16: runtime error: division by zero
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /tmp/div.c:7:16 in divide
)";
constexpr const char* kRealUbsanOverflow =
R"(/tmp/ov.c:8:14: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /tmp/ov.c:8:14 in add
)";
constexpr const char* kCleanRun = "INFO: libFuzzer ran the input but no crash.\n";
constexpr const char* kSummaryWithoutColumn =
"SUMMARY: AddressSanitizer: heap-use-after-free src/foo.c:42 in worker\n";
} // namespace
TEST_CASE("parseAsanReport extracts heap-buffer-overflow + file:line:col", "[fuzz][asan]") {
auto r = parseAsanReport(kHeapBufferOverflow);
REQUIRE(r.has_value());
REQUIRE(r->valid());
REQUIRE(r->kind == "heap-buffer-overflow");
REQUIRE(r->file == "/tmp/oob.c");
REQUIRE(r->line == 3);
REQUIRE(r->column == 19);
REQUIRE(r->function == "oobtest");
}
TEST_CASE("parseAsanReport extracts SEGV from deadly-signal block", "[fuzz][asan]") {
auto r = parseAsanReport(kSegv);
REQUIRE(r.has_value());
REQUIRE(r->kind == "SEGV");
REQUIRE(r->file == "/tmp/null.c");
REQUIRE(r->line == 5);
REQUIRE(r->column == 16);
REQUIRE(r->function == "leaky");
}
TEST_CASE("parseAsanReport handles UBSan signed-integer-overflow", "[fuzz][asan]") {
auto r = parseAsanReport(kUbsanOverflow);
REQUIRE(r.has_value());
REQUIRE(r->kind == "signed-integer-overflow");
REQUIRE(r->file == "/tmp/ov.c");
REQUIRE(r->line == 8);
REQUIRE(r->column == 14);
// UBSan summary doesn't include an `in <function>` clause.
REQUIRE(r->function.empty());
}
TEST_CASE("parseAsanReport recovers division class from a generic UBSan summary", "[fuzz][ubsan]") {
auto r = parseAsanReport(kRealUbsanDivision);
REQUIRE(r.has_value());
REQUIRE(r->kind == "division-by-zero");
REQUIRE(r->file == "/tmp/div.c");
REQUIRE(r->line == 7);
REQUIRE(r->column == 16);
REQUIRE(r->function == "divide");
}
TEST_CASE("parseAsanReport recovers overflow class from a generic UBSan summary", "[fuzz][ubsan]") {
auto r = parseAsanReport(kRealUbsanOverflow);
REQUIRE(r.has_value());
REQUIRE(r->kind == "signed-integer-overflow");
REQUIRE(r->file == "/tmp/ov.c");
REQUIRE(r->line == 8);
REQUIRE(r->column == 14);
REQUIRE(r->function == "add");
}
TEST_CASE("parseAsanReport returns nullopt on clean output", "[fuzz][asan]") {
REQUIRE_FALSE(parseAsanReport(kCleanRun).has_value());
}
TEST_CASE("parseAsanReport accepts summary lines without a column", "[fuzz][asan]") {
auto r = parseAsanReport(kSummaryWithoutColumn);
REQUIRE(r.has_value());
REQUIRE(r->kind == "heap-use-after-free");
REQUIRE(r->file == "src/foo.c");
REQUIRE(r->line == 42);
REQUIRE(r->column == 0);
REQUIRE(r->function == "worker");
}
TEST_CASE("parseAsanReport returns nullopt on empty input", "[fuzz][asan]") {
REQUIRE_FALSE(parseAsanReport("").has_value());
}