aboutsummaryrefslogtreecommitdiff
path: root/src/fam.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/fam.rs')
-rw-r--r--src/fam.rs240
1 files changed, 167 insertions, 73 deletions
diff --git a/src/fam.rs b/src/fam.rs
index 0d62b0f..13ba4c2 100644
--- a/src/fam.rs
+++ b/src/fam.rs
@@ -50,6 +50,8 @@ impl fmt::Display for Error {
/// * the implementer should be a POD
/// * the implementor should contain a flexible array member of elements of type `Entry`
/// * `Entry` should be a POD
+/// * the implementor should ensures that the FAM length as returned by [`FamStruct::len()`]
+/// always describes correctly the length of the flexible array member.
///
/// Violating these may cause problems.
///
@@ -99,7 +101,7 @@ impl fmt::Display for Error {
/// self.len as usize
/// }
///
-/// fn set_len(&mut self, len: usize) {
+/// unsafe fn set_len(&mut self, len: usize) {
/// self.len = len as u32
/// }
///
@@ -135,7 +137,12 @@ pub unsafe trait FamStruct {
///
/// These type of structures contain a member that holds the FAM length.
/// This method will set the value of that member.
- fn set_len(&mut self, len: usize);
+ ///
+ /// # Safety
+ ///
+ /// The caller needs to ensure that `len` here reflects the correct number of entries of the
+ /// flexible array part of the struct.
+ unsafe fn set_len(&mut self, len: usize);
/// Get max allowed FAM length
///
@@ -169,9 +176,14 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
///
/// Get the capacity required by mem_allocator in order to hold
/// the provided number of [`FamStruct::Entry`](trait.FamStruct.html#associatedtype.Entry).
- fn mem_allocator_len(fam_len: usize) -> usize {
- let wrapper_size_in_bytes = size_of::<T>() + fam_len * size_of::<T::Entry>();
- (wrapper_size_in_bytes + size_of::<T>() - 1) / size_of::<T>()
+ /// Returns `None` if the required length would overflow usize.
+ fn mem_allocator_len(fam_len: usize) -> Option<usize> {
+ let wrapper_size_in_bytes =
+ size_of::<T>().checked_add(fam_len.checked_mul(size_of::<T::Entry>())?)?;
+
+ wrapper_size_in_bytes
+ .checked_add(size_of::<T>().checked_sub(1)?)?
+ .checked_div(size_of::<T>())
}
/// Convert `mem_allocator` len to FAM len.
@@ -206,7 +218,8 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
return Err(Error::SizeLimitExceeded);
}
let required_mem_allocator_capacity =
- FamStructWrapper::<T>::mem_allocator_len(num_elements);
+ FamStructWrapper::<T>::mem_allocator_len(num_elements)
+ .ok_or(Error::SizeLimitExceeded)?;
let mut mem_allocator = Vec::with_capacity(required_mem_allocator_capacity);
mem_allocator.push(T::default());
@@ -214,7 +227,11 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
// SAFETY: Safe as long T follows the requirements of being POD.
mem_allocator.push(unsafe { mem::zeroed() })
}
- mem_allocator[0].set_len(num_elements);
+ // SAFETY: The flexible array part of the struct has `num_elements` capacity. We just
+ // initialized this in `mem_allocator`.
+ unsafe {
+ mem_allocator[0].set_len(num_elements);
+ }
Ok(FamStructWrapper { mem_allocator })
}
@@ -234,7 +251,8 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
let mut adapter = FamStructWrapper::<T>::new(entries.len())?;
{
- let wrapper_entries = adapter.as_mut_fam_struct().as_mut_slice();
+ // SAFETY: We are not modifying the length of the FamStruct
+ let wrapper_entries = unsafe { adapter.as_mut_fam_struct().as_mut_slice() };
wrapper_entries.copy_from_slice(entries);
}
@@ -271,7 +289,12 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
}
/// Get a mut reference to the actual [`FamStruct`](trait.FamStruct.html) instance.
- pub fn as_mut_fam_struct(&mut self) -> &mut T {
+ ///
+ /// # Safety
+ ///
+ /// Callers must not use the reference returned to modify the `len` filed of the underlying
+ /// `FamStruct`. See also the top-level documentation of [`FamStruct`].
+ pub unsafe fn as_mut_fam_struct(&mut self) -> &mut T {
&mut self.mem_allocator[0]
}
@@ -294,7 +317,8 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
/// Modifying the container referenced by this pointer may cause its buffer
/// to be reallocated, which would also make any pointers to it invalid.
pub fn as_mut_fam_struct_ptr(&mut self) -> *mut T {
- self.as_mut_fam_struct()
+ // SAFETY: We do not change the length of the underlying FamStruct.
+ unsafe { self.as_mut_fam_struct() }
}
/// Get the elements slice.
@@ -304,7 +328,8 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
/// Get the mutable elements slice.
pub fn as_mut_slice(&mut self) -> &mut [T::Entry] {
- self.as_mut_fam_struct().as_mut_slice()
+ // SAFETY: We do not change the length of the underlying FamStruct.
+ unsafe { self.as_mut_fam_struct() }.as_mut_slice()
}
/// Get the number of elements of type `FamStruct::Entry` currently in the vec.
@@ -326,17 +351,20 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
///
/// If the capacity is already reserved, this method doesn't do anything.
/// If not this will trigger a reallocation of the underlying buffer.
- fn reserve(&mut self, additional: usize) {
+ fn reserve(&mut self, additional: usize) -> Result<(), Error> {
let desired_capacity = self.len() + additional;
if desired_capacity <= self.capacity() {
- return;
+ return Ok(());
}
let current_mem_allocator_len = self.mem_allocator.len();
- let required_mem_allocator_len = FamStructWrapper::<T>::mem_allocator_len(desired_capacity);
+ let required_mem_allocator_len = FamStructWrapper::<T>::mem_allocator_len(desired_capacity)
+ .ok_or(Error::SizeLimitExceeded)?;
let additional_mem_allocator_len = required_mem_allocator_len - current_mem_allocator_len;
self.mem_allocator.reserve(additional_mem_allocator_len);
+
+ Ok(())
}
/// Update the length of the FamStructWrapper.
@@ -352,7 +380,10 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
///
/// When len is greater than the max possible len it returns Error::SizeLimitExceeded.
fn set_len(&mut self, len: usize) -> Result<(), Error> {
- let additional_elements: isize = len as isize - self.len() as isize;
+ let additional_elements = isize::try_from(len)
+ .and_then(|len| isize::try_from(self.len()).map(|self_len| len - self_len))
+ .map_err(|_| Error::SizeLimitExceeded)?;
+
// If len == self.len there's nothing to do.
if additional_elements == 0 {
return Ok(());
@@ -365,11 +396,12 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
return Err(Error::SizeLimitExceeded);
}
// Reserve additional capacity.
- self.reserve(additional_elements as usize);
+ self.reserve(additional_elements as usize)?;
}
let current_mem_allocator_len = self.mem_allocator.len();
- let required_mem_allocator_len = FamStructWrapper::<T>::mem_allocator_len(len);
+ let required_mem_allocator_len =
+ FamStructWrapper::<T>::mem_allocator_len(len).ok_or(Error::SizeLimitExceeded)?;
// Update the len of the `mem_allocator`.
// SAFETY: This is safe since enough capacity has been reserved.
unsafe {
@@ -382,7 +414,11 @@ impl<T: Default + FamStruct> FamStructWrapper<T> {
self.mem_allocator[i] = unsafe { mem::zeroed() }
}
// Update the len of the underlying `FamStruct`.
- self.as_mut_fam_struct().set_len(len);
+ // SAFETY: We just adjusted the memory for the underlying `mem_allocator` to hold `len`
+ // entries.
+ unsafe {
+ self.as_mut_fam_struct().set_len(len);
+ }
// If the len needs to be decreased, deallocate unnecessary memory
if additional_elements < 0 {
@@ -445,9 +481,9 @@ impl<T: Default + FamStruct + PartialEq> PartialEq for FamStructWrapper<T> {
impl<T: Default + FamStruct> Clone for FamStructWrapper<T> {
fn clone(&self) -> Self {
// The number of entries (self.as_slice().len()) can't be > T::max_len() since `self` is a
- // valid `FamStructWrapper`.
+ // valid `FamStructWrapper`. This makes the .unwrap() safe.
let required_mem_allocator_capacity =
- FamStructWrapper::<T>::mem_allocator_len(self.as_slice().len());
+ FamStructWrapper::<T>::mem_allocator_len(self.as_slice().len()).unwrap();
let mut mem_allocator = Vec::with_capacity(required_mem_allocator_capacity);
@@ -469,7 +505,7 @@ impl<T: Default + FamStruct> Clone for FamStructWrapper<T> {
let mut adapter = FamStructWrapper { mem_allocator };
{
- let wrapper_entries = adapter.as_mut_fam_struct().as_mut_slice();
+ let wrapper_entries = adapter.as_mut_slice();
wrapper_entries.copy_from_slice(self.as_slice());
}
adapter
@@ -527,13 +563,23 @@ where
{
use serde::de::Error;
- let header = seq
+ let header: X = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let entries: Vec<X::Entry> = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
+ if header.len() != entries.len() {
+ let msg = format!(
+ "Mismatch between length of FAM specified in FamStruct header ({}) \
+ and actual size of FAM ({})",
+ header.len(),
+ entries.len()
+ );
+ return Err(V::Error::custom(msg));
+ }
+
let mut result: Self::Value = FamStructWrapper::from_entries(entries.as_slice())
.map_err(|e| V::Error::custom(format!("{:?}", e)))?;
result.mem_allocator[0] = header;
@@ -557,7 +603,7 @@ macro_rules! generate_fam_struct_impl {
self.$field_name as usize
}
- fn set_len(&mut self, len: usize) {
+ unsafe fn set_len(&mut self, len: usize) {
self.$field_name = len as $field_type;
}
@@ -581,6 +627,7 @@ macro_rules! generate_fam_struct_impl {
#[cfg(test)]
mod tests {
#![allow(clippy::undocumented_unsafe_blocks)]
+
#[cfg(feature = "with-serde")]
use serde_derive::{Deserialize, Serialize};
@@ -589,7 +636,7 @@ mod tests {
const MAX_LEN: usize = 100;
#[repr(C)]
- #[derive(Default, PartialEq, Eq)]
+ #[derive(Default, Debug, PartialEq, Eq)]
pub struct __IncompleteArrayField<T>(::std::marker::PhantomData<T>, [T; 0]);
impl<T> __IncompleteArrayField<T> {
#[inline]
@@ -678,12 +725,30 @@ mod tests {
let fam_len = pair.0;
let mem_allocator_len = pair.1;
assert_eq!(
- mem_allocator_len,
+ Some(mem_allocator_len),
MockFamStructWrapper::mem_allocator_len(fam_len)
);
}
}
+ #[repr(C)]
+ #[derive(Default, PartialEq)]
+ struct MockFamStructU8 {
+ pub len: u32,
+ pub padding: u32,
+ pub entries: __IncompleteArrayField<u8>,
+ }
+ generate_fam_struct_impl!(MockFamStructU8, u8, entries, u32, len, 100);
+ type MockFamStructWrapperU8 = FamStructWrapper<MockFamStructU8>;
+ #[test]
+ fn test_invalid_type_conversion() {
+ let mut adapter = MockFamStructWrapperU8::new(10).unwrap();
+ assert!(matches!(
+ adapter.set_len(0xffff_ffff_ffff_ff00),
+ Err(Error::SizeLimitExceeded)
+ ));
+ }
+
#[test]
fn test_wrapper_len() {
for pair in MEM_ALLOCATOR_LEN_TO_FAM_LEN {
@@ -785,7 +850,7 @@ mod tests {
let num_elements = pair.0;
let required_mem_allocator_len = pair.1;
- adapter.reserve(num_elements);
+ adapter.reserve(num_elements).unwrap();
assert!(adapter.mem_allocator.capacity() >= required_mem_allocator_len);
assert_eq!(0, adapter.len());
@@ -794,7 +859,7 @@ mod tests {
// test that when the capacity is already reserved, the method doesn't do anything
let current_capacity = adapter.capacity();
- adapter.reserve(current_capacity - 1);
+ adapter.reserve(current_capacity - 1).unwrap();
assert_eq!(current_capacity, adapter.capacity());
}
@@ -831,7 +896,8 @@ mod tests {
assert_eq!(adapter.as_slice()[i], i as u32);
assert_eq!(adapter.len(), i + 1);
assert!(
- adapter.mem_allocator.capacity() >= MockFamStructWrapper::mem_allocator_len(i + 1)
+ adapter.mem_allocator.capacity()
+ >= MockFamStructWrapper::mem_allocator_len(i + 1).unwrap()
);
}
@@ -858,7 +924,7 @@ mod tests {
assert_eq!(adapter.len(), num_retained_entries);
assert!(
adapter.mem_allocator.capacity()
- >= MockFamStructWrapper::mem_allocator_len(num_retained_entries)
+ >= MockFamStructWrapper::mem_allocator_len(num_retained_entries).unwrap()
);
}
@@ -910,7 +976,7 @@ mod tests {
assert_eq!(payload[0], 0xA5);
assert_eq!(payload[1], 0x1e);
}
- assert_eq!(wrapper.as_mut_fam_struct().padding, 5);
+ assert_eq!(unsafe { wrapper.as_mut_fam_struct() }.padding, 5);
let data = wrapper.into_raw();
assert_eq!(data[0].len, 2);
assert_eq!(data[0].padding, 5);
@@ -996,53 +1062,81 @@ mod tests {
type FooFamStructWrapper = FamStructWrapper<Foo>;
let mut wrapper = FooFamStructWrapper::new(0).unwrap();
- wrapper.as_mut_fam_struct().index = 1;
- wrapper.as_mut_fam_struct().flags = 2;
- wrapper.as_mut_fam_struct().length = 3;
- wrapper.push(3).unwrap();
- wrapper.push(14).unwrap();
- assert_eq!(wrapper.as_slice().len(), 3 + 2);
- assert_eq!(wrapper.as_slice()[3], 3);
- assert_eq!(wrapper.as_slice()[3 + 1], 14);
-
- let mut wrapper2 = wrapper.clone();
- assert_eq!(
- wrapper.as_mut_fam_struct().index,
- wrapper2.as_mut_fam_struct().index
- );
- assert_eq!(
- wrapper.as_mut_fam_struct().length,
- wrapper2.as_mut_fam_struct().length
- );
- assert_eq!(
- wrapper.as_mut_fam_struct().flags,
- wrapper2.as_mut_fam_struct().flags
- );
- assert_eq!(wrapper.as_slice(), wrapper2.as_slice());
- assert_eq!(
- wrapper2.as_slice().len(),
- wrapper2.as_mut_fam_struct().length as usize
- );
- assert!(wrapper == wrapper2);
+ // SAFETY: We do play with length here, but that's just for testing purposes :)
+ unsafe {
+ wrapper.as_mut_fam_struct().index = 1;
+ wrapper.as_mut_fam_struct().flags = 2;
+ wrapper.as_mut_fam_struct().length = 3;
+ wrapper.push(3).unwrap();
+ wrapper.push(14).unwrap();
+ assert_eq!(wrapper.as_slice().len(), 3 + 2);
+ assert_eq!(wrapper.as_slice()[3], 3);
+ assert_eq!(wrapper.as_slice()[3 + 1], 14);
+
+ let mut wrapper2 = wrapper.clone();
+ assert_eq!(
+ wrapper.as_mut_fam_struct().index,
+ wrapper2.as_mut_fam_struct().index
+ );
+ assert_eq!(
+ wrapper.as_mut_fam_struct().length,
+ wrapper2.as_mut_fam_struct().length
+ );
+ assert_eq!(
+ wrapper.as_mut_fam_struct().flags,
+ wrapper2.as_mut_fam_struct().flags
+ );
+ assert_eq!(wrapper.as_slice(), wrapper2.as_slice());
+ assert_eq!(
+ wrapper2.as_slice().len(),
+ wrapper2.as_mut_fam_struct().length as usize
+ );
+ assert!(wrapper == wrapper2);
- wrapper.as_mut_fam_struct().index = 3;
- assert!(wrapper != wrapper2);
+ wrapper.as_mut_fam_struct().index = 3;
+ assert!(wrapper != wrapper2);
- wrapper.as_mut_fam_struct().length = 7;
- assert!(wrapper != wrapper2);
+ wrapper.as_mut_fam_struct().length = 7;
+ assert!(wrapper != wrapper2);
- wrapper.push(1).unwrap();
- assert_eq!(wrapper.as_mut_fam_struct().length, 8);
- assert!(wrapper != wrapper2);
+ wrapper.push(1).unwrap();
+ assert_eq!(wrapper.as_mut_fam_struct().length, 8);
+ assert!(wrapper != wrapper2);
- let mut wrapper2 = wrapper.clone();
- assert!(wrapper == wrapper2);
+ let mut wrapper2 = wrapper.clone();
+ assert!(wrapper == wrapper2);
- // Dropping the original variable should not affect its clone.
- drop(wrapper);
- assert_eq!(wrapper2.as_mut_fam_struct().index, 3);
- assert_eq!(wrapper2.as_mut_fam_struct().length, 8);
- assert_eq!(wrapper2.as_mut_fam_struct().flags, 2);
- assert_eq!(wrapper2.as_slice(), [0, 0, 0, 3, 14, 0, 0, 1]);
+ // Dropping the original variable should not affect its clone.
+ drop(wrapper);
+ assert_eq!(wrapper2.as_mut_fam_struct().index, 3);
+ assert_eq!(wrapper2.as_mut_fam_struct().length, 8);
+ assert_eq!(wrapper2.as_mut_fam_struct().flags, 2);
+ assert_eq!(wrapper2.as_slice(), [0, 0, 0, 3, 14, 0, 0, 1]);
+ }
+ }
+
+ #[cfg(feature = "with-serde")]
+ #[test]
+ fn test_bad_deserialize() {
+ #[repr(C)]
+ #[derive(Default, Debug, PartialEq, Serialize, Deserialize)]
+ struct Foo {
+ pub len: u32,
+ pub padding: u32,
+ pub entries: __IncompleteArrayField<u32>,
+ }
+
+ generate_fam_struct_impl!(Foo, u32, entries, u32, len, 100);
+
+ let state = FamStructWrapper::<Foo>::new(0).unwrap();
+ let mut bytes = bincode::serialize(&state).unwrap();
+
+ // The `len` field of the header is the first to be serialized.
+ // Writing at position 0 of the serialized data should change its value.
+ bytes[0] = 255;
+
+ assert!(
+ matches!(bincode::deserialize::<FamStructWrapper<Foo>>(&bytes).map_err(|boxed| *boxed), Err(bincode::ErrorKind::Custom(s)) if s == *"Mismatch between length of FAM specified in FamStruct header (255) and actual size of FAM (0)")
+ );
}
}