Skip to content

Commit aeaedf3

Browse files
authored
Adjust check for too-large 1-byte-page memories (#13039)
Add tests for the embedder API as well as through wasm, and then additionally ensure that embedder-API-based-growth is limited in the same way as core wasm.
1 parent 767789f commit aeaedf3

4 files changed

Lines changed: 98 additions & 39 deletions

File tree

crates/wasmtime/src/runtime/vm/memory.rs

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -415,38 +415,6 @@ impl Memory {
415415
delta_pages: u64,
416416
limiter: Option<&mut StoreResourceLimiter<'_>>,
417417
) -> Result<Option<usize>, Error> {
418-
let new_size = delta_pages
419-
.checked_mul(self.page_size())
420-
.and_then(|new_bytes| {
421-
let new_bytes = usize::try_from(new_bytes).ok()?;
422-
self.byte_size().checked_add(new_bytes)
423-
});
424-
match new_size {
425-
Some(new_size) => {
426-
// Disallow growth to a value which this linear memory's type
427-
// cannot represent. For example 1-byte-page memories cannot be
428-
// 4GiB in size.
429-
if !self.ty().allow_growth_to(new_size) {
430-
if let Some(limiter) = limiter {
431-
let err = crate::format_err!("memory growth exceeds memory type's limits");
432-
limiter.memory_grow_failed(err)?;
433-
}
434-
return Ok(None);
435-
}
436-
}
437-
438-
// If the new size in memory isn't representable in a `usize` then
439-
// there's no need to actually try to grow it to that size. It's
440-
// impossible to succeed so just fail it early.
441-
None => {
442-
if let Some(limiter) = limiter {
443-
let err = crate::format_err!("memory growth exceeds address space");
444-
limiter.memory_grow_failed(err)?;
445-
}
446-
return Ok(None);
447-
}
448-
}
449-
450418
let result = match self {
451419
Memory::Local(mem) => mem.grow(delta_pages, limiter).await?,
452420
Memory::Shared(mem) => mem.grow(delta_pages)?,
@@ -665,6 +633,17 @@ impl LocalMemory {
665633
.ok()
666634
.and_then(|n| usize::try_from(n).ok());
667635

636+
// Disallow growth to a value which this linear memory's type
637+
// cannot represent. For example 1-byte-page memories cannot be
638+
// 4GiB in size.
639+
if !self.ty().allow_growth_to(new_byte_size) {
640+
if let Some(limiter) = limiter {
641+
let err = crate::format_err!("memory growth exceeds memory type's limits");
642+
limiter.memory_grow_failed(err)?;
643+
}
644+
return Ok(None);
645+
}
646+
668647
// Store limiter gets first chance to reject memory_growing.
669648
if let Some(limiter) = &mut limiter {
670649
if !limiter

tests/all/memory.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,66 @@ fn shared_memory_wait_notify() -> Result<()> {
680680
Ok(())
681681
}
682682

683+
#[test]
684+
#[cfg_attr(miri, ignore)]
685+
fn custom_1byte_page_cant_grow() -> Result<()> {
686+
let mut config = Config::new();
687+
config.shared_memory(true);
688+
689+
let engine = Engine::new(&config)?;
690+
let mut store = Store::new(&engine, ());
691+
692+
// unshared too big
693+
Memory::new(
694+
&mut store,
695+
MemoryTypeBuilder::new()
696+
.min(0xffff_ffff)
697+
.max(Some(0xffff_ffff))
698+
.page_size_log2(0)
699+
.build()?,
700+
)
701+
.unwrap_err();
702+
703+
// shared too big
704+
SharedMemory::new(
705+
&engine,
706+
MemoryTypeBuilder::new()
707+
.min(0xffff_ffff)
708+
.max(Some(0xffff_ffff))
709+
.shared(true)
710+
.page_size_log2(0)
711+
.build()?,
712+
)
713+
.unwrap_err();
714+
715+
if cfg!(target_pointer_width = "64") {
716+
// unshared growth too big
717+
let memory = Memory::new(
718+
&mut store,
719+
MemoryTypeBuilder::new()
720+
.min(0xffff_fffe)
721+
.max(Some(0xffff_ffff))
722+
.page_size_log2(0)
723+
.build()?,
724+
)?;
725+
assert!(memory.grow(&mut store, 1).is_err());
726+
727+
// shared growth too big
728+
let memory = SharedMemory::new(
729+
&engine,
730+
MemoryTypeBuilder::new()
731+
.min(0xffff_fffe)
732+
.max(Some(0xffff_ffff))
733+
.shared(true)
734+
.page_size_log2(0)
735+
.build()?,
736+
)?;
737+
assert!(memory.grow(1).is_err());
738+
}
739+
740+
Ok(())
741+
}
742+
683743
#[wasmtime_test]
684744
#[cfg_attr(miri, ignore)]
685745
#[cfg(target_pointer_width = "64")] // requires large VM reservation
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
;;! custom_page_sizes = true
2+
;;! hogs_memory = true
3+
;;! threads = true
4+
5+
(assert_trap
6+
(module
7+
(memory 0xffff_ffff 0xffff_ffff shared (pagesize 1))
8+
)
9+
"memory minimum size of 4294967295 pages exceeds memory limits")
10+
11+
(module $m
12+
(memory (export "memory") 0xffff_fffe 0xffff_ffff shared (pagesize 1))
13+
)
14+
15+
(module
16+
(import "m" "memory" (memory 0 0xffff_ffff shared (pagesize 1)))
17+
18+
(func (export "grow") (param i32) (result i32)
19+
local.get 0
20+
memory.grow)
21+
)
22+
23+
(assert_return (invoke "grow" (i32.const 1)) (i32.const -1))
24+
(assert_return (invoke "grow" (i32.const 2)) (i32.const -1))
25+
(assert_return (invoke "grow" (i32.const 100)) (i32.const -1))
26+
(assert_return (invoke "grow" (i32.const -1)) (i32.const -1))
27+
(assert_return (invoke "grow" (i32.const 0)) (i32.const -2))

tests/misc_testsuite/custom-page-sizes/max-size-invalid.wast

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
;;! custom_page_sizes = true
22
;;! hogs_memory = true
33

4-
;; FIXME(WebAssembly/custom-page-sizes#45): unclear what the semantics of this
5-
;; test should be. For now Wasmtime traps so this tests that traps happen, but
6-
;; this may change in the specification itself. Either way the problem here is
7-
;; that with 1-byte pages an allocation of 0xffff_ffff bytes is
8-
;; indistinguishable from a growth failure which returns -1. Somehow this needs
9-
;; reconciliation and for now it's done as a trap.
10-
114
(assert_trap
125
(module
136
(memory 0xffff_ffff (pagesize 1))

0 commit comments

Comments
 (0)