Skip to content

Commit d5f1b97

Browse files
authored
Fix integer overflow in DRC free list (#13110)
Fixes #13067
1 parent cbfd981 commit d5f1b97

File tree

3 files changed

+34
-5
lines changed

3 files changed

+34
-5
lines changed

crates/wasmtime/src/runtime/vm/gc/enabled/drc.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ impl DrcHeap {
238238
fn dealloc(&mut self, gc_ref: VMGcRef) {
239239
let drc_ref = drc_ref(&gc_ref);
240240
let size = self.index(drc_ref).object_size;
241-
let alloc_size = FreeList::aligned_size(size);
241+
let alloc_size = FreeList::aligned_size(size).unwrap();
242242
let index = gc_ref.as_heap_index().unwrap();
243243

244244
// Poison the freed memory so that any stale access is detectable.
@@ -373,7 +373,7 @@ impl DrcHeap {
373373
}
374374

375375
// Deallocate using the object_size we already read.
376-
let alloc_size = FreeList::aligned_size(object_size);
376+
let alloc_size = FreeList::aligned_size(object_size).unwrap();
377377
let index = gc_ref.as_heap_index().unwrap();
378378

379379
if cfg!(gc_zeal) {
@@ -1007,7 +1007,8 @@ unsafe impl GcHeap for DrcHeap {
10071007
}
10081008

10091009
let object_size = u32::try_from(layout.size()).unwrap();
1010-
let alloc_size = FreeList::aligned_size(object_size);
1010+
let alloc_size = FreeList::aligned_size(object_size)
1011+
.ok_or_else(|| format_err!("allocation size too large"))?;
10111012

10121013
let gc_ref = match self.free_list.as_mut().unwrap().alloc_fast(alloc_size) {
10131014
None => return Ok(Err(u64::try_from(layout.size()).unwrap())),

crates/wasmtime/src/runtime/vm/gc/enabled/free_list.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ impl FreeList {
5858
/// Compute the aligned allocation size for a given byte size. Returns the
5959
/// size rounded up to this free list's alignment, as a u32.
6060
#[inline]
61-
pub fn aligned_size(size: u32) -> u32 {
62-
(size + ALIGN_U32 - 1) & !(ALIGN_U32 - 1)
61+
pub fn aligned_size(size: u32) -> Option<u32> {
62+
Some((size.checked_add(ALIGN_U32)? - 1) & !(ALIGN_U32 - 1))
6363
}
6464

6565
/// Get the current total capacity this free list manages.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
;;! gc = true
2+
;;! exceptions = true
3+
;;! simd = true
4+
5+
(module
6+
(type $a (array (mut i8)))
7+
(global $g (mut anyref) (ref.null any))
8+
(func (export "f") (param i32)
9+
(global.set $g (array.new_default $a (i32.sub (i32.const -1) (local.get 0))))
10+
)
11+
)
12+
13+
;; Most of these will trap with either "allocation size too large" or "GC heap
14+
;; out of memory", but some may succeed. Any of those results is fine, which is
15+
;; why we do not `assert_return` or `assert_trap` here. What we mostly don't
16+
;; want is to e.g. hit any integer overflow assertions in our free lists or bump
17+
;; pointers.
18+
(invoke "f" (i32.const 0))
19+
(invoke "f" (i32.const 8))
20+
(invoke "f" (i32.const 16))
21+
(invoke "f" (i32.const 24))
22+
(invoke "f" (i32.const 32))
23+
(invoke "f" (i32.const 40))
24+
(invoke "f" (i32.const 48))
25+
(invoke "f" (i32.const 56))
26+
(invoke "f" (i32.const 64))
27+
(invoke "f" (i32.const 72))
28+
(invoke "f" (i32.const 80))

0 commit comments

Comments
 (0)