Skip to content

Commit ad9bc5b

Browse files
authored
Fix passive element segment GC roots' endianness (#13230)
* Fix passive element segment GC roots' endianness Passive element segments were registered as GC roots by converting a `*mut ValRaw` to a `*mut VMGcRef`. Because we always store GC refs as little endian inside `ValRaw`, regardless of the target architecture's endianness, this cast is only valid on little endian systems. This bug was not exposed until the introduction of the copying collector in #13107 The fix that this commit makes is to add another kind of GC root for `ValRaw`, where we can get and set the GC root using `ValRaw` internally, to ensure that the endianness (and incidentally also the GC ref offsets within the `ValRaw`) are matched up correctly between the GC root's definition and the collector's use of it. This brings us to three kinds of GC roots: Wasm stack roots, `VMGcRef` roots, and `ValRaw` roots. FWIW, I initially tried to make `VMGcRef` also always store its data as little endian, but this was a larger, more-invasive change and with feedback like #13193 (comment) suggesting the use of `[u8; 4]` instead of `u32` to make the byte ordering explicit, we break `rustc`'s niche type optimizations (since `VMGcRef` is non-zero right now). I also investigated making `PassiveElementSegment` an `enum` or either funcrefs or externrefs, similar to what we do for `wasmtime::runtime::vm::Table`. This also led to an outsized amount of code churn and didn't feel like it was paying for itself. Ultimately, I abandoned these approaches, preferring the one taken in this commit instead. * fix compilation of no-gc builds * review feedback
1 parent f7c886b commit ad9bc5b

7 files changed

Lines changed: 70 additions & 25 deletions

File tree

crates/wasmtime/src/runtime/externals/global.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ impl Global {
312312

313313
if let Some(gc_ref) = unsafe { self.definition(store).as_ref().as_gc_ref() } {
314314
unsafe {
315-
gc_roots_list.add_root(gc_ref.into(), "Wasm global");
315+
gc_roots_list.add_vmgcref_root(gc_ref.into(), "Wasm global");
316316
}
317317
}
318318
}

