Move existing BOLT11 fuzz test to the `fuzz` crate
authorMatt Corallo <git@bluematt.me>
Tue, 30 Apr 2024 13:58:21 +0000 (13:58 +0000)
committerMatt Corallo <git@bluematt.me>
Wed, 8 May 2024 19:36:15 +0000 (19:36 +0000)
12 files changed:
.github/workflows/build.yml
fuzz/Cargo.toml
fuzz/src/bin/bolt11_deser_target.rs [new file with mode: 0644]
fuzz/src/bin/gen_target.sh
fuzz/src/bolt11_deser.rs [new file with mode: 0644]
fuzz/src/lib.rs
fuzz/targets.h
lightning-invoice/fuzz/.gitignore [deleted file]
lightning-invoice/fuzz/Cargo.toml [deleted file]
lightning-invoice/fuzz/ci-fuzz.sh [deleted file]
lightning-invoice/fuzz/fuzz_targets/serde_data_part.rs [deleted file]
rustfmt_excluded_files

index 951691e96bb892cdb0fca2968314f9fee4e216be..eb930f6fcfface6e777eea82b1efcca3dad8edc9 100644 (file)
@@ -191,13 +191,10 @@ jobs:
       - name: Pin the regex dependency
         run: |
           cd fuzz && cargo update -p regex --precise "1.9.6" --verbose && cd ..
-          cd lightning-invoice/fuzz && cargo update -p regex --precise "1.9.6" --verbose
       - name: Sanity check fuzz targets on Rust ${{ env.TOOLCHAIN }}
         run: cd fuzz && RUSTFLAGS="--cfg=fuzzing" cargo test --verbose --color always
       - name: Run fuzzers
         run: cd fuzz && ./ci-fuzz.sh && cd ..
-      - name: Run lightning-invoice fuzzers
-        run: cd lightning-invoice/fuzz && RUSTFLAGS="--cfg=fuzzing" cargo test --verbose && ./ci-fuzz.sh
 
   linting:
     runs-on: ubuntu-latest
index d87be2ef6a4e918ba41bb44dfc4d4f921ccf1094..c14fce790ff53c4c8245460fc61f314a211bd3d8 100644 (file)
@@ -19,6 +19,7 @@ stdin_fuzz = []
 
 [dependencies]
 lightning = { path = "../lightning", features = ["regex", "hashbrown", "_test_utils"] }
+lightning-invoice = { path = "../lightning-invoice" }
 lightning-rapid-gossip-sync = { path = "../lightning-rapid-gossip-sync" }
 bitcoin = { version = "0.30.2", features = ["secp-lowmemory"] }
 hex = { package = "hex-conservative", version = "0.1.1", default-features = false }
diff --git a/fuzz/src/bin/bolt11_deser_target.rs b/fuzz/src/bin/bolt11_deser_target.rs
new file mode 100644 (file)
index 0000000..626a465
--- /dev/null
@@ -0,0 +1,113 @@
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+// This file is auto-generated by gen_target.sh based on target_template.txt
+// To modify it, modify target_template.txt and run gen_target.sh instead.
+
+#![cfg_attr(feature = "libfuzzer_fuzz", no_main)]
+
+#[cfg(not(fuzzing))]
+compile_error!("Fuzz targets need cfg=fuzzing");
+
+extern crate lightning_fuzz;
+use lightning_fuzz::bolt11_deser::*;
+
+#[cfg(feature = "afl")]
+#[macro_use] extern crate afl;
+#[cfg(feature = "afl")]
+fn main() {
+       fuzz!(|data| {
+               bolt11_deser_run(data.as_ptr(), data.len());
+       });
+}
+
+#[cfg(feature = "honggfuzz")]
+#[macro_use] extern crate honggfuzz;
+#[cfg(feature = "honggfuzz")]
+fn main() {
+       loop {
+               fuzz!(|data| {
+                       bolt11_deser_run(data.as_ptr(), data.len());
+               });
+       }
+}
+
+#[cfg(feature = "libfuzzer_fuzz")]
+#[macro_use] extern crate libfuzzer_sys;
+#[cfg(feature = "libfuzzer_fuzz")]
+fuzz_target!(|data: &[u8]| {
+       bolt11_deser_run(data.as_ptr(), data.len());
+});
+
+#[cfg(feature = "stdin_fuzz")]
+fn main() {
+       use std::io::Read;
+
+       let mut data = Vec::with_capacity(8192);
+       std::io::stdin().read_to_end(&mut data).unwrap();
+       bolt11_deser_run(data.as_ptr(), data.len());
+}
+
+#[test]
+fn run_test_cases() {
+       use std::fs;
+       use std::io::Read;
+       use lightning_fuzz::utils::test_logger::StringBuffer;
+
+       use std::sync::{atomic, Arc};
+       {
+               let data: Vec<u8> = vec![0];
+               bolt11_deser_run(data.as_ptr(), data.len());
+       }
+       let mut threads = Vec::new();
+       let threads_running = Arc::new(atomic::AtomicUsize::new(0));
+       if let Ok(tests) = fs::read_dir("test_cases/bolt11_deser") {
+               for test in tests {
+                       let mut data: Vec<u8> = Vec::new();
+                       let path = test.unwrap().path();
+                       fs::File::open(&path).unwrap().read_to_end(&mut data).unwrap();
+                       threads_running.fetch_add(1, atomic::Ordering::AcqRel);
+
+                       let thread_count_ref = Arc::clone(&threads_running);
+                       let main_thread_ref = std::thread::current();
+                       threads.push((path.file_name().unwrap().to_str().unwrap().to_string(),
+                               std::thread::spawn(move || {
+                                       let string_logger = StringBuffer::new();
+
+                                       let panic_logger = string_logger.clone();
+                                       let res = if ::std::panic::catch_unwind(move || {
+                                               bolt11_deser_test(&data, panic_logger);
+                                       }).is_err() {
+                                               Some(string_logger.into_string())
+                                       } else { None };
+                                       thread_count_ref.fetch_sub(1, atomic::Ordering::AcqRel);
+                                       main_thread_ref.unpark();
+                                       res
+                               })
+                       ));
+                       while threads_running.load(atomic::Ordering::Acquire) > 32 {
+                               std::thread::park();
+                       }
+               }
+       }
+       let mut failed_outputs = Vec::new();
+       for (test, thread) in threads.drain(..) {
+               if let Some(output) = thread.join().unwrap() {
+                       println!("\nOutput of {}:\n{}\n", test, output);
+                       failed_outputs.push(test);
+               }
+       }
+       if !failed_outputs.is_empty() {
+               println!("Test cases which failed: ");
+               for case in failed_outputs {
+                       println!("{}", case);
+               }
+               panic!();
+       }
+}
index 62381622f6b58bfddfd7da27b11e7aee10206a1e..f124c1e0ef03bd710417cbebf0b770f81df8094e 100755 (executable)
@@ -13,6 +13,7 @@ GEN_TEST full_stack
 GEN_TEST invoice_deser
 GEN_TEST invoice_request_deser
 GEN_TEST offer_deser
