// Unit tests for the constant-zero hazard state machine // (scanInstructionsForConstHazards), exercised with synthetic ARM64 sequences // — no radare2. The pass proves division-by-zero (divisor is a constant 0) and // null-dereference (deref through a constant-0 register) without path modeling, // and resets tracking at every branch so it never reasons across a path. #include #include #include #include #include #include "fold/binary/r2_backend.hpp" #include "fold/report/finding.hpp" namespace { using Op = std::pair; std::vector seq(std::initializer_list lines) { std::vector ops; std::uint64_t addr = 0x1000; for (const auto& l : lines) ops.emplace_back(addr += 4, l); return ops; } } // namespace TEST_CASE("const-hazard: sdiv by a literal-zero register is division by zero", "[binary][divz]") { auto h = fold::binary::scanInstructionsForConstHazards(seq({ "ldr w8, [var_ch]", "mov w9, 0", "sdiv w0, w8, w9", })); REQUIRE(h.size() == 1); REQUIRE(h[0].kind == fold::FindingKind::DivByZero); REQUIRE(h[0].reg == "w9"); } TEST_CASE("const-hazard: deref of a zero pointer loaded from a zeroed slot is null-deref", "[binary][nullderef]") { auto h = fold::binary::scanInstructionsForConstHazards(seq({ "str xzr, [var_8h]", "ldr x8, [var_8h]", "ldr w0, [x8]", })); REQUIRE(h.size() == 1); REQUIRE(h[0].kind == fold::FindingKind::NullDeref); REQUIRE(h[0].reg == "x8"); } TEST_CASE("const-hazard: a non-zero divisor is not flagged", "[binary][divz]") { auto h = fold::binary::scanInstructionsForConstHazards(seq({ "mov w9, 5", "sdiv w0, w8, w9", })); REQUIRE(h.empty()); } TEST_CASE("const-hazard: an unknown (loaded) divisor is not flagged", "[binary][divz]") { auto h = fold::binary::scanInstructionsForConstHazards(seq({ "ldr w9, [var_4h]", "sdiv w0, w8, w9", })); REQUIRE(h.empty()); } TEST_CASE("const-hazard: tracking is reset across a branch (no cross-path zero)", "[binary][divz]") { // The zero in w9 must not carry past the branch to the sdiv: that would be // an unmodeled-path inference and a potential false positive. auto h = fold::binary::scanInstructionsForConstHazards(seq({ "mov w9, 0", "cbnz w8, 0x2000", "sdiv w0, w8, w9", })); REQUIRE(h.empty()); } TEST_CASE("const-hazard: deref through a non-zero pointer is not flagged", "[binary][nullderef]") { auto h = fold::binary::scanInstructionsForConstHazards(seq({ "ldr x8, [var_8h]", // var_8h not known-zero "ldr w0, [x8]", })); REQUIRE(h.empty()); }