diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index a7234e1..df9b89e 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -9,6 +9,10 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" +arbitrary = { version = "1.0", features = [ + "derive", +] } +rand = "0.9.1" [dependencies.midi2] path = "../midi2" @@ -32,3 +36,10 @@ path = "./fuzz_targets/sysex7_payload_roundtrip.rs" test = false doc = false bench = false + +[[bin]] +name = "generic_sysex_inserting_payloads" +path = "./fuzz_targets/generic_sysex_inserting_payloads.rs" +test = false +doc = false +bench = false diff --git a/fuzz/fuzz_targets/generic_sysex_inserting_payloads.rs b/fuzz/fuzz_targets/generic_sysex_inserting_payloads.rs new file mode 100644 index 0000000..fa91db9 --- /dev/null +++ b/fuzz/fuzz_targets/generic_sysex_inserting_payloads.rs @@ -0,0 +1,126 @@ +#![no_main] + +use libfuzzer_sys::fuzz_target; +use midi2::Sysex; +use rand::{Rng, SeedableRng}; + +struct FixedSizeBuffer(Vec); + +impl midi2::buffer::Buffer for FixedSizeBuffer { + type Unit = U; + fn buffer(&self) -> &[Self::Unit] { + &self.0 + } +} + +impl midi2::buffer::BufferMut for FixedSizeBuffer { + fn buffer_mut(&mut self) -> &mut [Self::Unit] { + &mut self.0 + } +} + +impl midi2::buffer::BufferTryResize for FixedSizeBuffer { + fn try_resize(&mut self, new_size: usize) -> Result<(), midi2::error::BufferOverflow> { + if new_size > self.0.len() { + return Err(midi2::error::BufferOverflow); + } + Ok(()) + } +} + +impl FixedSizeBuffer { + fn new(size: usize) -> Self { + Self(std::iter::repeat_n(U::zero(), size).collect()) + } +} + +#[derive(arbitrary::Arbitrary, Debug)] +struct InputData { + seed: u64, + initial_data: Vec, + data_to_insert: Vec, +} + +const MAX_BUFFER_SIZE: usize = 1024; + +trait IntoByte { + fn byte(&self) -> B; +} + +impl IntoByte for u8 { + fn byte(&self) -> midi2::ux::u7 { + midi2::num::u7::new(self & 0x7F) + } +} + +impl IntoByte for u8 { + fn byte(&self) -> u8 { + *self + } +} + +fn test_case(data: &InputData, mut message: M, index: usize) +where + B: midi2::buffer::Buffer + midi2::buffer::BufferTryResize + midi2::buffer::BufferMut, + M: midi2::Sysex, + >::Byte: Eq + core::fmt::Debug, + u8: IntoByte<>::Byte>, +{ + let Ok(()) = message.try_set_payload(data.initial_data.iter().map(u8::byte)) else { + return; + }; + + { + let initial = message.payload().collect::>(); + assert_eq!( + initial, + data.initial_data.iter().map(u8::byte).collect::>() + ); + } + + let Ok(()) = message.try_insert_payload(data.data_to_insert.iter().map(u8::byte), index) else { + return; + }; + + let actual = message.payload().collect::>(); + let expected = { + let mut ret = data.initial_data.clone(); + ret.splice(index..index, data.data_to_insert.clone()); + ret.iter().map(u8::byte).collect::>() + }; + assert_eq!(actual, expected); +} + +fuzz_target!(|data: InputData| { + let mut rng = rand::rngs::StdRng::seed_from_u64(data.seed); + let fized_size_buffer_size = rng.random_range(4..MAX_BUFFER_SIZE); + let index = if data.initial_data.is_empty() { + 0 + } else { + rng.random_range(0..data.initial_data.len()) + }; + test_case( + &data, + midi2::sysex8::Sysex8::>::try_new_with_buffer( + FixedSizeBuffer::::new(fized_size_buffer_size), + ) + .unwrap(), + index, + ); + test_case( + &data, + midi2::sysex7::Sysex7::>::try_new_with_buffer( + FixedSizeBuffer::::new(fized_size_buffer_size), + ) + .unwrap(), + index, + ); + test_case( + &data, + midi2::sysex7::Sysex7::>::try_new_with_buffer( + FixedSizeBuffer::::new(fized_size_buffer_size), + ) + .unwrap(), + index, + ); +}); diff --git a/midi2/src/traits.rs b/midi2/src/traits.rs index b049cda..fae4d8a 100644 --- a/midi2/src/traits.rs +++ b/midi2/src/traits.rs @@ -613,7 +613,7 @@ pub trait Sysex { where B: crate::buffer::BufferMut + crate::buffer::BufferResize, { - self.insert_payload(std::iter::once(byte), self.payload_size()); + self.insert_payload(core::iter::once(byte), self.payload_size()); } /// Pushes the provided byte into the back of the @@ -629,7 +629,7 @@ pub trait Sysex { where B: crate::buffer::BufferMut + crate::buffer::BufferTryResize, { - self.try_insert_payload(std::iter::once(byte), self.payload_size()) + self.try_insert_payload(core::iter::once(byte), self.payload_size()) } }