+GEN_TEST bolt11_deser
 GEN_TEST onion_message
 GEN_TEST peer_crypt
 GEN_TEST process_network_graph
diff --git a/fuzz/src/bolt11_deser.rs b/fuzz/src/bolt11_deser.rs
new file mode 100644 (file)
index 0000000..105235a
--- /dev/null
@@ -0,0 +1,39 @@
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+use bitcoin::bech32::{u5, FromBase32, ToBase32};
+use crate::utils::test_logger;
+use lightning_invoice::RawDataPart;
+
+#[inline]
+pub fn do_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
+       let bech32 = data.iter().map(|x| u5::try_from_u8(x % 32).unwrap()).collect::<Vec<_>>();
+       let invoice = match RawDataPart::from_base32(&bech32) {
+               Ok(invoice) => invoice,
+               Err(_) => return,
+       };
+
+       // Our encoding is not worse than the input
+       assert!(invoice.to_base32().len() <= bech32.len());
+
+       // Our serialization is loss-less
+       assert_eq!(
+               RawDataPart::from_base32(&invoice.to_base32()).expect("faild parsing out own encoding"),
+               invoice
+       );
+}
+
+pub fn bolt11_deser_test<Out: test_logger::Output>(data: &[u8], out: Out) {
+       do_test(data, out);
+}
+
+#[no_mangle]
+pub extern "C" fn bolt11_deser_run(data: *const u8, datalen: usize) {
+       do_test(unsafe { std::slice::from_raw_parts(data, datalen) }, test_logger::DevNull {});
+}
index 5b5cd69cf9681ac2d1fa5c8b75a52eebbd0447a5..6efdd94dfcb433650c5480b92e4ceabb81909f4b 100644 (file)
@@ -22,6 +22,7 @@ pub mod indexedmap;
 pub mod invoice_deser;
 pub mod invoice_request_deser;
 pub mod offer_deser;
+pub mod bolt11_deser;
 pub mod onion_message;
 pub mod peer_crypt;
 pub mod process_network_graph;
index 841ed55cea337ba114b01f0d7258facb67d3dcba..d8b755c1a7e5a3a7fa9cdb5732b852fd3452404c 100644 (file)
@@ -6,6 +6,7 @@ void full_stack_run(const unsigned char* data, size_t data_len);
 void invoice_deser_run(const unsigned char* data, size_t data_len);
 void invoice_request_deser_run(const unsigned char* data, size_t data_len);
 void offer_deser_run(const unsigned char* data, size_t data_len);
+void bolt11_deser_run(const unsigned char* data, size_t data_len);
 void onion_message_run(const unsigned char* data, size_t data_len);
 void peer_crypt_run(const unsigned char* data, size_t data_len);
 void process_network_graph_run(const unsigned char* data, size_t data_len);