crates/wasmtime/src/runtime/externals/table.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,7 +574,7 @@ impl Table {
574574
for gc_ref in table.gc_refs_mut() {
575575
if let Some(gc_ref) = gc_ref {
576576
unsafe {
577-
gc_roots_list.add_root(gc_ref.into(), "Wasm table element");
577+
gc_roots_list.add_vmgcref_root(gc_ref.into(), "Wasm table element");
578578
}
579579
}
580580
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,15 +452,15 @@ impl RootSet {
452452
log::trace!("Begin trace user LIFO roots");
453453
for root in &mut self.lifo_roots {
454454
unsafe {
455-
gc_roots_list.add_root((&mut root.gc_ref).into(), "user LIFO root");
455+
gc_roots_list.add_vmgcref_root((&mut root.gc_ref).into(), "user LIFO root");
456456
}
457457
}
458458
log::trace!("End trace user LIFO roots");
459459

460460
log::trace!("Begin trace user owned roots");
461461
for (_id, root) in self.owned_rooted.iter_mut() {
462462
unsafe {
463-
gc_roots_list.add_root(root.into(), "user owned root");
463+
gc_roots_list.add_vmgcref_root(root.into(), "user owned root");
464464
}
465465
}
466466
log::trace!("End trace user owned roots");

crates/wasmtime/src/runtime/store.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2350,7 +2350,7 @@ impl StoreOpaque {
23502350
if let Some(pending_exception) = self.pending_exception.as_mut() {
23512351
unsafe {
23522352
let root = pending_exception.as_gc_ref_mut();
2353-
gc_roots_list.add_root(root.into(), "Pending exception");
2353+
gc_roots_list.add_vmgcref_root(root.into(), "Pending exception");
23542354
}
23552355
}
23562356
log::trace!("End trace GC roots :: pending exception");

crates/wasmtime/src/runtime/vm/gc/gc_runtime.rs

Lines changed: 43 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
use crate::prelude::*;
44
use crate::runtime::vm::{
55
ExternRefHostDataId, ExternRefHostDataTable, GcHeapObject, SendSyncPtr, TypedGcRef, VMArrayRef,
6-
VMExternRef, VMGcHeader, VMGcObjectData, VMGcRef,
6+
VMExternRef, VMGcHeader, VMGcObjectData, VMGcRef, ValRaw,
77
};
88
use crate::store::Asyncness;
99
use crate::vm::VMMemoryDefinition;
@@ -575,12 +575,18 @@ pub struct GcRootsList(Vec<RawGcRoot>);
575575
)]
576576
enum RawGcRoot {
577577
Stack(SendSyncPtr<u32>),
578-
NonStack(SendSyncPtr<VMGcRef>),
578+
VMGcRef(SendSyncPtr<VMGcRef>),
579+
ValRaw(SendSyncPtr<ValRaw>),
579580
}
580581

581582
#[cfg(feature = "gc")]
582583
impl GcRootsList {
583584
/// Add a GC root that is inside a Wasm stack frame to this list.
585+
///
586+
/// # Safety
587+
///
588+
/// The pointer must be to a valid stack-map slot on the Wasm stack and must
589+
/// remain valid while registered within this `GcRootsList`.
584590
#[inline]
585591
pub unsafe fn add_wasm_stack_root(&mut self, ptr_to_root: SendSyncPtr<u32>) {
586592
unsafe {
@@ -595,15 +601,37 @@ impl GcRootsList {
595601
}
596602

597603
/// Add a GC root to this list.
604+
///
605+
/// # Safety
606+
///
607+
/// The pointer must be to a valid `VMGcRef` and must remain valid while
608+
/// registered within this `GcRootsList`.
598609
#[inline]
599-
pub unsafe fn add_root(&mut self, ptr_to_root: SendSyncPtr<VMGcRef>, why: &str) {
610+
pub unsafe fn add_vmgcref_root(&mut self, ptr_to_root: SendSyncPtr<VMGcRef>, why: &str) {
600611
unsafe {
601612
log::trace!(
602-
"Adding non-stack root: {why}: {:#p}",
613+
"Adding VMGcRef root: {why}: {:#p}",
603614
ptr_to_root.as_ref().unchecked_copy()
604615
);
605616
}
606-
self.0.push(RawGcRoot::NonStack(ptr_to_root))
617+
self.0.push(RawGcRoot::VMGcRef(ptr_to_root))
618+
}
619+
620+
/// Add a GC root to this list.
621+
///
622+
/// # Safety
623+
///
624+
/// The pointer must be to a valid `ValRaw` that is a GC reference and must
625+
/// remain valid while registered within this `GcRootsList`.
626+
#[inline]
627+
pub unsafe fn add_val_raw_root(&mut self, ptr_to_root: SendSyncPtr<ValRaw>, why: &str) {
628+
unsafe {
629+
log::trace!(
630+
"Adding ValRaw root: {why}: {:#x}",
631+
ptr_to_root.as_ref().get_anyref()
632+
);
633+
}
634+
self.0.push(RawGcRoot::ValRaw(ptr_to_root))
607635
}
608636

609637
/// Get an iterator over all roots in this list.
@@ -677,11 +705,15 @@ impl GcRoot<'_> {
677705
#[inline]
678706
pub fn get(&self) -> VMGcRef {
679707
match self.raw {
680-
RawGcRoot::NonStack(ptr) => unsafe { ptr::read(ptr.as_ptr()) },
708+
RawGcRoot::VMGcRef(ptr) => unsafe { ptr::read(ptr.as_ptr()) },
681709
RawGcRoot::Stack(ptr) => unsafe {
682710
let raw: u32 = ptr::read(ptr.as_ptr());
683711
VMGcRef::from_raw_u32(raw).expect("non-null")
684712
},
713+
RawGcRoot::ValRaw(ptr) => unsafe {
714+
let val: ValRaw = ptr::read(ptr.as_ptr());
715+
val.get_vmgcref().expect("non-null")
716+
},
685717
}
686718
}
687719

@@ -694,12 +726,16 @@ impl GcRoot<'_> {
694726
/// referencing.
695727
pub fn set(&mut self, new_ref: VMGcRef) {
696728
match self.raw {
697-
RawGcRoot::NonStack(ptr) => unsafe {
729+
RawGcRoot::VMGcRef(ptr) => unsafe {
698730
ptr::write(ptr.as_ptr(), new_ref);
699731
},
700732
RawGcRoot::Stack(ptr) => unsafe {
701733
ptr::write(ptr.as_ptr(), new_ref.as_raw_u32());
702734
},
735+
RawGcRoot::ValRaw(ptr) => unsafe {
736+
let val = ValRaw::vmgcref(Some(new_ref));
737+
ptr::write(ptr.as_ptr(), val);
738+
},
703739
}
704740
}
705741
}

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,15 +227,16 @@ impl Instance {
227227
for segment in self.passive_elements_mut().iter_mut() {
228228
if segment.needs_gc_rooting {
229229
for e in segment.elements() {
230-
let Some(root) = e.as_vmgc_ref_ptr() else {
230+
if e.get_vmgcref().is_none() {
231231
continue;
232-
};
233-
let root: SendSyncPtr<super::VMGcRef> = root.into();
232+
}
233+
234+
let root: SendSyncPtr<ValRaw> = e.into();
234235

235236
// Safety: We know this is a type that needs GC rooting and
236237
// the lifetime is implied by our safety contract.
237238
unsafe {
238-
gc_roots.add_root(root, "passive element segment");
239+
gc_roots.add_val_raw_root(root, "passive element segment");
239240
}
240241
}
241242
}

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1750,6 +1750,18 @@ impl ValRaw {
17501750
ValRaw { exnref: r.to_le() }
17511751
}
17521752

1753+
#[inline]
1754+
pub(crate) fn vmgcref(r: Option<VMGcRef>) -> ValRaw {
1755+
let raw = r.map_or(0, |r| r.as_raw_u32());
1756+
1757+
// NB: All `VMGcRef`-based `ValRaw`s are the same.
1758+
debug_assert_eq!(raw, ValRaw::anyref(raw).get_exnref());
1759+
debug_assert_eq!(raw, ValRaw::exnref(raw).get_externref());
1760+
debug_assert_eq!(raw, ValRaw::externref(raw).get_anyref());
1761+
1762+
ValRaw::anyref(raw)
1763+
}
1764+
17531765
/// Gets the WebAssembly `i32` value
17541766
#[inline]
17551767
pub fn get_i32(&self) -> i32 {
@@ -1823,15 +1835,11 @@ impl ValRaw {
18231835
exnref
18241836
}
18251837

1826-
/// Convert this `&ValRaw` into a pointer to its inner `VMGcRef`.
1827-
#[cfg(feature = "gc")]
1828-
pub(crate) fn as_vmgc_ref_ptr(&self) -> Option<NonNull<crate::vm::VMGcRef>> {
1829-
if self.get_anyref() == 0 {
1830-
return None;
1831-
}
1832-
let ptr = &raw const self.anyref;
1833-
let ptr = NonNull::new(ptr.cast_mut()).unwrap();
1834-
Some(ptr.cast())
1838+
/// Get the inner `VMGcRef`.
1839+
pub(crate) fn get_vmgcref(&self) -> Option<crate::vm::VMGcRef> {
1840+
debug_assert_eq!(self.get_anyref(), self.get_exnref());
1841+
debug_assert_eq!(self.get_anyref(), self.get_externref());
1842+
VMGcRef::from_raw_u32(self.get_anyref())
18351843
}
18361844
}
18371845

0 commit comments

Comments
 (0)