Skip to content

Commit e60c374

Browse files
authored
Merge pull request from GHSA-44mr-8vmm-wjhg
This ensures that memories, even with zero contents, still have the necessary virtual mappings as required by the code generator to report out-of-bounds reads/writes.
1 parent 2614f2e commit e60c374

File tree

2 files changed

+64
-11
lines changed

2 files changed

+64
-11
lines changed

crates/runtime/src/instance/allocator/pooling.rs

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use std::convert::TryFrom;
1919
use std::mem;
2020
use std::sync::Mutex;
2121
use wasmtime_environ::{
22-
DefinedMemoryIndex, DefinedTableIndex, HostPtr, Module, PrimaryMap, Tunables, VMOffsets,
23-
WASM_PAGE_SIZE,
22+
DefinedMemoryIndex, DefinedTableIndex, HostPtr, MemoryStyle, Module, PrimaryMap, Tunables,
23+
VMOffsets, WASM_PAGE_SIZE,
2424
};
2525

2626
mod index_allocator;
@@ -386,6 +386,20 @@ impl InstancePool {
386386
.defined_memory_index(memory_index)
387387
.expect("should be a defined memory since we skipped imported ones");
388388

389+
match plan.style {
390+
MemoryStyle::Static { bound } => {
391+
let bound = bound * u64::from(WASM_PAGE_SIZE);
392+
if bound < self.memories.static_memory_bound {
393+
return Err(InstantiationError::Resource(anyhow!(
394+
"static bound of {bound:x} bytes incompatible with \
395+
reservation of {:x} bytes",
396+
self.memories.static_memory_bound,
397+
)));
398+
}
399+
}
400+
MemoryStyle::Dynamic { .. } => {}
401+
}
402+
389403
let memory = unsafe {
390404
std::slice::from_raw_parts_mut(
391405
self.memories.get_base(instance_index, defined_index),
@@ -658,6 +672,7 @@ struct MemoryPool {
658672
initial_memory_offset: usize,
659673
max_memories: usize,
660674
max_instances: usize,
675+
static_memory_bound: u64,
661676
}
662677

663678
impl MemoryPool {
@@ -679,15 +694,11 @@ impl MemoryPool {
679694
);
680695
}
681696

682-
let memory_size = if instance_limits.memory_pages > 0 {
683-
usize::try_from(
684-
u64::from(tunables.static_memory_bound) * u64::from(WASM_PAGE_SIZE)
685-
+ tunables.static_memory_offset_guard_size,
686-
)
687-
.map_err(|_| anyhow!("memory reservation size exceeds addressable memory"))?
688-
} else {
689-
0
690-
};
697+
let static_memory_bound =
698+
u64::from(tunables.static_memory_bound) * u64::from(WASM_PAGE_SIZE);
699+
let memory_size =
700+
usize::try_from(static_memory_bound + tunables.static_memory_offset_guard_size)
701+
.map_err(|_| anyhow!("memory reservation size exceeds addressable memory"))?;
691702

692703
assert!(
693704
memory_size % crate::page_size() == 0,
@@ -745,6 +756,7 @@ impl MemoryPool {
745756
max_memories,
746757
max_instances,
747758
max_memory_size: (instance_limits.memory_pages as usize) * (WASM_PAGE_SIZE as usize),
759+
static_memory_bound,
748760
};
749761

750762
Ok(pool)

tests/all/pooling_allocator.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,3 +721,44 @@ configured maximum of 16 bytes; breakdown of allocation requirement:
721721

722722
Ok(())
723723
}
724+
725+
#[test]
726+
fn zero_memory_pages_disallows_oob() -> Result<()> {
727+
let mut config = Config::new();
728+
config.allocation_strategy(InstanceAllocationStrategy::Pooling {
729+
strategy: PoolingAllocationStrategy::NextAvailable,
730+
instance_limits: InstanceLimits {
731+
count: 1,
732+
memory_pages: 0,
733+
..Default::default()
734+
},
735+
});
736+
737+
let engine = Engine::new(&config)?;
738+
let module = Module::new(
739+
&engine,
740+
r#"
741+
(module
742+
(memory 0)
743+
744+
(func (export "load") (param i32) (result i32)
745+
local.get 0
746+
i32.load)
747+
748+
(func (export "store") (param i32 )
749+
local.get 0
750+
local.get 0
751+
i32.store)
752+
)
753+
"#,
754+
)?;
755+
let mut store = Store::new(&engine, ());
756+
let instance = Instance::new(&mut store, &module, &[])?;
757+
let load32 = instance.get_typed_func::<i32, i32, _>(&mut store, "load")?;
758+
let store32 = instance.get_typed_func::<i32, (), _>(&mut store, "store")?;
759+
for i in 0..31 {
760+
assert!(load32.call(&mut store, 1 << i).is_err());
761+
assert!(store32.call(&mut store, 1 << i).is_err());
762+
}
763+
Ok(())
764+
}

0 commit comments

Comments
 (0)