diff --git a/lightning-invoice/fuzz/.gitignore b/lightning-invoice/fuzz/.gitignore
deleted file mode 100644 (file)
index 38a9008..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-target
-hfuzz_*
diff --git a/lightning-invoice/fuzz/Cargo.toml b/lightning-invoice/fuzz/Cargo.toml
deleted file mode 100644 (file)
index 746fe63..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-[package]
-name = "lightning-invoice-fuzz"
-version = "0.0.1"
-authors = ["Automatically generated"]
-publish = false
-edition = "2021"
-
-[package.metadata]
-cargo-fuzz = true
-
-[features]
-afl_fuzz = ["afl"]
-honggfuzz_fuzz = ["honggfuzz"]
-
-[dependencies]
-honggfuzz = { version = "0.5", optional = true, default-features = false }
-afl = { version = "0.4", optional = true }
-lightning-invoice = { path = ".." }
-lightning = { path = "../../lightning", features = ["regex"] }
-bech32 = "0.9.0"
-
-# Prevent this from interfering with workspaces
-[workspace]
-members = ["."]
-
-[[bin]]
-name = "serde_data_part"
-path = "fuzz_targets/serde_data_part.rs"
diff --git a/lightning-invoice/fuzz/ci-fuzz.sh b/lightning-invoice/fuzz/ci-fuzz.sh
deleted file mode 100755 (executable)
index db1b9eb..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-set -e
-cargo install --force honggfuzz --no-default-features
-for TARGET in fuzz_targets/*; do
-    FILENAME=$(basename $TARGET)
-       FILE="${FILENAME%.*}"
-       if [ -d hfuzz_input/$FILE ]; then
-           HFUZZ_INPUT_ARGS="-f hfuzz_input/$FILE/input"
-       fi
-       HFUZZ_BUILD_ARGS="--features honggfuzz_fuzz" HFUZZ_RUN_ARGS="-N1000000 --exit_upon_crash -v $HFUZZ_INPUT_ARGS" cargo hfuzz run $FILE
-
-       if [ -f hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT ]; then
-               cat hfuzz_workspace/$FILE/HONGGFUZZ.REPORT.TXT
-               for CASE in hfuzz_workspace/$FILE/SIG*; do
-                       cat $CASE | xxd -p
-               done
-               exit 1
-       fi
-done
diff --git a/lightning-invoice/fuzz/fuzz_targets/serde_data_part.rs b/lightning-invoice/fuzz/fuzz_targets/serde_data_part.rs
deleted file mode 100644 (file)
index 871c6c7..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-extern crate lightning_invoice;
-extern crate bech32;
-
-use lightning_invoice::RawDataPart;
-use bech32::{FromBase32, ToBase32, u5};
-
-fn do_test(data: &[u8]) {
-    let bech32 = data.iter().map(|x| u5::try_from_u8(x % 32).unwrap()).collect::<Vec<_>>();
-    let invoice = match RawDataPart::from_base32(&bech32) {
-        Ok(invoice) => invoice,
-        Err(_) => return,
-    };
-
-    // Our encoding is not worse than the input
-    assert!(invoice.to_base32().len() <= bech32.len());
-
-    // Our serialization is loss-less
-    assert_eq!(
-        RawDataPart::from_base32(&invoice.to_base32()).expect("faild parsing out own encoding"),
-        invoice
-    );
-}
-
-#[cfg(feature = "afl")]
-#[macro_use] extern crate afl;
-#[cfg(feature = "afl")]
-fn main() {
-    fuzz!(|data| {
-        do_test(&data);
-    });
-}
-
-#[cfg(feature = "honggfuzz")]
-#[macro_use] extern crate honggfuzz;
-#[cfg(feature = "honggfuzz")]
-fn main() {
-    loop {
-        fuzz!(|data| {
-            do_test(data);
-        });
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
-        let mut b = 0;
-        for (idx, c) in hex.as_bytes().iter().filter(|&&c| c != b'\n').enumerate() {
-            b <<= 4;
-            match *c {
-                b'A'..=b'F' => b |= c - b'A' + 10,
-                b'a'..=b'f' => b |= c - b'a' + 10,
-                b'0'..=b'9' => b |= c - b'0',
-                _ => panic!("Bad hex"),
-            }
-            if (idx & 1) == 1 {
-                out.push(b);
-                b = 0;
-            }
-        }
-    }
-
-    #[test]
-    fn duplicate_crash() {
-        let mut a = Vec::new();
-        extend_vec_from_hex("000000", &mut a);
-        super::do_test(&a);
-    }
-}
index 51731c40d092dc3e706354c79e01568624569348..4069cbe793b35ac9b2843fae3a8e0e2a3606bcec 100644 (file)
@@ -3,6 +3,7 @@
 ./fuzz/src/bech32_parse.rs
 ./fuzz/src/bin/base32_target.rs
 ./fuzz/src/bin/bech32_parse_target.rs
+./fuzz/src/bin/bolt11_deser_target.rs
 ./fuzz/src/bin/chanmon_consistency_target.rs
 ./fuzz/src/bin/chanmon_deser_target.rs
 ./fuzz/src/bin/fromstr_to_netaddress_target.rs