Fix MemFd's allocated memory for dynamic memories (#3763)

This fixes a bug in the memfd-related management of a linear memory
where for dynamic memories memfd wasn't informed of the extra room that
the dynamic memory could grow into, only the actual size of linear
memory, which ended up tripping an assert once the memory was grown. The
fix here is pretty simple which is to factor in this extra space when
passing the allocation size to the creation of the `MemFdSlot`.
This commit is contained in:
Alex Crichton
2022-02-03 11:56:16 -06:00
committed by GitHub
parent 31d4d2cbe0
commit 4ba3404ca3
3 changed files with 61 additions and 1 deletions

View File

@@ -405,6 +405,7 @@ pub fn make_api_calls(api: generators::api::ApiCalls) {
/// ///
/// Ensures that spec tests pass regardless of the `Config`. /// Ensures that spec tests pass regardless of the `Config`.
pub fn spectest(mut fuzz_config: generators::Config, test: generators::SpecTest) { pub fn spectest(mut fuzz_config: generators::Config, test: generators::SpecTest) {
crate::init_fuzzing();
fuzz_config.module_config.set_spectest_compliant(); fuzz_config.module_config.set_spectest_compliant();
log::debug!("running {:?} with {:?}", test.file, fuzz_config); log::debug!("running {:?} with {:?}", test.file, fuzz_config);
let mut wast_context = WastContext::new(fuzz_config.to_store()); let mut wast_context = WastContext::new(fuzz_config.to_store());

View File

@@ -159,7 +159,11 @@ impl MmapMemory {
let memfd = match memfd_image { let memfd = match memfd_image {
Some(image) => { Some(image) => {
let base = unsafe { mmap.as_mut_ptr().add(pre_guard_bytes) }; let base = unsafe { mmap.as_mut_ptr().add(pre_guard_bytes) };
let mut memfd_slot = MemFdSlot::create(base.cast(), minimum, alloc_bytes); let mut memfd_slot = MemFdSlot::create(
base.cast(),
minimum,
alloc_bytes + extra_to_reserve_on_growth,
);
memfd_slot.instantiate(minimum, Some(image))?; memfd_slot.instantiate(minimum, Some(image))?;
// On drop, we will unmap our mmap'd range that this // On drop, we will unmap our mmap'd range that this
// memfd_slot was mapped on top of, so there is no // memfd_slot was mapped on top of, so there is no

View File

@@ -376,3 +376,58 @@ fn static_forced_max() -> Result<()> {
assert!(mem.grow(&mut store, 1).is_err()); assert!(mem.grow(&mut store, 1).is_err());
Ok(()) Ok(())
} }
#[test]
fn dynamic_extra_growth_unchanged_pointer() -> Result<()> {
const EXTRA_PAGES: u64 = 5;
let mut config = Config::new();
config.static_memory_maximum_size(0);
// 5 wasm pages extra
config.dynamic_memory_reserved_for_growth(EXTRA_PAGES * (1 << 16));
let engine = Engine::new(&config)?;
let mut store = Store::new(&engine, ());
fn assert_behaves_well(store: &mut Store<()>, mem: &Memory) -> Result<()> {
let ptr = mem.data_ptr(&store);
// Each growth here should retain the same linear pointer in memory and the
// memory shouldn't get moved.
for _ in 0..EXTRA_PAGES {
mem.grow(&mut *store, 1)?;
assert_eq!(ptr, mem.data_ptr(&store));
}
// Growth afterwards though will be forced to move the pointer
mem.grow(&mut *store, 1)?;
let new_ptr = mem.data_ptr(&store);
assert_ne!(ptr, new_ptr);
for _ in 0..EXTRA_PAGES - 1 {
mem.grow(&mut *store, 1)?;
assert_eq!(new_ptr, mem.data_ptr(&store));
}
Ok(())
}
let mem = Memory::new(&mut store, MemoryType::new(10, None))?;
assert_behaves_well(&mut store, &mem)?;
let module = Module::new(&engine, r#"(module (memory (export "mem") 10))"#)?;
let instance = Instance::new(&mut store, &module, &[])?;
let mem = instance.get_memory(&mut store, "mem").unwrap();
assert_behaves_well(&mut store, &mem)?;
let module = Module::new(
&engine,
r#"
(module
(memory (export "mem") 10)
(data (i32.const 0) ""))
"#,
)?;
let instance = Instance::new(&mut store, &module, &[])?;
let mem = instance.get_memory(&mut store, "mem").unwrap();
assert_behaves_well(&mut store, &mem)?;
Ok(())
}