diff --git a/fuzz/src/bin/slicevec.rs b/fuzz/src/bin/slicevec.rs new file mode 100644 index 0000000..628a3b8 --- /dev/null +++ b/fuzz/src/bin/slicevec.rs @@ -0,0 +1,123 @@ +use honggfuzz::fuzz; +use rutenspitz::arbitrary_stateful_operations; +use std::{fmt::Debug, iter::FromIterator, ops::RangeBounds}; + +use tinyvec::SliceVec; +use tinyvec_fuzz::ArbRange; + +arbitrary_stateful_operations! { + model = Vec, + tested = SliceVec<'_, T>, + + type_parameters = < + T: Default + Clone + Debug + Eq + Ord, + R: RangeBounds + Clone + Debug, + >, + + methods { + equal { + fn as_mut_slice(&mut self) -> &mut [T]; + fn as_slice(&self) -> &[T]; + fn clear(&mut self); + //fn dedup(&mut self); + fn extend_from_slice(&mut self, sli: &Box<[T]>); + fn insert(&mut self, index: usize, item: T); + fn is_empty(&self) -> bool; + fn len(&self) -> usize; + fn pop(&mut self) -> Option; + fn push(&mut self, item: T); + fn remove(&mut self, index: usize) -> T; + fn resize(&mut self, new_len: usize, new_val: T); + fn swap_remove(&mut self, index: usize) -> T; + fn truncate(&mut self, new_len: usize); + } + + equal_with(Vec::from_iter) { + //fn split_off(&mut self, at: usize) -> impl IntoIterator; + fn drain(&self, range: R) -> impl Iterator; + fn iter(&self) -> impl Iterator; + fn iter_mut(&self) -> impl Iterator; + } + } + + pre { + match self { + // We are comparing ArrayVec with a limited capacity against + // Vec to which you can push indefinitely. This is a behavior mismatch. + // To compensate we skip adding any elements if it would exceed capacity. + Self::insert { .. } | Self::push { .. } if model.len() == model.capacity() => { + return; + } + Self::resize { new_len, .. } if new_len > model.capacity() => { + return; + } + Self::extend_from_slice { sli } if model.len().saturating_add(sli.len()) > model.capacity() => { + return; + } + _ => {} + } + } +} + +fn xorshift(x: u32) -> u32 { + let x = x ^ (x << 13); + let x = x ^ (x >> 17); + let x = x ^ (x << 5); + return x; +} + +fn seed(data: &[u8]) -> u32 { + let mut rng = [1u8; 4]; + let len = if data.len() > 4 { 4 } else { data.len() }; + rng[..len].copy_from_slice(&data[..len]); + u32::from_ne_bytes(rng) +} + +fn rand_array(mut x: u32) -> [u32; 32] { + let mut tested = [0u32; 32]; + + for item in tested.iter_mut() { + x = xorshift(x); + *item = x; + } + + return tested; +} + + +fn fuzz_cycle(data: &[u8]) -> Result<(), ()> { + use arbitrary::{Arbitrary, Unstructured}; + + let mut ring = Unstructured::new(&data); + + let mut array = rand_array(seed(data)); + let mut model = array.to_vec(); + let mut tested = SliceVec::from(&mut array); + + while let Ok(op) = + > as Arbitrary>::arbitrary(&mut ring) + { + #[cfg(fuzzing_debug)] + eprintln!("{:?}", op); + op.execute_and_compare(&mut model, &mut tested); + } + + Ok(()) +} + +fn main() -> Result<(), ()> { + std::panic::set_hook(Box::new(|panic_info| { + if let Some(outpanic) = + panic_info.payload().downcast_ref::() + { + eprintln!("{}", outpanic.0); + std::process::abort(); + } + })); + + loop { + fuzz!(|data: &[u8]| { + let _ = fuzz_cycle(data); + }); + } +} diff --git a/src/arrayvec.rs b/src/arrayvec.rs index 55db2b0..f517d51 100644 --- a/src/arrayvec.rs +++ b/src/arrayvec.rs @@ -754,11 +754,11 @@ impl ArrayVec { use core::ops::Bound; let start = match range.start_bound() { Bound::Included(x) => *x, - Bound::Excluded(x) => x + 1, + Bound::Excluded(x) => x.saturating_add(1), Bound::Unbounded => 0, }; let end = match range.end_bound() { - Bound::Included(x) => x + 1, + Bound::Included(x) => x.saturating_add(1), Bound::Excluded(x) => *x, Bound::Unbounded => self.len(), }; diff --git a/src/arrayvec_drain.rs b/src/arrayvec_drain.rs index 62efb06..8721b7e 100644 --- a/src/arrayvec_drain.rs +++ b/src/arrayvec_drain.rs @@ -21,11 +21,11 @@ impl<'a, T: 'a + Default> ArrayVecDrain<'a, T> { let start = match range.start_bound() { Bound::Unbounded => 0, Bound::Included(&n) => n, - Bound::Excluded(&n) => n + 1, + Bound::Excluded(&n) => n.saturating_add(1), }; let end = match range.end_bound() { Bound::Unbounded => arr.len(), - Bound::Included(&n) => n + 1, + Bound::Included(&n) => n.saturating_add(1), Bound::Excluded(&n) => n, }; diff --git a/src/slicevec.rs b/src/slicevec.rs index facee32..c54c5cd 100644 --- a/src/slicevec.rs +++ b/src/slicevec.rs @@ -161,11 +161,11 @@ impl<'s, T> SliceVec<'s, T> { use core::ops::Bound; let start = match range.start_bound() { Bound::Included(x) => *x, - Bound::Excluded(x) => x + 1, + Bound::Excluded(x) => x.saturating_add(1), Bound::Unbounded => 0, }; let end = match range.end_bound() { - Bound::Included(x) => x + 1, + Bound::Included(x) => x.saturating_add(1), Bound::Excluded(x) => *x, Bound::Unbounded => self.len, }; diff --git a/src/tinyvec.rs b/src/tinyvec.rs index f57d69d..5de4827 100644 --- a/src/tinyvec.rs +++ b/src/tinyvec.rs @@ -789,11 +789,11 @@ impl TinyVec { use core::ops::Bound; let start = match range.start_bound() { Bound::Included(x) => *x, - Bound::Excluded(x) => x + 1, + Bound::Excluded(x) => x.saturating_add(1), Bound::Unbounded => 0, }; let end = match range.end_bound() { - Bound::Included(x) => x + 1, + Bound::Included(x) => x.saturating_add(1), Bound::Excluded(x) => *x, Bound::Unbounded => self.len(), };