Skip to content

Commit 6f7399d

Browse files
authored
Handle overflow in GcArrayLayout computation (#13115)
* Handle overflow in `GcArrayLayout` computation Fixes #13034 * fix ci
1 parent 9707e9c commit 6f7399d

File tree

9 files changed

+65
-21
lines changed

9 files changed

+65
-21
lines changed

crates/environ/src/gc.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -349,23 +349,24 @@ pub struct GcArrayLayout {
349349
impl GcArrayLayout {
350350
/// Get the total size of this array for a given length of elements.
351351
#[inline]
352-
pub fn size_for_len(&self, len: u32) -> u32 {
352+
pub fn size_for_len(&self, len: u32) -> Option<u32> {
353353
self.elem_offset(len)
354354
}
355355

356356
/// Get the offset of the `i`th element in an array with this layout.
357357
#[inline]
358-
pub fn elem_offset(&self, i: u32) -> u32 {
359-
self.base_size + i * self.elem_size
358+
pub fn elem_offset(&self, i: u32) -> Option<u32> {
359+
let elem_offset = i.checked_mul(self.elem_size)?;
360+
self.base_size.checked_add(elem_offset)
360361
}
361362

362363
/// Get a `core::alloc::Layout` for an array of this type with the given
363364
/// length.
364-
pub fn layout(&self, len: u32) -> Layout {
365-
let size = self.size_for_len(len);
365+
pub fn layout(&self, len: u32) -> Option<Layout> {
366+
let size = self.size_for_len(len)?;
366367
let size = usize::try_from(size).unwrap();
367368
let align = usize::try_from(self.align).unwrap();
368-
Layout::from_size_align(size, align).unwrap()
369+
Layout::from_size_align(size, align).ok()
369370
}
370371
}
371372

crates/wasmtime/src/runtime/gc/enabled/arrayref.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -393,22 +393,21 @@ impl ArrayRef {
393393
"attempted to use a `ArrayRefPre` with the wrong store"
394394
);
395395

396-
// Type check the elements against the element type.
397-
for elem in elems.clone() {
398-
elem.ensure_matches_ty(store, allocator.ty.element_type().unpack())
399-
.context("element type mismatch")?;
400-
}
401-
402396
let len = u32::try_from(elems.len()).unwrap();
403397

404-
// Allocate the array and write each field value into the appropriate
405-
// offset.
398+
// Allocate the array.
406399
let arrayref = store
407400
.require_gc_store_mut()?
408401
.alloc_uninit_array(allocator.type_index(), len, allocator.layout())
409402
.context("unrecoverable error when allocating new `arrayref`")?
410403
.map_err(|n| GcHeapOutOfMemory::new((), n))?;
411404

405+
// Type check the elements against the element type.
406+
for elem in elems.clone() {
407+
elem.ensure_matches_ty(store, allocator.ty.element_type().unpack())
408+
.context("element type mismatch")?;
409+
}
410+
412411
// From this point on, if we get any errors, then the array is not
413412
// fully initialized, so we need to eagerly deallocate it before the
414413
// next GC where the collector might try to interpret one of the

crates/wasmtime/src/runtime/store/gc.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ impl StoreOpaque {
197197
Ok(x) => Ok(x),
198198
Err(e) => match e.downcast::<crate::GcHeapOutOfMemory<T>>() {
199199
Ok(oom) => {
200+
log::trace!("Got GC heap OOM: {oom}");
201+
200202
let (value, oom) = oom.take_inner();
201203
let bytes_needed = oom.bytes_needed();
202204

crates/wasmtime/src/runtime/types.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1567,7 +1567,7 @@ impl From<TagType> for ExternType {
15671567
///
15681568
/// This is either a packed 8- or -16 bit integer, or else it is some unpacked
15691569
/// Wasm value type.
1570-
#[derive(Clone, Hash)]
1570+
#[derive(Debug, Clone, Hash)]
15711571
pub enum StorageType {
15721572
/// `i8`, an 8-bit integer.
15731573
I8,

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ impl VMArrayRef {
145145
ty: &StorageType,
146146
index: u32,
147147
) -> Val {
148-
let offset = layout.elem_offset(index);
148+
let offset = layout.elem_offset(index).unwrap();
149149
let data = store.unwrap_gc_store_mut().gc_object_data(self.as_gc_ref());
150150
match ty {
151151
StorageType::I8 => Val::I32(data.read_u8(offset).into()),
@@ -205,7 +205,7 @@ impl VMArrayRef {
205205
) -> Result<()> {
206206
debug_assert!(val._matches_ty(&store, &ty.unpack())?);
207207

208-
let offset = layout.elem_offset(index);
208+
let offset = layout.elem_offset(index).unwrap();
209209
let data = store.unwrap_gc_store_mut().gc_object_data(self.as_gc_ref());
210210
match val {
211211
Val::I32(i) if ty.is_i8() => data.write_i8(offset, truncate_i32_to_i8(i)),
@@ -319,7 +319,7 @@ impl VMArrayRef {
319319
val: Val,
320320
) -> Result<()> {
321321
debug_assert!(val._matches_ty(&store, &ty.unpack())?);
322-
let offset = layout.elem_offset(index);
322+
let offset = layout.elem_offset(index).unwrap();
323323
let gcstore = store.require_gc_store_mut()?;
324324
match val {
325325
Val::I32(i) if ty.is_i8() => gcstore

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,7 @@ impl DrcHeap {
414414
let info = match gc_layout {
415415
GcLayout::Array(l) => {
416416
if l.elems_are_gc_refs {
417-
debug_assert_eq!(l.elem_offset(0), GC_REF_ARRAY_ELEMS_OFFSET,);
417+
debug_assert_eq!(l.elem_offset(0), Some(GC_REF_ARRAY_ELEMS_OFFSET));
418418
}
419419
TraceInfo::Array {
420420
gc_ref_elems: l.elems_are_gc_refs,
@@ -1070,9 +1070,12 @@ unsafe impl GcHeap for DrcHeap {
10701070
length: u32,
10711071
layout: &GcArrayLayout,
10721072
) -> Result<Result<VMArrayRef, u64>> {
1073+
let layout = layout
1074+
.layout(length)
1075+
.ok_or_else(|| format_err!("allocation size too large"))?;
10731076
let gc_ref = match self.alloc_raw(
10741077
VMGcHeader::from_kind_and_index(VMGcKind::ArrayRef, ty),
1075-
layout.layout(length),
1078+
layout,
10761079
)? {
10771080
Err(n) => return Ok(Err(n)),
10781081
Ok(gc_ref) => gc_ref,

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,9 +307,12 @@ unsafe impl GcHeap for NullHeap {
307307
length: u32,
308308
layout: &GcArrayLayout,
309309
) -> Result<Result<VMArrayRef, u64>> {
310+
let layout = layout
311+
.layout(length)
312+
.ok_or_else(|| format_err!("allocation size too large"))?;
310313
self.alloc(
311314
VMGcHeader::from_kind_and_index(VMGcKind::ArrayRef, ty),
312-
layout.layout(length),
315+
layout,
313316
)
314317
.map(|r| {
315318
r.map(|r| {

tests/all/arrays.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use crate::ErrorExt;
2+
13
use super::gc_store;
24
use wasmtime::*;
35

@@ -876,3 +878,22 @@ fn instantiate_with_array_global() -> Result<()> {
876878

877879
Ok(())
878880
}
881+
882+
#[test]
883+
fn issue_13034_array_layout_overflow() -> Result<()> {
884+
for (storage, len, val) in [
885+
(StorageType::I8, u32::MAX, Val::I32(0)),
886+
(StorageType::I8, u32::MAX - 1, Val::I32(0)),
887+
(StorageType::I16, u32::MAX / 2, Val::I32(0)),
888+
(ValType::I32.into(), u32::MAX / 4, Val::I32(0)),
889+
(ValType::I64.into(), u32::MAX / 8, Val::I32(0)),
890+
] {
891+
log::trace!("Testing [{storage}; {len}]");
892+
let mut store = gc_store()?;
893+
let array_ty = ArrayType::new(store.engine(), FieldType::new(Mutability::Const, storage));
894+
let pre = ArrayRefPre::new(&mut store, array_ty);
895+
let err = ArrayRef::new(&mut store, &pre, &val, len).unwrap_err();
896+
err.assert_contains("allocation size too large");
897+
}
898+
Ok(())
899+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
;;! gc = true
2+
3+
(module
4+
(type $i32_array (array (mut i32)))
5+
6+
(global $arr (mut (ref null $i32_array)) (ref.null $i32_array))
7+
8+
(func (export "run")
9+
(global.set $arr
10+
(array.new $i32_array (i32.const 0) (i32.const 1073741817))
11+
)
12+
)
13+
)
14+
15+
(assert_trap (invoke "run") "allocation size too large")

0 commit comments

Comments
 (0)