#include #include #include #include #include "fold/fuzz/corpus_writer.hpp" #include "fold/fuzz/harness_emitter.hpp" #include "fold/fuzz/witness_recorder.hpp" using fold::fuzz::canonicalSeeds; using fold::fuzz::emitHarness; using fold::fuzz::packSeed; using fold::fuzz::ParamWitness; using fold::fuzz::PathWitness; using fold::fuzz::PointerShape; using fold::fuzz::seedHashHex; using fold::fuzz::WitnessKind; namespace { std::vector bytesOf(std::initializer_list bs) { std::vector out; out.reserve(bs.size()); for (auto b : bs) out.push_back(static_cast(b)); return out; } PathWitness twoParamWitness(bool with_bytes) { PathWitness w; w.function = "ns::target"; w.cpp_return_type = "void"; w.finding_id = "id:1"; ParamWitness x; x.cpp_type = "int"; x.kind = WitnessKind::Integer; x.bit_width = 32; x.is_signed = true; if (with_bytes) x.bytes = bytesOf({0xEF, 0xBE, 0xAD, 0xDE}); ParamWitness y; y.cpp_type = "unsigned char"; y.kind = WitnessKind::Integer; y.bit_width = 8; if (with_bytes) y.bytes = bytesOf({0xAB}); w.params = {x, y}; return w; } } // namespace TEST_CASE("packSeed produces buffer matching harness total_input_bytes", "[fuzz][corpus]") { PathWitness w = twoParamWitness(true); auto art = emitHarness({w}); auto seed = packSeed(w); REQUIRE(seed.size() == art.total_input_bytes); REQUIRE(seed.size() == 5); // First parameter bytes at offset 0 REQUIRE(static_cast(seed[0]) == 0xEF); REQUIRE(static_cast(seed[3]) == 0xDE); REQUIRE(static_cast(seed[4]) == 0xAB); } TEST_CASE("packSeed zero-pads when a param's bytes are shorter than its slot", "[fuzz][corpus]") { PathWitness w = twoParamWitness(false); // Leave x.bytes empty; packSeed should zero-fill to slot. auto seed = packSeed(w); REQUIRE(seed.size() == 5); for (auto b : seed) REQUIRE(static_cast(b) == 0); } TEST_CASE("packSeed truncates when a param's bytes exceed its slot", "[fuzz][corpus]") { PathWitness w = twoParamWitness(true); // Over-fill y to 4 bytes; slot is 1 — only the first byte should // make it into the seed. w.params[1].bytes = bytesOf({0x11, 0x22, 0x33, 0x44}); auto seed = packSeed(w); REQUIRE(seed.size() == 5); REQUIRE(static_cast(seed[4]) == 0x11); } TEST_CASE("packSeed packs pointer buffer_contents, zero-fills the rest", "[fuzz][corpus][pointer]") { PathWitness w; w.function = "f"; ParamWitness p; p.cpp_type = "char*"; p.kind = WitnessKind::Pointer; p.pointer.shape = PointerShape::NullableBuffer; p.pointer.buffer_bytes = 8; p.pointer.buffer_contents = bytesOf({0xA, 0xB, 0xC}); w.params = {p}; auto seed = packSeed(w); REQUIRE(seed.size() == 8); REQUIRE(static_cast(seed[0]) == 0x0A); REQUIRE(static_cast(seed[1]) == 0x0B); REQUIRE(static_cast(seed[2]) == 0x0C); for (std::size_t i = 3; i < seed.size(); ++i) REQUIRE(static_cast(seed[i]) == 0); } TEST_CASE("packSeed skips Unsupported and ConcreteNull pointer slots", "[fuzz][corpus]") { PathWitness w; w.function = "f"; ParamWitness u; u.kind = WitnessKind::Unsupported; ParamWitness np; np.kind = WitnessKind::Pointer; np.pointer.shape = PointerShape::ConcreteNull; np.pointer.buffer_bytes = 0; w.params = {u, np}; auto seed = packSeed(w); REQUIRE(seed.empty()); } TEST_CASE("seedHashHex is deterministic and 16 lowercase hex chars", "[fuzz][corpus][hash]") { auto seed = packSeed(twoParamWitness(true)); auto h1 = seedHashHex(seed); auto h2 = seedHashHex(seed); REQUIRE(h1 == h2); REQUIRE(h1.size() == 16); for (char c : h1) REQUIRE(((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))); } TEST_CASE("seedHashHex distinguishes different-length all-zero buffers", "[fuzz][corpus][hash]") { std::vector a(4, std::byte{0}); std::vector b(8, std::byte{0}); REQUIRE(seedHashHex(a) != seedHashHex(b)); } TEST_CASE("canonicalSeeds returns three distinct seeds at the requested size", "[fuzz][corpus][canonical]") { auto seeds = canonicalSeeds(4); REQUIRE(seeds.size() == 3); REQUIRE(seeds[0].size() == 4); REQUIRE(seeds[1].size() == 4); REQUIRE(seeds[2].size() == 4); // All zeros, all ones, then 01 00 00 00 REQUIRE(static_cast(seeds[0][0]) == 0x00); REQUIRE(static_cast(seeds[1][0]) == 0xFF); REQUIRE(static_cast(seeds[2][0]) == 0x01); REQUIRE(static_cast(seeds[2][1]) == 0x00); } TEST_CASE("canonicalSeeds returns empty when total_bytes is zero", "[fuzz][corpus][canonical]") { REQUIRE(canonicalSeeds(0).empty()); }