Explicitly support memory size changing in between allocations (#9477)

Co-authored-by: Bastian Köcher <info@kchr.de>
This commit is contained in:
Sergei Shulepov
2021-08-03 16:22:01 +02:00
committed by GitHub
parent 3b471704d9
commit c816ebc4b3
2 changed files with 61 additions and 0 deletions
+3
View File
@@ -24,6 +24,9 @@ pub enum Error {
/// Allocator run out of space. /// Allocator run out of space.
#[error("Allocator ran out of space")] #[error("Allocator ran out of space")]
AllocatorOutOfSpace, AllocatorOutOfSpace,
/// The client passed a memory instance which is smaller than previously observed.
#[error("Shrinking of the underlying memory is observed")]
MemoryShrinked,
/// Some other error occurred. /// Some other error occurred.
#[error("Other: {0}")] #[error("Other: {0}")]
Other(&'static str), Other(&'static str),
@@ -326,6 +326,7 @@ pub struct FreeingBumpHeapAllocator {
poisoned: bool, poisoned: bool,
max_total_size: u32, max_total_size: u32,
max_bumper: u32, max_bumper: u32,
last_observed_memory_size: u32,
} }
impl Drop for FreeingBumpHeapAllocator { impl Drop for FreeingBumpHeapAllocator {
@@ -355,6 +356,7 @@ impl FreeingBumpHeapAllocator {
poisoned: false, poisoned: false,
max_total_size: 0, max_total_size: 0,
max_bumper: aligned_heap_base, max_bumper: aligned_heap_base,
last_observed_memory_size: 0,
} }
} }
@@ -364,6 +366,9 @@ impl FreeingBumpHeapAllocator {
/// this function is rounded to the next power of two. If the requested /// this function is rounded to the next power of two. If the requested
/// size is below 8 bytes it will be rounded up to 8 bytes. /// size is below 8 bytes it will be rounded up to 8 bytes.
/// ///
/// The identity or the type of the passed memory object does not matter. However, the size of
/// memory cannot shrink compared to the memory passed in previous invocations.
///
/// NOTE: Once the allocator has returned an error all subsequent requests will return an error. /// NOTE: Once the allocator has returned an error all subsequent requests will return an error.
/// ///
/// # Arguments /// # Arguments
@@ -380,6 +385,8 @@ impl FreeingBumpHeapAllocator {
} }
let bomb = PoisonBomb { poisoned: &mut self.poisoned }; let bomb = PoisonBomb { poisoned: &mut self.poisoned };
Self::observe_memory_size(&mut self.last_observed_memory_size, mem)?;
let order = Order::from_size(size)?; let order = Order::from_size(size)?;
let header_ptr: u32 = match self.free_lists[order] { let header_ptr: u32 = match self.free_lists[order] {
@@ -430,6 +437,9 @@ impl FreeingBumpHeapAllocator {
/// Deallocates the space which was allocated for a pointer. /// Deallocates the space which was allocated for a pointer.
/// ///
/// The identity or the type of the passed memory object does not matter. However, the size of
/// memory cannot shrink compared to the memory passed in previous invocations.
///
/// NOTE: Once the allocator has returned an error all subsequent requests will return an error. /// NOTE: Once the allocator has returned an error all subsequent requests will return an error.
/// ///
/// # Arguments /// # Arguments
@@ -447,6 +457,8 @@ impl FreeingBumpHeapAllocator {
let bomb = PoisonBomb { poisoned: &mut self.poisoned }; let bomb = PoisonBomb { poisoned: &mut self.poisoned };
Self::observe_memory_size(&mut self.last_observed_memory_size, mem)?;
let header_ptr = u32::from(ptr) let header_ptr = u32::from(ptr)
.checked_sub(HEADER_SIZE) .checked_sub(HEADER_SIZE)
.ok_or_else(|| error("Invalid pointer for deallocation"))?; .ok_or_else(|| error("Invalid pointer for deallocation"))?;
@@ -493,6 +505,17 @@ impl FreeingBumpHeapAllocator {
*bumper += size; *bumper += size;
Ok(res) Ok(res)
} }
fn observe_memory_size<M: Memory + ?Sized>(
last_observed_memory_size: &mut u32,
mem: &mut M,
) -> Result<(), Error> {
if mem.size() < *last_observed_memory_size {
return Err(Error::MemoryShrinked)
}
*last_observed_memory_size = mem.size();
Ok(())
}
} }
/// A trait for abstraction of accesses to a wasm linear memory. Used to read or modify the /// A trait for abstraction of accesses to a wasm linear memory. Used to read or modify the
@@ -930,4 +953,39 @@ mod tests {
MAX_POSSIBLE_ALLOCATION MAX_POSSIBLE_ALLOCATION
); );
} }
#[test]
fn accepts_growing_memory() {
const ITEM_SIZE: u32 = 16;
const ITEM_ON_HEAP_SIZE: usize = 16 + HEADER_SIZE as usize;
let mut mem = vec![0u8; ITEM_ON_HEAP_SIZE * 2];
let mut heap = FreeingBumpHeapAllocator::new(0);
let _ = heap.allocate(&mut mem[..], ITEM_SIZE).unwrap();
let _ = heap.allocate(&mut mem[..], ITEM_SIZE).unwrap();
mem.extend_from_slice(&[0u8; ITEM_ON_HEAP_SIZE]);
let _ = heap.allocate(&mut mem[..], ITEM_SIZE).unwrap();
}
#[test]
fn doesnt_accept_shrinking_memory() {
const ITEM_SIZE: u32 = 16;
const ITEM_ON_HEAP_SIZE: usize = 16 + HEADER_SIZE as usize;
let initial_size = ITEM_ON_HEAP_SIZE * 3;
let mut mem = vec![0u8; initial_size];
let mut heap = FreeingBumpHeapAllocator::new(0);
let _ = heap.allocate(&mut mem[..], ITEM_SIZE).unwrap();
mem.truncate(initial_size - 1);
match heap.allocate(&mut mem[..], ITEM_SIZE).unwrap_err() {
Error::MemoryShrinked => (),
_ => panic!(),
}
}
} }