Repository: bytecodealliance/wasm-tools
Pull request #1756 (2024-09-03 21:37:51)

threads: fuzz `shared-everything-threads`
This adds initial support for fuzzing the WebAssembly side of the shared-everything-threads [proposal]. Eventual addition of the CM builtins will require extensions to this.

[proposal]: https://github.com/WebAssembly/shared-everything-threads

abrown: At this stage (after df11990) the fuzzer will trigger an `unimplemented!` failure [here](https://github.com/bytecodealliance/wasm-tools/blob/main/crates/wasm-mutate/src/module.rs#L46):

```console
$ cargo +nightly fuzz run run
...
thread '<unnamed>' panicked at crates/wasm-mutate/src/module.rs:46:18:
not implemented
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```

It is unclear to me why this hasn't been triggered previously: shouldn't occasionally enabling GC have triggered this failure due to

https://github.com/bytecodealliance/wasm-tools/blob/8c3346e72d3f5100ed80db2e44e326873a9d2443/crates/wasm-smith/src/core.rs#L1052-L1058
abrown: @fitzgen, as I revisited this PR, I realized that I wanted to add several missing bits as well as split several of the commits into separate PRs. Since I've substantially changed the PR, let's forget about the previous review, though I do believe I've addressed the comments you made.

This PR is currently in an intermediate state and I could use some help figuring out what to do: I could (a) just continue hacking away at this until fuzzing "fully works" but that might take a while due to the large amount of reverse validation I have to add to wasm-smith (i.e., many instructions generate invalid parameters in a shared function context). Another option is to (b) just cordon off the ability to create shared function bodies for now; this would allow generating all the types and what not and for that to be fuzzed while I work on (a). Opinions?
fitzgen: > This PR is currently in an intermediate state and I could use some help figuring out what to do: I could (a) just continue hacking away at this until fuzzing "fully works" but that might take a while due to the large amount of reverse validation I have to add to wasm-smith (i.e., many instructions generate invalid parameters in a shared function context). Another option is to (b) just cordon off the ability to create shared function bodies for now; this would allow generating all the types and what not and for that to be fuzzed while I work on (a). Opinions?

I'm all for landing incremental progress, so long as the parts that are landing are working. We don't need to take an all-or-nothing approach to adding support for generating new wasm proposals in `wasm-smith`.

Patch:
diff --git a/crates/wasm-encoder/src/core/types.rs b/crates/wasm-encoder/src/core/types.rs
index 5cc3bdf5..5dda818f 100644
--- a/crates/wasm-encoder/src/core/types.rs
+++ b/crates/wasm-encoder/src/core/types.rs
@@ -293,6 +293,14 @@ impl RefType {
         },
     };
 
+    /// Create a new abstract reference type.
+    pub fn new_abstract(ty: AbstractHeapType, nullable: bool, shared: bool) -> Self {
+        Self {
+            nullable,
+            heap_type: HeapType::Abstract { shared, ty },
+        }
+    }
+
     /// Set the nullability of this reference type.
     pub fn nullable(mut self, nullable: bool) -> Self {
         self.nullable = nullable;
diff --git a/crates/wasm-mutate/src/mutators/peephole.rs b/crates/wasm-mutate/src/mutators/peephole.rs
index efa618a8..75ede341 100644
--- a/crates/wasm-mutate/src/mutators/peephole.rs
+++ b/crates/wasm-mutate/src/mutators/peephole.rs
@@ -1595,4 +1595,18 @@ mod tests {
         mutator.rules = Some(rules);
         config.match_mutation(original, mutator, expected);
     }
+
+    #[test]
+    fn i8x16_shuffle_handled() {
+        test_default_peephole_mutator(
+            "(module (func (param v128 v128)
+                local.get 0
+                local.get 1
+                i8x16.shuffle 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+                drop)
+            )",
+            "(module (func (param v128 v128)))",
+            4,
+        );
+    }
 }
diff --git a/crates/wasm-mutate/src/mutators/peephole/dfg.rs b/crates/wasm-mutate/src/mutators/peephole/dfg.rs
index 70a19fc6..78664314 100644
--- a/crates/wasm-mutate/src/mutators/peephole/dfg.rs
+++ b/crates/wasm-mutate/src/mutators/peephole/dfg.rs
@@ -3,7 +3,7 @@
 
 use super::eggsy::encoder::rebuild::build_expr;
 use crate::mutators::peephole::{
-    Lang, MemArg, MemArgLane, MemoryCopy, MemoryInit, RefType, TableCopy, TableInit,
+    Lang, MemArg, MemArgLane, MemoryCopy, MemoryInit, RefType, Shuffle, TableCopy, TableInit,
 };
 use crate::mutators::OperatorAndByteOffset;
 use crate::{ModuleInfo, WasmMutate};
@@ -865,6 +865,11 @@ impl<'a> DFGBuilder {
                 }
 
                 Operator::I8x16Swizzle => self.binop(idx, Lang::I8x16Swizzle),
+                Operator::I8x16Shuffle { lanes } => {
+                    let a = Id::from(self.pop_operand(idx, false));
+                    let b = Id::from(self.pop_operand(idx, false));
+                    self.push_node(Lang::I8x16Shuffle(Shuffle { indices: *lanes }, [b, a]), idx);
+                }
                 Operator::I8x16Splat => self.unop(idx, Lang::I8x16Splat),
                 Operator::I16x8Splat => self.unop(idx, Lang::I16x8Splat),
                 Operator::I32x4Splat => self.unop(idx, Lang::I32x4Splat),
