diff --git a/crates/runtime/src/memory.rs b/crates/runtime/src/memory.rs index 5a69d2136e..6ab2ed83fc 100644 --- a/crates/runtime/src/memory.rs +++ b/crates/runtime/src/memory.rs @@ -90,7 +90,7 @@ pub struct MmapMemory { impl MmapMemory { /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. - pub fn new(plan: &MemoryPlan, minimum: usize, maximum: Option) -> Result { + pub fn new(plan: &MemoryPlan, minimum: usize, mut maximum: Option) -> Result { // It's a programmer error for these two configuration values to exceed // the host available address space, so panic if such a configuration is // found (mostly an issue for hypothetical 32-bit hosts). @@ -98,13 +98,21 @@ impl MmapMemory { let pre_guard_bytes = usize::try_from(plan.pre_guard_size).unwrap(); let (alloc_bytes, extra_to_reserve_on_growth) = match plan.style { + // Dynamic memories start with the minimum size plus the `reserve` + // amount specified to grow into. MemoryStyle::Dynamic { reserve } => (minimum, usize::try_from(reserve).unwrap()), + + // Static memories will never move in memory and consequently get + // their entire allocation up-front with no extra room to grow into. + // Note that the `maximum` is adjusted here to whatever the smaller + // of the two is, the `maximum` given or the `bound` specified for + // this memory. MemoryStyle::Static { bound } => { assert_ge!(bound, plan.memory.minimum); - ( - usize::try_from(bound.checked_mul(WASM_PAGE_SIZE_U64).unwrap()).unwrap(), - 0, - ) + let bound_bytes = + usize::try_from(bound.checked_mul(WASM_PAGE_SIZE_U64).unwrap()).unwrap(); + maximum = Some(bound_bytes.min(maximum.unwrap_or(usize::MAX))); + (bound_bytes, 0) } }; let request_bytes = pre_guard_bytes diff --git a/crates/wasmtime/src/config.rs b/crates/wasmtime/src/config.rs index 84dfc4d2d8..be7dd93d9b 100644 --- a/crates/wasmtime/src/config.rs +++ b/crates/wasmtime/src/config.rs @@ -1119,6 +1119,22 @@ impl Config { self } + /// Indicates that the "static" style of memory should always be used. + /// + /// This configuration option enables selecting the "static" option for all + /// linear memories created within this `Config`. This means that all + /// memories will be allocated up-front and will never move. Additionally + /// this means that all memories are synthetically limited by the + /// [`Config::static_memory_maximum_size`] option, irregardless of what the + /// actual maximum size is on the memory's original type. + /// + /// For the difference between static and dynamic memories, see the + /// [`Config::static_memory_maximum_size`]. + pub fn static_memory_forced(&mut self, force: bool) -> &mut Self { + self.tunables.static_memory_bound_is_maximum = force; + self + } + /// Configures the size, in bytes, of the guard region used at the end of a /// static memory's address space reservation. /// diff --git a/src/lib.rs b/src/lib.rs index e3941e42c0..fb9487e5a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -244,6 +244,10 @@ struct CommonOptions { #[structopt(long, value_name = "MAXIMUM")] static_memory_maximum_size: Option, + /// Force using a "static" style for all wasm memories. + #[structopt(long)] + static_memory_forced: bool, + /// Byte size of the guard region after static memories are allocated. #[structopt(long, value_name = "SIZE")] static_memory_guard_size: Option, @@ -319,6 +323,8 @@ impl CommonOptions { config.static_memory_maximum_size(max); } + config.static_memory_forced(self.static_memory_forced); + if let Some(size) = self.static_memory_guard_size { config.static_memory_guard_size(size); } diff --git a/tests/all/memory.rs b/tests/all/memory.rs index c212f50a5a..387e41bc28 100644 --- a/tests/all/memory.rs +++ b/tests/all/memory.rs @@ -362,3 +362,17 @@ fn tiny_static_heap() -> Result<()> { f.call(&mut store, ())?; Ok(()) } + +#[test] +fn static_forced_max() -> Result<()> { + let mut config = Config::new(); + config.static_memory_maximum_size(5 * 65536); + config.static_memory_forced(true); + let engine = Engine::new(&config)?; + let mut store = Store::new(&engine, ()); + + let mem = Memory::new(&mut store, MemoryType::new(0, None))?; + mem.grow(&mut store, 5).unwrap(); + assert!(mem.grow(&mut store, 1).is_err()); + Ok(()) +}