diff --git a/src/aligned_memory.rs b/src/aligned_memory.rs index bab8ef92..29413802 100644 --- a/src/aligned_memory.rs +++ b/src/aligned_memory.rs @@ -1,8 +1,20 @@ -#![allow(clippy::integer_arithmetic)] //! Aligned memory use std::{mem, ptr}; +/// Scalar types, aka "plain old data" +pub trait Pod {} + +impl Pod for bool {} +impl Pod for u8 {} +impl Pod for u16 {} +impl Pod for u32 {} +impl Pod for u64 {} +impl Pod for i8 {} +impl Pod for i16 {} +impl Pod for i32 {} +impl Pod for i64 {} + /// Provides u8 slices at a specified alignment #[derive(Debug, PartialEq, Eq)] pub struct AlignedMemory<const ALIGN: usize> { @@ -14,7 +26,7 @@ pub struct AlignedMemory<const ALIGN: usize> { impl<const ALIGN: usize> AlignedMemory<ALIGN> { fn get_mem(max_len: usize) -> (Vec<u8>, usize) { - let mut mem: Vec<u8> = Vec::with_capacity(max_len + ALIGN); + let mut mem: Vec<u8> = Vec::with_capacity(max_len.saturating_add(ALIGN)); mem.push(0); let align_offset = mem.as_ptr().align_offset(ALIGN); mem.resize(align_offset, 0); @@ -26,7 +38,7 @@ impl<const ALIGN: usize> AlignedMemory<ALIGN> { // https://github.com/rust-lang/rust/issues/54628 let mut mem = vec![0; max_len]; let align_offset = mem.as_ptr().align_offset(ALIGN); - mem.resize(align_offset + max_len, 0); + mem.resize(max_len.saturating_add(align_offset), 0); (mem, align_offset) } /// Returns a filled AlignedMemory by copying the given slice @@ -74,15 +86,15 @@ impl<const ALIGN: usize> AlignedMemory<ALIGN> { } /// Calculate memory size pub fn mem_size(&self) -> usize { - mem::size_of::<Self>() + self.mem.capacity() + self.mem.capacity().saturating_add(mem::size_of::<Self>()) } /// Get the length of the data pub fn len(&self) -> usize { - self.mem.len() - self.align_offset + self.mem.len().saturating_sub(self.align_offset) } /// Is the memory empty pub fn is_empty(&self) -> bool { - self.mem.len() - self.align_offset == 0 + self.mem.len() == self.align_offset } /// Get the current write index pub fn write_index(&self) -> usize { @@ -102,44 +114,38 @@ impl<const ALIGN: usize> AlignedMemory<ALIGN> { } /// Grows memory with `value` repeated `num` times starting at the `write_index` pub fn fill_write(&mut self, num: usize, value: u8) -> std::io::Result<()> { - if self.mem.len() + num > self.align_offset + self.max_len { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "aligned memory resize failed", - )); - } + let new_len = match ( + self.mem.len().checked_add(num), + self.align_offset.checked_add(self.max_len), + ) { + (Some(new_len), Some(allocation_end)) if new_len <= allocation_end => new_len, + _ => { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "aligned memory resize failed", + )) + } + }; if self.zero_up_to_max_len && value == 0 { // Safe because everything up to `max_len` is zeroed and no shrinking is allowed unsafe { - self.mem.set_len(self.mem.len() + num); + self.mem.set_len(new_len); } } else { - self.mem.resize(self.mem.len() + num, value); + self.mem.resize(new_len, value); } Ok(()) } - /// Write one byte into the memory. + /// Write a generic type T into the memory. /// /// # Safety /// /// Unsafe since it assumes that there is enough capacity. - pub unsafe fn write_u8_unchecked(&mut self, value: u8) { + pub unsafe fn write_unchecked<T: Pod>(&mut self, value: T) { let pos = self.mem.len(); - debug_assert!(pos < self.align_offset + self.max_len); - self.mem.set_len(pos + 1); - *self.mem.get_unchecked_mut(pos) = value; - } - - /// Write one u64 into the memory. - /// - /// # Safety - /// - /// Unsafe since it assumes that there is enough capacity. - pub unsafe fn write_u64_unchecked(&mut self, value: u64) { - let pos = self.mem.len(); - let new_len = pos + mem::size_of::<u64>(); - debug_assert!(new_len <= self.align_offset + self.max_len); + let new_len = pos.saturating_add(mem::size_of::<T>()); + debug_assert!(new_len <= self.align_offset.saturating_add(self.max_len)); self.mem.set_len(new_len); ptr::write_unaligned( self.mem.get_unchecked_mut(pos..new_len).as_mut_ptr().cast(), @@ -154,10 +160,11 @@ impl<const ALIGN: usize> AlignedMemory<ALIGN> { /// Unsafe since it assumes that there is enough capacity. pub unsafe fn write_all_unchecked(&mut self, value: &[u8]) { let pos = self.mem.len(); - debug_assert!(pos + value.len() <= self.align_offset + self.max_len); - self.mem.set_len(pos + value.len()); + let new_len = pos.saturating_add(value.len()); + debug_assert!(new_len <= self.align_offset.saturating_add(self.max_len)); + self.mem.set_len(new_len); self.mem - .get_unchecked_mut(pos..pos + value.len()) + .get_unchecked_mut(pos..new_len) .copy_from_slice(value); } } @@ -173,11 +180,17 @@ impl<const ALIGN: usize> Clone for AlignedMemory<ALIGN> { impl<const ALIGN: usize> std::io::Write for AlignedMemory<ALIGN> { fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> { - if self.mem.len() + buf.len() > self.align_offset + self.max_len { - return Err(std::io::Error::new( - std::io::ErrorKind::InvalidInput, - "aligned memory write failed", - )); + match ( + self.mem.len().checked_add(buf.len()), + self.align_offset.checked_add(self.max_len), + ) { + (Some(new_len), Some(allocation_end)) if new_len <= allocation_end => {} + _ => { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "aligned memory write failed", + )) + } } self.mem.extend_from_slice(buf); Ok(buf.len()) @@ -193,18 +206,17 @@ impl<const ALIGN: usize, T: AsRef<[u8]>> From<T> for AlignedMemory<ALIGN> { } } -/// Returns true if `data` is aligned to `align`. -pub fn is_memory_aligned(data: &[u8], align: usize) -> bool { - (data.as_ptr() as usize) - .checked_rem(align) +/// Returns true if `ptr` is aligned to `align`. +pub fn is_memory_aligned(ptr: usize, align: usize) -> bool { + ptr.checked_rem(align) .map(|remainder| remainder == 0) .unwrap_or(false) } #[cfg(test)] mod tests { - use super::*; - use std::io::Write; + #![allow(clippy::integer_arithmetic)] + use {super::*, std::io::Write}; fn do_test<const ALIGN: usize>() { let mut aligned_memory = AlignedMemory::<ALIGN>::with_capacity(10); @@ -234,9 +246,9 @@ mod tests { let mut aligned_memory = AlignedMemory::<ALIGN>::with_capacity_zeroed(15); unsafe { - aligned_memory.write_u8_unchecked(42); + aligned_memory.write_unchecked::<u8>(42); assert_eq!(aligned_memory.len(), 1); - aligned_memory.write_u64_unchecked(0xCAFEBADDDEADCAFE); + aligned_memory.write_unchecked::<u64>(0xCAFEBADDDEADCAFE); assert_eq!(aligned_memory.len(), 9); aligned_memory.fill_write(3, 0).unwrap(); aligned_memory.write_all_unchecked(b"foo"); @@ -262,29 +274,18 @@ mod tests { #[cfg(debug_assertions)] #[test] - #[should_panic(expected = "< self.align_offset + self.max_len")] - fn test_write_u8_unchecked_debug_assert() { - let mut aligned_memory = AlignedMemory::<8>::with_capacity(1); - unsafe { - aligned_memory.write_u8_unchecked(42); - aligned_memory.write_u8_unchecked(24); - } - } - - #[cfg(debug_assertions)] - #[test] - #[should_panic(expected = "<= self.align_offset + self.max_len")] - fn test_write_u64_unchecked_debug_assert() { + #[should_panic(expected = "<= self.align_offset.saturating_add(self.max_len)")] + fn test_write_unchecked_debug_assert() { let mut aligned_memory = AlignedMemory::<8>::with_capacity(15); unsafe { - aligned_memory.write_u64_unchecked(42); - aligned_memory.write_u64_unchecked(24); + aligned_memory.write_unchecked::<u64>(42); + aligned_memory.write_unchecked::<u64>(24); } } #[cfg(debug_assertions)] #[test] - #[should_panic(expected = "<= self.align_offset + self.max_len")] + #[should_panic(expected = "<= self.align_offset.saturating_add(self.max_len)")] fn test_write_all_unchecked_debug_assert() { let mut aligned_memory = AlignedMemory::<8>::with_capacity(5); unsafe { diff --git a/src/elf.rs b/src/elf.rs index abd689a7..182fd650 100644 --- a/src/elf.rs +++ b/src/elf.rs @@ -414,7 +414,7 @@ impl<I: InstructionMeter> Executable<I> { // it must be properly aligned. We assume that HOST_ALIGN is a // multiple of the ELF "natural" alignment. See test_load_unaligned. let aligned; - let bytes = if is_memory_aligned(bytes, HOST_ALIGN) { + let bytes = if is_memory_aligned(bytes.as_ptr() as usize, HOST_ALIGN) { bytes } else { aligned = AlignedMemory::<{ HOST_ALIGN }>::from_slice(bytes);