117 lines
4.1 KiB
C++
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());
|
|
}
|