From b2b3d67889a3550f238b1648144c5c25ef352bc0 Mon Sep 17 00:00:00 2001 From: "Andrea Cappa (zi0Black)" <13380579+zi0Black@users.noreply.github.com> Date: Sun, 19 Jan 2025 12:27:00 +0100 Subject: [PATCH] Fuzzing Tooling: cmin and precise stack trace for PartialVMErrors (#15739) * Enhance fuzz.sh script with 'cmin' functionality for corpus minimization. Introduce debug panic mechanism in errors.rs for improved error handling during fuzzing and update README.md for usage instructions. * fmt * rename remove useless collect --- testsuite/fuzzer/README.md | 11 ++++++++++ testsuite/fuzzer/fuzz.sh | 19 ++++++++++++++++- .../move/move-binary-format/src/errors.rs | 21 +++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/testsuite/fuzzer/README.md b/testsuite/fuzzer/README.md index d948c5cc611a0..5bf3425f6eb25 100644 --- a/testsuite/fuzzer/README.md +++ b/testsuite/fuzzer/README.md @@ -25,6 +25,10 @@ The script includes several functions to manage and execute fuzz tests: ```bash ./fuzz.sh build-oss-fuzz ``` +- `cmin`: Distillate corpora + ```bash + ./fuzz.sh cmin [corpora_directory] + ``` - `coverage`: Generates coverage report in HTML format ```bash ./fuzz.sh coverage @@ -124,6 +128,13 @@ The CSV file is structured as follows: - Column 2: Module address - Column 3: Base64-encoded bytecode of the module +## Debug Crashes +Flamegraph and GDB are integrated into fuzz.sh for advanced metrics and debugging. A more rudimentary option is also available: since we have symbolized binaries, we can directly use the stack trace produced by the fuzzer. However, for INVARIANT_VIOLATIONS, the stack trace is incorrect. To obtain the correct stack trace, you can use the following command: +```bash +DEBUG_VM_STATUS= ./fuzz.sh run +``` +This command is selective, so only the specified, comma-separated statuses will trigger the panic in PartialVMError. + ## References - [Rust Fuzz Book](https://rust-fuzz.github.io/book/) - [Google OSS-Fuzz](https://google.github.io/oss-fuzz/) diff --git a/testsuite/fuzzer/fuzz.sh b/testsuite/fuzzer/fuzz.sh index 355d1a548d5df..88c4b8891d00d 100755 --- a/testsuite/fuzzer/fuzz.sh +++ b/testsuite/fuzzer/fuzz.sh @@ -53,6 +53,9 @@ function usage() { "build-oss-fuzz") echo "Usage: $0 build-oss-fuzz " ;; + "cmin") + echo "Usage: $0 cmin [corpus_dir]" + ;; "coverage") echo "Usage: $0 coverage " ;; @@ -80,6 +83,7 @@ function usage() { echo " block-builder runs rust tool to hel build fuzzers" echo " build builds fuzz targets" echo " build-oss-fuzz builds fuzz targets for oss-fuzz" + echo " cmin minimizes a corpus for a target" echo " coverage generates coverage for a fuzz target" echo " clean-coverage clean coverage for a fuzz target" echo " debug debugs a fuzz target with a testcase" @@ -166,6 +170,15 @@ function build-oss-fuzz() { done } +function cmin() { + if [ -z "$1" ]; then + usage cmin + fi + fuzz_target=$1 + corpus_dir=${2:-./fuzz/corpus/$fuzz_target} + cargo_fuzz cmin $fuzz_target $corpus_dir +} + function install-coverage-tools() { cargo +$NIGHTLY_VERSION install cargo-binutils cargo +$NIGHTLY_VERSION install rustfilt @@ -271,7 +284,7 @@ function run() { fi fi info "Running $fuzz_target" - cargo_fuzz run --sanitizer none -O $fuzz_target $testcase -- -fork=10 + cargo_fuzz run --sanitizer address -O $fuzz_target $testcase -- -fork=15 #-ignore_crashes=1 } function test() { @@ -340,6 +353,10 @@ case "$1" in shift build-oss-fuzz "$@" ;; + "cmin") + shift + cmin "$@" + ;; "coverage") shift coverage "$@" diff --git a/third_party/move/move-binary-format/src/errors.rs b/third_party/move/move-binary-format/src/errors.rs index a5f5e447786b1..5fe9af70b5a1d 100644 --- a/third_party/move/move-binary-format/src/errors.rs +++ b/third_party/move/move-binary-format/src/errors.rs @@ -16,6 +16,23 @@ pub type VMResult = ::std::result::Result; pub type BinaryLoaderResult = ::std::result::Result; pub type PartialVMResult = ::std::result::Result; +/// This macro is used to panic while debugging fuzzing crashes obtaining the right stack trace. +/// e.g. DEBUG_VM_STATUS=ABORTED,UNKNOWN_INVARIANT_VIOLATION_ERROR ./fuzz.sh run move_aptosvm_publish_and_run +/// third_party/move/move-core/types/src/vm_status.rs:506 for the list of status codes. +#[cfg(feature = "fuzzing")] +macro_rules! fuzzing_maybe_panic { + ($major_status:expr, $message:expr) => {{ + if let Ok(debug_statuses) = std::env::var("DEBUG_VM_STATUS") { + if debug_statuses + .split(',') + .any(|s| s.trim() == format!("{:?}", $major_status)) + { + panic!("PartialVMError: {:?} {:?}", $major_status, $message); + } + } + }}; +} + #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub enum Location { Undefined, @@ -438,6 +455,10 @@ impl PartialVMError { } else { None }; + + #[cfg(feature = "fuzzing")] + fuzzing_maybe_panic!(major_status, message); + Self(Box::new(PartialVMError_ { major_status, sub_status: None,