diff --git a/crates/wasm-mutate/src/mutators/peephole/eggsy/analysis.rs b/crates/wasm-mutate/src/mutators/peephole/eggsy/analysis.rs
index c2ee6f6b..a8981f23 100644
--- a/crates/wasm-mutate/src/mutators/peephole/eggsy/analysis.rs
+++ b/crates/wasm-mutate/src/mutators/peephole/eggsy/analysis.rs
@@ -347,6 +347,7 @@ impl PeepholeMutationAnalysis {
             Lang::F64x2ReplaceLane(..) => Ok(PrimitiveTypeInfo::V128),
 
             Lang::I8x16Swizzle(_) => Ok(PrimitiveTypeInfo::V128),
+            Lang::I8x16Shuffle(..) => Ok(PrimitiveTypeInfo::V128),
             Lang::I8x16Splat(_) => Ok(PrimitiveTypeInfo::V128),
             Lang::I16x8Splat(_) => Ok(PrimitiveTypeInfo::V128),
             Lang::I32x4Splat(_) => Ok(PrimitiveTypeInfo::V128),
diff --git a/crates/wasm-mutate/src/mutators/peephole/eggsy/encoder/expr2wasm.rs b/crates/wasm-mutate/src/mutators/peephole/eggsy/encoder/expr2wasm.rs
index 67e2c295..f50049ae 100644
--- a/crates/wasm-mutate/src/mutators/peephole/eggsy/encoder/expr2wasm.rs
+++ b/crates/wasm-mutate/src/mutators/peephole/eggsy/encoder/expr2wasm.rs
@@ -525,6 +525,9 @@ pub fn expr2wasm(
                     Lang::F64x2ReplaceLane(lane, _) => insn(Instruction::F64x2ReplaceLane(*lane)),
 
                     Lang::I8x16Swizzle(_) => insn(Instruction::I8x16Swizzle),
+                    Lang::I8x16Shuffle(indices, _) => {
+                        insn(Instruction::I8x16Shuffle(indices.indices))
+                    }
                     Lang::I8x16Splat(_) => insn(Instruction::I8x16Splat),
                     Lang::I16x8Splat(_) => insn(Instruction::I16x8Splat),
                     Lang::I32x4Splat(_) => insn(Instruction::I32x4Splat),
diff --git a/crates/wasm-mutate/src/mutators/peephole/eggsy/lang.rs b/crates/wasm-mutate/src/mutators/peephole/eggsy/lang.rs
index db4d9210..c6640e80 100644
--- a/crates/wasm-mutate/src/mutators/peephole/eggsy/lang.rs
+++ b/crates/wasm-mutate/src/mutators/peephole/eggsy/lang.rs
@@ -714,6 +714,7 @@ lang! {
         F64x2ReplaceLane(u8, [Id; 2]) = "f64x2.replace_lane",
 
         I8x16Swizzle([Id; 2]) = "i8x16.swizzle",
+        I8x16Shuffle(Shuffle, [Id; 2]) = "i8x16.shuffle",
         I8x16Splat([Id; 1]) = "i8x16.splat",
         I16x8Splat([Id; 1]) = "i16x8.splat",
         I32x4Splat([Id; 1]) = "i32x4.splat",
@@ -1188,6 +1189,40 @@ impl FromStr for MemArgLane {
     }
 }
 
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
+pub struct Shuffle {
+    pub indices: [u8; 16],
+}
+
+impl fmt::Display for Shuffle {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        for (i, idx) in self.indices.iter().enumerate() {
+            if i != 0 {
+                write!(f, ".")?;
+            }
+            write!(f, "{idx}")?;
+        }
+        Ok(())
+    }
+}
+
+impl FromStr for Shuffle {
+    type Err = String;
+    fn from_str(s: &str) -> Result<Shuffle, String> {
+        let indices = s
+            .split('.')
+            .map(|part| part.parse::<u8>())
+            .collect::<Result<Vec<_>, _>>()
+            .map_err(|_| format!("failed to parse integer"))?;
+        let indices: &[u8; 16] = indices
+            .as_slice()
+            .try_into()
+            .map_err(|_| format!("wrong number of lanes"))?;
+
+        Ok(Shuffle { indices: *indices })
+    }
+}
+
 trait Children: Sized {
     fn from(list: Vec<Id>) -> Result<Self, String>;
     fn as_slice(&self) -> &[Id];
diff --git a/crates/wasm-smith/src/config.rs b/crates/wasm-smith/src/config.rs
index 7702a170..42cd8102 100644
--- a/crates/wasm-smith/src/config.rs
+++ b/crates/wasm-smith/src/config.rs
@@ -551,17 +551,30 @@ define_config! {
         /// Defaults to `true`.
         pub relaxed_simd_enabled: bool = true,
 
-        /// Determines whether the nontrapping-float-to-int-conversions propsal
-        /// is enabled.
+        /// Determines whether the non-trapping float-to-int conversions
+        /// proposal is enabled.
         ///
         /// Defaults to `true`.
         pub saturating_float_to_int_enabled: bool = true,
 
-        /// Determines whether the sign-extension-ops propsal is enabled.
+        /// Determines whether the sign-extension-ops proposal is enabled.
         ///
         /// Defaults to `true`.
         pub sign_extension_ops_enabled: bool = true,
 
+        /// Determines whether the shared-everything-threads proposal is
+        /// enabled.
+        ///
+        /// The [shared-everything-threads] proposal, among other things,
+        /// extends `shared` attributes to all WebAssembly objects; it builds on
+        /// the [threads] proposal.
+        ///
+        /// [shared-everything-threads]: https://github.com/WebAssembly/shared-everything-threads
+        /// [threads]: https://github.com/WebAssembly/threads
+        ///
+        /// Defaults to `false`.
+        pub shared_everything_threads_enabled: bool = false,
+
         /// Determines whether the SIMD proposal is enabled for generating
         /// instructions.
         ///
@@ -754,6 +767,7 @@ impl<'a> Arbitrary<'a> for Config {
             memory64_enabled: false,
             custom_page_sizes_enabled: false,
             wide_arithmetic_enabled: false,
+            shared_everything_threads_enabled: false,
         };
         config.sanitize();
         Ok(config)
@@ -779,6 +793,7 @@ impl Config {
         if !self.reference_types_enabled {
             self.max_tables = self.max_tables.min(1);
             self.gc_enabled = false;
+            self.shared_everything_threads_enabled = false;
         }
 
         // If simd is disabled then disable all relaxed simd instructions as
@@ -786,6 +801,12 @@ impl Config {
         if !self.simd_enabled {
             self.relaxed_simd_enabled = false;
         }
+
+        // It is impossible to use the shared-everything-threads proposal
+        // without threads, which it is built on.
+        if !self.threads_enabled {
+            self.shared_everything_threads_enabled = false;
+        }
     }
 
     /// Returns the set of features that are necessary for validating against
@@ -821,6 +842,10 @@ impl Config {
         features.set(WasmFeatures::FUNCTION_REFERENCES, self.gc_enabled);
         features.set(WasmFeatures::GC, self.gc_enabled);
         features.set(WasmFeatures::THREADS, self.threads_enabled);
+        features.set(
+            WasmFeatures::SHARED_EVERYTHING_THREADS,
+            self.shared_everything_threads_enabled,
+        );
         features.set(
             WasmFeatures::CUSTOM_PAGE_SIZES,
             self.custom_page_sizes_enabled,
diff --git a/crates/wasm-smith/src/core.rs b/crates/wasm-smith/src/core.rs
index b5430b09..d9d40660 100644
--- a/crates/wasm-smith/src/core.rs
+++ b/crates/wasm-smith/src/core.rs
@@ -1,4 +1,4 @@
-//! Generating arbitary core Wasm modules.
+//! Generating arbitrary core Wasm modules.
 
 mod code_builder;
 pub(crate) mod encode;
@@ -66,6 +66,10 @@ pub struct Module {
     /// Whether we should encode a types section, even if `self.types` is empty.
     should_encode_types: bool,
 
+    /// Whether we should propagate sharedness to types generated inside
+    /// `propagate_shared`.
+    must_share: bool,
+
     /// All of this module's imports. These don't have their own index space,
     /// but instead introduce entries to each imported entity's associated index
     /// space.
@@ -246,6 +250,7 @@ impl Module {
             max_type_limit: MaxTypeLimit::ModuleTypes,
             interesting_values32: Vec::new(),
             interesting_values64: Vec::new(),
+            must_share: false,
         }
     }
 }
@@ -516,7 +521,7 @@ impl Module {
 
             (HT::Concrete(a), HT::Abstract { shared, ty }) => {
                 let a_ty = &self.ty(a).composite_type;
-                if a_ty.shared == shared {
+                if a_ty.shared != shared {
                     return false;
                 }
                 match ty {
@@ -530,7 +535,7 @@ impl Module {
 
             (HT::Abstract { shared, ty }, HT::Concrete(b)) => {
                 let b_ty = &self.ty(b).composite_type;
-                if shared == b_ty.shared {
+                if shared != b_ty.shared {
                     return false;
                 }
                 match ty {
@@ -572,6 +577,7 @@ impl Module {
         let index = u32::try_from(self.types.len()).unwrap();
 
         if let Some(supertype) = ty.supertype {
+            assert_eq!(self.is_shared_type(supertype), ty.composite_type.shared);
             self.super_to_sub_types
                 .entry(supertype)
                 .or_default()
@@ -658,12 +664,12 @@ impl Module {
 
     fn clone_rec_group(&mut self, u: &mut Unstructured, kind: AllowEmptyRecGroup) -> Result<()> {
         // NB: this does *not* guarantee that the cloned rec group will
-        // canonicalize the same as the original rec group and be
-        // deduplicated. That would reqiure a second pass over the cloned types
-        // to rewrite references within the original rec group to be references
-        // into the new rec group. That might make sense to do one day, but for
-        // now we don't do it. That also means that we can't mark the new types
-        // as "subtypes" of the old types and vice versa.
+        // canonicalize the same as the original rec group and be deduplicated.
+        // That would require a second pass over the cloned types to rewrite
+        // references within the original rec group to be references into the
+        // new rec group. That might make sense to do one day, but for now we
+        // don't do it. That also means that we can't mark the new types as
+        // "subtypes" of the old types and vice versa.
         let candidates: Vec<_> = self.clonable_rec_groups(kind).collect();
         let group = u.choose(&candidates)?.clone();
         let new_rec_group_start = self.types.len();
@@ -678,9 +684,11 @@ impl Module {
 
     fn arbitrary_sub_type(&mut self, u: &mut Unstructured) -> Result<SubType> {
         if !self.config.gc_enabled {
+            let shared = self.arbitrary_shared(u)?;
+            let func_type = self.propagate_shared(shared, |m| m.arbitrary_func_type(u))?;
             let composite_type = CompositeType {
-                inner: CompositeInnerType::Func(self.arbitrary_func_type(u)?),
-                shared: false,
+                inner: CompositeInnerType::Func(func_type),
+                shared,
             };
             return Ok(SubType {
                 is_final: true,
@@ -713,7 +721,9 @@ impl Module {
                 *f = self.arbitrary_matching_func_type(u, f)?;
             }
             CompositeInnerType::Struct(s) => {
-                *s = self.arbitrary_matching_struct_type(u, s)?;
+                *s = self.propagate_shared(composite_type.shared, |m| {
+                    m.arbitrary_matching_struct_type(u, s)
+                })?;
             }
         }
         Ok(SubType {
@@ -787,44 +797,55 @@ impl Module {
     }
 
     fn arbitrary_matching_heap_type(&self, u: &mut Unstructured, ty: HeapType) -> Result<HeapType> {
+        use {AbstractHeapType as AHT, CompositeInnerType as CT, HeapType as HT};
+
         if !self.config.gc_enabled {
             return Ok(ty);
         }
-        use CompositeInnerType as CT;
-        use HeapType as HT;
+
         let mut choices = vec![ty];
         match ty {
             HT::Abstract { shared, ty } => {
                 use AbstractHeapType::*;
-                let ht = |ty| HT::Abstract { shared, ty };
+                let add_abstract = |choices: &mut Vec<HT>, tys: &[AHT]| {
+                    choices.extend(tys.iter().map(|&ty| HT::Abstract { shared, ty }));
+                };
+                let add_concrete = |choices: &mut Vec<HT>, tys: &[u32]| {
+                    choices.extend(
+                        tys.iter()
+                            .filter(|&&idx| shared == self.is_shared_type(idx))
+                            .copied()
+                            .map(HT::Concrete),
+                    );
+                };
                 match ty {
                     Any => {
-                        choices.extend([ht(Eq), ht(Struct), ht(Array), ht(I31), ht(None)]);
-                        choices.extend(self.array_types.iter().copied().map(HT::Concrete));
-                        choices.extend(self.struct_types.iter().copied().map(HT::Concrete));
+                        add_abstract(&mut choices, &[Eq, Struct, Array, I31, None]);
+                        add_concrete(&mut choices, &self.array_types);
+                        add_concrete(&mut choices, &self.struct_types);
                     }
                     Eq => {
-                        choices.extend([ht(Struct), ht(Array), ht(I31), ht(None)]);
-                        choices.extend(self.array_types.iter().copied().map(HT::Concrete));
-                        choices.extend(self.struct_types.iter().copied().map(HT::Concrete));
+                        add_abstract(&mut choices, &[Struct, Array, I31, None]);
+                        add_concrete(&mut choices, &self.array_types);
+                        add_concrete(&mut choices, &self.struct_types);
                     }
                     Struct => {
-                        choices.extend([ht(Struct), ht(None)]);
-                        choices.extend(self.struct_types.iter().copied().map(HT::Concrete));
+                        add_abstract(&mut choices, &[Struct, None]);
+                        add_concrete(&mut choices, &self.struct_types);
                     }
                     Array => {
-                        choices.extend([ht(Array), ht(None)]);
-                        choices.extend(self.array_types.iter().copied().map(HT::Concrete));
+                        add_abstract(&mut choices, &[Array, None]);
+                        add_concrete(&mut choices, &self.array_types);
                     }
                     I31 => {
-                        choices.push(ht(None));
+                        add_abstract(&mut choices, &[None]);
                     }
                     Func => {
-                        choices.extend(self.func_types.iter().copied().map(HT::Concrete));
-                        choices.push(ht(NoFunc));
+                        add_abstract(&mut choices, &[NoFunc]);
+                        add_concrete(&mut choices, &self.func_types);
                     }
                     Extern => {
-                        choices.push(ht(NoExtern));
+                        add_abstract(&mut choices, &[NoExtern]);
                     }
                     Exn | NoExn | None | NoExtern | NoFunc | Cont | NoCont => {}
                 }
@@ -915,67 +936,71 @@ impl Module {
         u: &mut Unstructured,
         ty: HeapType,
     ) -> Result<HeapType> {
+        use {AbstractHeapType as AHT, CompositeInnerType as CT, HeapType as HT};
+
         if !self.config.gc_enabled {
             return Ok(ty);
         }
-        use CompositeInnerType as CT;
-        use HeapType as HT;
+
         let mut choices = vec![ty];
         match ty {
             HT::Abstract { shared, ty } => {
                 use AbstractHeapType::*;
-                let ht = |ty| HT::Abstract { shared, ty };
+                let add_abstract = |choices: &mut Vec<HT>, tys: &[AHT]| {
+                    choices.extend(tys.iter().map(|&ty| HT::Abstract { shared, ty }));
+                };
+                let add_concrete = |choices: &mut Vec<HT>, tys: &[u32]| {
+                    choices.extend(
+                        tys.iter()
+                            .filter(|&&idx| shared == self.is_shared_type(idx))
+                            .copied()
+                            .map(HT::Concrete),
+                    );
+                };
                 match ty {
                     None => {
-                        choices.extend([ht(Any), ht(Eq), ht(Struct), ht(Array), ht(I31)]);
-                        choices.extend(self.array_types.iter().copied().map(HT::Concrete));
-                        choices.extend(self.struct_types.iter().copied().map(HT::Concrete));
+                        add_abstract(&mut choices, &[Any, Eq, Struct, Array, I31]);
+                        add_concrete(&mut choices, &self.array_types);
+                        add_concrete(&mut choices, &self.struct_types);
                     }
                     NoExtern => {
-                        choices.push(ht(Extern));
+                        add_abstract(&mut choices, &[Extern]);
                     }
                     NoFunc => {
-                        choices.extend(self.func_types.iter().copied().map(HT::Concrete));
-                        choices.push(ht(Func));
+                        add_abstract(&mut choices, &[Func]);
+                        add_concrete(&mut choices, &self.func_types);
                     }
                     NoExn => {
-                        choices.push(ht(Exn));
+                        add_abstract(&mut choices, &[Exn]);
                     }
                     Struct | Array | I31 => {
-                        choices.extend([ht(Any), ht(Eq)]);
+                        add_abstract(&mut choices, &[Any, Eq]);
                     }
                     Eq => {
-                        choices.push(ht(Any));
+                        add_abstract(&mut choices, &[Any]);
                     }
                     NoCont => {
-                        choices.push(ht(Cont));
+                        add_abstract(&mut choices, &[Cont]);
                     }
                     Exn | Any | Func | Extern | Cont => {}
                 }
             }
             HT::Concrete(mut idx) => {
                 if let Some(sub_ty) = &self.types.get(usize::try_from(idx).unwrap()) {
+                    use AbstractHeapType::*;
                     let ht = |ty| HT::Abstract {
                         shared: sub_ty.composite_type.shared,
                         ty,
                     };
                     match &sub_ty.composite_type.inner {
                         CT::Array(_) => {
-                            choices.extend([
-                                ht(AbstractHeapType::Any),
-                                ht(AbstractHeapType::Eq),
-                                ht(AbstractHeapType::Array),
-                            ]);
+                            choices.extend([ht(Any), ht(Eq), ht(Array)]);
                         }
                         CT::Func(_) => {
-                            choices.push(ht(AbstractHeapType::Func));
+                            choices.push(ht(Func));
                         }
                         CT::Struct(_) => {
-                            choices.extend([
-                                ht(AbstractHeapType::Any),
-                                ht(AbstractHeapType::Eq),
-                                ht(AbstractHeapType::Struct),
-                            ]);
+                            choices.extend([ht(Any), ht(Eq), ht(Struct)]);
                         }
                     }
                 } else {
@@ -1001,26 +1026,29 @@ impl Module {
 
     fn arbitrary_composite_type(&mut self, u: &mut Unstructured) -> Result<CompositeType> {
         use CompositeInnerType as CT;
-        let shared = false; // TODO: handle shared
+        let shared = self.arbitrary_shared(u)?;
+
         if !self.config.gc_enabled {
             return Ok(CompositeType {
                 shared,
-                inner: CT::Func(self.arbitrary_func_type(u)?),
+                inner: CT::Func(self.propagate_shared(shared, |m| m.arbitrary_func_type(u))?),
             });
         }
 
         match u.int_in_range(0..=2)? {
             0 => Ok(CompositeType {
                 shared,
-                inner: CT::Array(ArrayType(self.arbitrary_field_type(u)?)),
+                inner: CT::Array(ArrayType(
+                    self.propagate_shared(shared, |m| m.arbitrary_field_type(u))?,
+                )),
             }),
             1 => Ok(CompositeType {
                 shared,
-                inner: CT::Func(self.arbitrary_func_type(u)?),
+                inner: CT::Func(self.propagate_shared(shared, |m| m.arbitrary_func_type(u))?),
             }),
             2 => Ok(CompositeType {
                 shared,
-                inner: CT::Struct(self.arbitrary_struct_type(u)?),
+                inner: CT::Struct(self.propagate_shared(shared, |m| m.arbitrary_struct_type(u))?),
             }),
             _ => unreachable!(),
         }
@@ -1073,7 +1101,18 @@ impl Module {
 
         if self.config.gc_enabled && concrete_type_limit > 0 && u.arbitrary()? {
             let idx = u.int_in_range(0..=concrete_type_limit - 1)?;
-            return Ok(HeapType::Concrete(idx));
+            // If the caller is demanding a shared heap type but the concrete
+            // type we found is not in fact shared, we skip down below to use an
+            // abstract heap type instead. If the caller is not demanding a
+            // shared type, though, we can use either a shared or unshared
+            // concrete type.
+            if let Some(ty) = self.types.get(idx as usize) {
+                // TODO: in the future, once we can easily query a list of
+                // existing shared types, remove this extra check.
+                if !(self.must_share && !ty.composite_type.shared) {
+                    return Ok(HeapType::Concrete(idx));
+                }
+            }
         }
 
         use AbstractHeapType::*;
@@ -1090,7 +1129,7 @@ impl Module {
         }
 
         Ok(HeapType::Abstract {
-            shared: false, // TODO: turn on shared attribute with shared-everything-threads.
+            shared: self.arbitrary_shared(u)?,
             ty: *u.choose(&choices)?,
         })
     }
@@ -1305,7 +1344,7 @@ impl Module {
 
         // Returns the index to the translated type in the to-be type section, and the reference to
         // the type itself.
-        let mut make_func_type = |parsed_sig_idx: u32| {
+        let mut make_func_type = |module: &Self, parsed_sig_idx: u32| {
             let serialized_sig_idx = match available_types.get_mut(parsed_sig_idx as usize) {
                 None => panic!("signature index refers to a type out of bounds"),
                 Some((_, Some(idx))) => *idx as usize,
@@ -1328,10 +1367,11 @@ impl Module {
                             .collect(),
                     });
                     index_store.replace(new_index as u32);
+                    let shared = module.arbitrary_shared(u).ok()?;
                     new_types.push(SubType {
                         is_final: true,
                         supertype: None,
-                        composite_type: CompositeType::new_func(Rc::clone(&func_type), false), // TODO: handle shared
+                        composite_type: CompositeType::new_func(Rc::clone(&func_type), shared),
                     });
                     new_index
                 }
@@ -1351,7 +1391,7 @@ impl Module {
                 wasmparser::TypeRef::Func(sig_idx) => {
                     if self.funcs.len() >= self.config.max_funcs {
                         continue;
-                    } else if let Some((sig_idx, func_type)) = make_func_type(*sig_idx) {
+                    } else if let Some((sig_idx, func_type)) = make_func_type(&self, *sig_idx) {
                         let entity = EntityType::Func(sig_idx as u32, Rc::clone(&func_type));
                         if type_size_budget < entity.size() {
                             continue;
@@ -1367,7 +1407,8 @@ impl Module {
                     let can_add_tag = self.tags.len() < self.config.max_tags;
                     if !self.config.exceptions_enabled || !can_add_tag {
                         continue;
-                    } else if let Some((sig_idx, func_type)) = make_func_type(*func_type_idx) {
+                    } else if let Some((sig_idx, func_type)) = make_func_type(&self, *func_type_idx)
+                    {
                         let tag_type = TagType {
                             func_type_idx: sig_idx,
                             func_type,
@@ -1530,10 +1571,18 @@ impl Module {
     }
 
     fn arbitrary_global_type(&self, u: &mut Unstructured) -> Result<GlobalType> {
+        let val_type = self.arbitrary_valtype(u)?;
+        // Propagate the inner type's sharedness to the global type.
+        let shared = match val_type {
+            ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {
+                self.arbitrary_shared(u)?
+            }
+            ValType::Ref(r) => self.is_shared_ref_type(r),
+        };
         Ok(GlobalType {
-            val_type: self.arbitrary_valtype(u)?,
+            val_type,
             mutable: u.arbitrary()?,
-            shared: false,
+            shared,
         })
     }
 
@@ -1564,12 +1613,26 @@ impl Module {
             return Ok(());
         }
 
+        // For now, only define non-shared functions. Until we can update
+        // instruction generation to understand the additional sharedness
+        // validation, we don't want to generate instructions that touch
+        // unshared objects from a shared context (TODO: handle shared).
+        let unshared_func_types: Vec<_> = self
+            .func_types
+            .iter()
+            .copied()
+            .filter(|&i| !self.is_shared_type(i))
+            .collect();
+        if unshared_func_types.is_empty() {
+            return Ok(());
+        }
+
         arbitrary_loop(u, self.config.min_funcs, self.config.max_funcs, |u| {
             if !self.can_add_local_or_import_func() {
                 return Ok(false);
             }
-            let max = self.func_types.len() - 1;
-            let ty = self.func_types[u.int_in_range(0..=max)?];
+            let max = unshared_func_types.len() - 1;
+            let ty = unshared_func_types[u.int_in_range(0..=max)?];
             self.funcs.push((ty, self.func_type(ty).clone()));
             self.num_defined_funcs += 1;
             Ok(true)
@@ -1650,7 +1713,6 @@ impl Module {
     fn arbitrary_const_expr(&mut self, ty: ValType, u: &mut Unstructured) -> Result<ConstExpr> {
         let mut choices = mem::take(&mut self.const_expr_choices);
         choices.clear();
-        let num_funcs = self.funcs.len() as u32;
 
         // MVP wasm can `global.get` any immutable imported global in a
         // constant expression, and the GC proposal enables this for all
@@ -1690,12 +1752,25 @@ impl Module {
                 match ty.heap_type {
                     HeapType::Abstract {
                         ty: AbstractHeapType::Func,
-                        ..
-                    } if num_funcs > 0 => {
-                        choices.push(Box::new(move |u, _| {
-                            let func = u.int_in_range(0..=num_funcs - 1)?;
-                            Ok(ConstExpr::ref_func(func))
-                        }));
+                        shared,
+                    } => {
+                        let num_funcs = self
+                            .funcs
+                            .iter()
+                            .filter(|(t, _)| shared == self.is_shared_type(*t))
+                            .count();
+                        if num_funcs > 0 {
+                            let pick = u.int_in_range(0..=num_funcs - 1)?;
+                            let (i, _) = self
+                                .funcs
+                                .iter()
+                                .map(|(t, _)| *t)
+                                .enumerate()
+                                .filter(|(_, t)| shared == self.is_shared_type(*t))
+                                .nth(pick)
+                                .unwrap();
+                            choices.push(Box::new(move |_, _| Ok(ConstExpr::ref_func(i as u32))));
+                        }
                     }
 
                     HeapType::Concrete(ty) => {
@@ -1883,8 +1958,8 @@ impl Module {
                                 supertype: None,
                                 composite_type: CompositeType::new_func(
                                     Rc::clone(&new_type),
-                                    false,
-                                ), // TODO: handle shared
+                                    subtype.composite_type.shared,
+                                ),
                             });
                             let func_index = self.funcs.len() as u32;
                             self.funcs.push((type_index, new_type));
@@ -2202,8 +2277,9 @@ impl Module {
 
         self.code.reserve(self.num_defined_funcs);
         let mut allocs = CodeBuilderAllocations::new(self, self.config.exports.is_some());
-        for (_, ty) in self.funcs[self.funcs.len() - self.num_defined_funcs..].iter() {
-            let body = self.arbitrary_func_body(u, ty, &mut allocs)?;
+        for (idx, ty) in self.funcs[self.funcs.len() - self.num_defined_funcs..].iter() {
+            let shared = self.is_shared_type(*idx);
+            let body = self.arbitrary_func_body(u, ty, &mut allocs, shared)?;
             self.code.push(body);
         }
         allocs.finish(u, self)?;
@@ -2215,9 +2291,10 @@ impl Module {
         u: &mut Unstructured,
         ty: &FuncType,
         allocs: &mut CodeBuilderAllocations,
+        shared: bool,
     ) -> Result<Code> {
         let mut locals = self.arbitrary_locals(u)?;
-        let builder = allocs.builder(ty, &mut locals);
+        let builder = allocs.builder(ty, &mut locals, shared);
         let instructions = if self.config.allow_invalid_funcs && u.arbitrary().unwrap_or(false) {
             Instructions::Arbitrary(arbitrary_vec_u8(u)?)
         } else {
@@ -2549,6 +2626,34 @@ impl Module {
             }
         }
     }
+
+    fn propagate_shared<T>(&mut self, must_share: bool, mut f: impl FnMut(&mut Self) -> T) -> T {
+        let tmp = mem::replace(&mut self.must_share, must_share);
+        let result = f(self);
+        self.must_share = tmp;
+        result
+    }
+
+    fn arbitrary_shared(&self, u: &mut Unstructured) -> Result<bool> {
+        if self.must_share {
+            Ok(true)
+        } else {
+            Ok(self.config.shared_everything_threads_enabled && u.ratio(1, 4)?)
+        }
+    }
+
+    fn is_shared_ref_type(&self, ty: RefType) -> bool {
+        match ty.heap_type {
+            HeapType::Abstract { shared, .. } => shared,
+            HeapType::Concrete(i) => self.types[i as usize].composite_type.shared,
+        }
+    }
+
+    fn is_shared_type(&self, index: u32) -> bool {
+        let index = usize::try_from(index).unwrap();
+        let ty = self.types.get(index).unwrap();
+        ty.composite_type.shared
+    }
 }
 
 pub(crate) fn arbitrary_limits64(
@@ -2604,14 +2709,20 @@ pub(crate) fn configured_valtypes(config: &Config) -> Vec<ValType> {
             true,
         ] {
             use AbstractHeapType::*;
-            for ty in [
+            let abs_ref_types = [
                 Any, Eq, I31, Array, Struct, None, Func, NoFunc, Extern, NoExtern,
-            ] {
-                valtypes.push(ValType::Ref(RefType {
-                    nullable,
-                    // TODO: handle shared
-                    heap_type: HeapType::Abstract { shared: false, ty },
-                }));
+            ];
+            valtypes.extend(
+                abs_ref_types
+                    .iter()
+                    .map(|&ty| ValType::Ref(RefType::new_abstract(ty, nullable, false))),
+            );
+            if config.shared_everything_threads_enabled {
+                valtypes.extend(
+                    abs_ref_types
+                        .iter()
+                        .map(|&ty| ValType::Ref(RefType::new_abstract(ty, nullable, true))),
+                );
             }
         }
     } else if config.reference_types_enabled {
@@ -2646,12 +2757,19 @@ pub(crate) fn arbitrary_table_type(
         Some(module) => module.arbitrary_ref_type(u)?,
         None => RefType::FUNCREF,
     };
+
+    // Propagate the element type's sharedness to the table type.
+    let shared = match module {
+        Some(module) => module.is_shared_ref_type(element_type),
+        None => false,
+    };
+
     Ok(TableType {
         element_type,
         minimum,
         maximum,
         table64,
-        shared: false, // TODO: handle shared
+        shared,
     })
 }
 
diff --git a/crates/wasm-smith/src/core/code_builder.rs b/crates/wasm-smith/src/core/code_builder.rs
index 310800bf..c8c26714 100644
--- a/crates/wasm-smith/src/core/code_builder.rs
+++ b/crates/wasm-smith/src/core/code_builder.rs
@@ -668,6 +668,8 @@ pub(crate) struct CodeBuilderAllocations {
 }
 
 pub(crate) struct CodeBuilder<'a> {
+    #[allow(dead_code)]
+    shared: bool,
     func_ty: &'a FuncType,
     locals: &'a mut Vec<ValType>,
     allocs: &'a mut CodeBuilderAllocations,
@@ -903,6 +905,7 @@ impl CodeBuilderAllocations {
         &'a mut self,
         func_ty: &'a FuncType,
         locals: &'a mut Vec<ValType>,
+        shared: bool,
     ) -> CodeBuilder<'a> {
         self.controls.clear();
         self.controls.push(Control {
@@ -916,6 +919,7 @@ impl CodeBuilderAllocations {
         self.options.clear();
 
         CodeBuilder {
+            shared,
             func_ty,
             locals,
             allocs: self,
@@ -2323,8 +2327,8 @@ fn br_on_null(
                 let ty = *u.choose(&[
                     Func, Extern, Any, None, NoExtern, NoFunc, Eq, Struct, Array, I31,
                 ])?;
-                // TODO: handle shared
-                HeapType::Abstract { shared: false, ty }
+                let shared = module.arbitrary_shared(u)?;
+                HeapType::Abstract { shared, ty }
             }
         }
     };
@@ -5397,25 +5401,26 @@ fn ref_null(
     }
     if module.config.gc_enabled {
         use AbstractHeapType::*;
-        let r = |heap_type| RefType {
-            nullable: true,
-            heap_type,
-        };
-        let a = |abstract_heap_type| HeapType::Abstract {
-            shared: false, // TODO: handle shared
-            ty: abstract_heap_type,
-        };
-        choices.push(r(a(Any)));
-        choices.push(r(a(Eq)));
-        choices.push(r(a(Array)));
-        choices.push(r(a(Struct)));
-        choices.push(r(a(I31)));
-        choices.push(r(a(None)));
-        choices.push(r(a(NoFunc)));
-        choices.push(r(a(NoExtern)));
+        let abs_ref_types = [Any, Eq, Array, Struct, I31, None, NoFunc, NoExtern];
+        choices.extend(
+            abs_ref_types
+                .iter()
+                .map(|&ty| RefType::new_abstract(ty, true, false)),
+        );
+        if module.config().shared_everything_threads_enabled {
+            choices.extend(
+                abs_ref_types
+                    .iter()
+                    .map(|&ty| RefType::new_abstract(ty, true, true)),
+            );
+        }
+
         for i in 0..module.types.len() {
             let i = u32::try_from(i).unwrap();
-            choices.push(r(HeapType::Concrete(i)));
+            choices.push(RefType {
+                nullable: true,
+                heap_type: HeapType::Concrete(i),
+            });
         }
     }
     let ty = *u.choose(&choices)?;
diff --git a/crates/wit-component/src/semver_check.rs b/crates/wit-component/src/semver_check.rs
index 1c4a9f4f..58d0aa74 100644
--- a/crates/wit-component/src/semver_check.rs
+++ b/crates/wit-component/src/semver_check.rs
@@ -55,7 +55,7 @@ pub fn semver_check(mut resolve: Resolve, prev: WorldId, new: WorldId) -> Result
         .package
         .context("new world not in named package")?;
     let new_pkg_name = &resolve.packages[new_pkg_id].name;
-    if old_pkg_id != new_pkg_id {
+    if old_pkg_name != new_pkg_name {
         bail!("the old world is in package {old_pkg_name}, which is not the same as the new world, which is in package {new_pkg_name}", )
     }
 
diff --git a/fuzz/src/lib.rs b/fuzz/src/lib.rs
index f0d70ed9..66942dec 100644
--- a/fuzz/src/lib.rs
+++ b/fuzz/src/lib.rs
@@ -27,6 +27,7 @@ pub fn generate_valid_module(
     config.canonicalize_nans = u.arbitrary()?;
     config.custom_page_sizes_enabled = u.arbitrary()?;
     config.wide_arithmetic_enabled = u.arbitrary()?;
+    config.shared_everything_threads_enabled = u.arbitrary()?;
 
     configure(&mut config, u)?;
 
diff --git a/fuzz/src/mutate.rs b/fuzz/src/mutate.rs
index d1492f1d..f0923d2e 100644
--- a/fuzz/src/mutate.rs
+++ b/fuzz/src/mutate.rs
@@ -11,12 +11,16 @@ pub fn run(u: &mut Unstructured<'_>) -> Result<()> {
 
     let mut seed = 0;
     let mut preserve_semantics = false;
-    let (wasm, _config) = crate::generate_valid_module(u, |_config, u| {
+    let (wasm, _config) = crate::generate_valid_module(u, |config, u| {
         // NB: wasm-mutate is a general-purpose tool so unsupported proposals by
         // wasm-mutate are not disabled here. Those must be rejected with a
         // first-class error in wasm-mutate instead of panicking.
         seed = u.arbitrary()?;
         preserve_semantics = u.arbitrary()?;
+
+        // NB: the mutator will change shared to unshared in ways that create
+        // invalid modules so we disable the proposal (TODO: handle shared).
+        config.shared_everything_threads_enabled = false;
         Ok(())
     })?;
     log::debug!("seed = {}", seed);


Test patch:
diff --git a/tests/cli/semver-check-different-package-versions.wit b/tests/cli/semver-check-different-package-versions.wit
new file mode 100644
index 00000000..ff169c1f
--- /dev/null
+++ b/tests/cli/semver-check-different-package-versions.wit
@@ -0,0 +1,14 @@
+// FAIL: component semver-check % --prev a:b/worldly@0.2.0 --new a:b/worldly@0.2.1
+
+package foo:root;
+package a:b@0.2.0 {
+  world worldly {
+    import a: func();
+    import b: interface {}
+  }
+}
+
+package a:b@0.2.1 {
+  world worldly {
+  }
+}
diff --git a/tests/cli/semver-check-different-package-versions.wit.stderr b/tests/cli/semver-check-different-package-versions.wit.stderr
new file mode 100644
index 00000000..07bfece7
--- /dev/null
+++ b/tests/cli/semver-check-different-package-versions.wit.stderr
@@ -0,0 +1,5 @@
+error: new world is not semver-compatible with the previous world
+
+Caused by:
+    0: type mismatch for import `worldly`
+       missing import named `a` (at offset 0x186)