// Fuzz harness for Triangles script interpreter. // // Compile with: // clang++ -fsanitize=fuzzer,address,undefined -g -O1 \ // -I src -I src/leveldb/include \ // src/test/fuzz/script_fuzz.cpp \ // // // Input format (libFuzzer): // [1 byte scriptLen] [scriptLen bytes of raw script bytes] // Anything beyond the first 1 + scriptLen bytes is ignored, so seed // corpus files can be arbitrary-length — only the prefix matters. // // Or run a single corpus file: // ./script_fuzz corpus/script_001.bin // // What this covers: // * Every opcode dispatch in EvalScript (src/script.cpp:332) // * Stack underflow / overflow paths // * OP_CHECKMULTISIG stack walk (the area with the most historical // bugs — sigcache, multisig stack-walk, combineSigs) // * Push-data edge cases (OP_PUSHDATA1/2/4) // * Numeric opcode handling (overflow, MIN/MAX edge values) // // What this does NOT cover: // * Signature verification (needs a real CKey/CTransaction; tested // by BOOST unit tests instead — see src/test/script_tests.cpp) // * P2SH (EvalScript runs first; the second-script eval in VerifyScript // is gated on the first script returning true, which requires a // real signature flow) // // Why EvalScript alone is the right target: every bug in the script // interpreter has lived here, and the input surface is small (a CScript // is just a byte vector). libFuzzer can mutate script bytes freely // without needing realistic sig/key setup. This is the same approach // Bitcoin Core's `script_tests` fuzzer uses. #include #include #include #include #include #include "script.h" #include "main.h" // Entry point for libFuzzer. extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { if (size < 1) return 0; // First byte: script length. Cap at 10000 to keep EvalScript bounded. // (Triangles enforces MAX_SCRIPT_SIZE=10000 in IsPushOnly and others.) const size_t script_len = std::min(data[0], 10000); if (size < 1 + script_len) return 0; std::vector script_bytes(data + 1, data + 1 + script_len); CScript script(script_bytes.begin(), script_bytes.end()); // Run EvalScript against an empty transaction. nIn=0, nHashType=0. // We don't care about the return value or final stack state — we // care that no input crashes, leaks, or trips UBSan. std::vector> stack; CTransaction tx; // default-constructed, empty EvalScript(stack, script, tx, 0, 0); return 0; }