diff --git a/crates/wasm-smith/tests/common/mod.rs b/crates/wasm-smith/tests/common/mod.rs index 8a238776d6..332fd0ca77 100644 --- a/crates/wasm-smith/tests/common/mod.rs +++ b/crates/wasm-smith/tests/common/mod.rs @@ -2,7 +2,7 @@ use wasm_smith::Config; use wasmparser::{types::Types, Validator, WasmFeatures}; pub fn parser_features_from_config(config: &Config) -> WasmFeatures { - let mut features = WasmFeatures::MUTABLE_GLOBAL | WasmFeatures::FLOATS; + let mut features = WasmFeatures::MUTABLE_GLOBAL | WasmFeatures::wasm1(); features.set( WasmFeatures::SATURATING_FLOAT_TO_INT, config.saturating_float_to_int_enabled, diff --git a/crates/wasmparser/src/features.rs b/crates/wasmparser/src/features.rs index fc154b1675..aa47eee06d 100644 --- a/crates/wasmparser/src/features.rs +++ b/crates/wasmparser/src/features.rs @@ -157,6 +157,23 @@ define_wasm_features! { /// Support this feature as long as all leading browsers also support it /// https://github.com/WebAssembly/exception-handling/blob/main/proposals/exception-handling/legacy/Exceptions.md pub legacy_exceptions: LEGACY_EXCEPTIONS(1 << 25) = false; + /// Whether or not gc types are enabled. + /// + /// This feature does not correspond to any WebAssembly proposal nor + /// concept in the specification itself. This is intended to assist + /// embedders in disabling support for GC types at validation time. For + /// example if an engine wants to support all of WebAssembly except + /// a runtime garbage collector it could disable this feature. + /// + /// This features is enabled by default and is used to gate types such + /// as `externref` or `anyref`. Note that the requisite WebAssembly + /// proposal must also be enabled for types like `externref`, meaning + /// that it requires both `REFERENCE_TYPES` and `GC_TYPE` to be enabled. + /// + /// Note that the `funcref` and `exnref` types are not gated by this + /// feature. Those are expected to not require a full garbage collector + /// so are not gated by this. + pub gc_types: GC_TYPES(1 << 26) = true; } } @@ -164,7 +181,7 @@ impl WasmFeatures { /// Returns the feature set associated with the 1.0 version of the /// WebAssembly specification or the "MVP" feature set. pub fn wasm1() -> WasmFeatures { - WasmFeatures::FLOATS + WasmFeatures::FLOATS | WasmFeatures::GC_TYPES } /// Returns the feature set associated with the 2.0 version of the diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index 5425fad7d2..700130e57f 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -251,6 +251,12 @@ impl WasmFeatures { } match r.heap_type() { HeapType::Concrete(_) => { + // Note that `self.gc_types()` is not checked here because + // concrete pointers to function types are allowed. GC types + // are disallowed by instead rejecting the definition of + // array/struct types and only allowing the definition of + // function types. + // Indexed types require either the function-references or gc // proposal as gc implies function references here. if self.function_references() || self.gc() { @@ -266,6 +272,13 @@ impl WasmFeatures { "shared reference types require the shared-everything-threads proposal", ); } + + // Apply the "gc-types" feature which disallows all heap types + // except exnref/funcref. + if !self.gc_types() && ty != Func && ty != Exn { + return Err("gc types are disallowed but found type which requires gc"); + } + match (ty, r.is_nullable()) { // funcref/externref only require `reference-types`. (Func, true) | (Extern, true) => Ok(()), diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs index fbcdbcbbec..75cb01c5b2 100644 --- a/crates/wasmparser/src/validator/core.rs +++ b/crates/wasmparser/src/validator/core.rs @@ -699,10 +699,16 @@ impl Module { } CompositeInnerType::Array(t) => { if !features.gc() { - return Err(BinaryReaderError::new( + bail!( + offset, "array indexed types not supported without the gc feature", + ); + } + if !features.gc_types() { + bail!( offset, - )); + "cannot define array types when gc types are disabled", + ); } match &t.0.element_type { StorageType::I8 | StorageType::I16 => { @@ -713,10 +719,16 @@ impl Module { } CompositeInnerType::Struct(t) => { if !features.gc() { - return Err(BinaryReaderError::new( + bail!( + offset, "struct indexed types not supported without the gc feature", + ); + } + if !features.gc_types() { + bail!( offset, - )); + "cannot define struct types when gc types are disabled", + ); } for ft in t.fields.iter() { match &ft.element_type { diff --git a/tests/cli/validate-unknown-features.wat.stderr b/tests/cli/validate-unknown-features.wat.stderr index 7aed7b2cda..3e0ccb84e4 100644 --- a/tests/cli/validate-unknown-features.wat.stderr +++ b/tests/cli/validate-unknown-features.wat.stderr @@ -1,4 +1,4 @@ error: invalid value 'unknown' for '--features ': unknown feature `unknown` -Valid features: mutable-global, saturating-float-to-int, sign-extension, reference-types, multi-value, bulk-memory, simd, relaxed-simd, threads, shared-everything-threads, tail-call, floats, multi-memory, exceptions, memory64, extended-const, component-model, function-references, memory-control, gc, custom-page-sizes, component-model-values, component-model-nested-names, component-model-more-flags, component-model-multiple-returns, legacy-exceptions +Valid features: mutable-global, saturating-float-to-int, sign-extension, reference-types, multi-value, bulk-memory, simd, relaxed-simd, threads, shared-everything-threads, tail-call, floats, multi-memory, exceptions, memory64, extended-const, component-model, function-references, memory-control, gc, custom-page-sizes, component-model-values, component-model-nested-names, component-model-more-flags, component-model-multiple-returns, legacy-exceptions, gc-types For more information, try '--help'. diff --git a/tests/local/missing-features/gc/gc-types-disabled.wast b/tests/local/missing-features/gc/gc-types-disabled.wast new file mode 100644 index 0000000000..3cd594849b --- /dev/null +++ b/tests/local/missing-features/gc/gc-types-disabled.wast @@ -0,0 +1,32 @@ +(assert_invalid + (module + (type (func (result externref))) + ) + "gc types are disallowed") + +(assert_invalid + (module + (type (func (result (ref any)))) + ) + "gc types are disallowed") + +(module + (table 1 funcref) +) + +(module + (type $t (func)) + (table 1 (ref null $t)) +) + +(assert_invalid + (module + (type (array i8)) + ) + "cannot define array types") + +(assert_invalid + (module + (type (struct)) + ) + "cannot define struct types") diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index bf8350fcae..63917769fd 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -610,9 +610,11 @@ impl TestState { features.remove(WasmFeatures::THREADS); } "missing-features" => { - features = WasmFeatures::empty() | WasmFeatures::FLOATS; + features = + WasmFeatures::empty() | WasmFeatures::FLOATS | WasmFeatures::GC_TYPES; } "floats-disabled.wast" => features.remove(WasmFeatures::FLOATS), + "gc-types-disabled.wast" => features.remove(WasmFeatures::GC_TYPES), "threads" => { features.insert(WasmFeatures::THREADS); features.remove(WasmFeatures::BULK_MEMORY); diff --git a/tests/snapshots/local/missing-features/gc/gc-types-disabled.wast.json b/tests/snapshots/local/missing-features/gc/gc-types-disabled.wast.json new file mode 100644 index 0000000000..3cec0176ce --- /dev/null +++ b/tests/snapshots/local/missing-features/gc/gc-types-disabled.wast.json @@ -0,0 +1,43 @@ +{ + "source_filename": "tests/local/missing-features/gc/gc-types-disabled.wast", + "commands": [ + { + "type": "assert_invalid", + "line": 2, + "filename": "gc-types-disabled.0.wasm", + "text": "gc types are disallowed", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 8, + "filename": "gc-types-disabled.1.wasm", + "text": "gc types are disallowed", + "module_type": "binary" + }, + { + "type": "module", + "line": 13, + "filename": "gc-types-disabled.2.wasm" + }, + { + "type": "module", + "line": 17, + "filename": "gc-types-disabled.3.wasm" + }, + { + "type": "assert_invalid", + "line": 23, + "filename": "gc-types-disabled.4.wasm", + "text": "cannot define array types", + "module_type": "binary" + }, + { + "type": "assert_invalid", + "line": 29, + "filename": "gc-types-disabled.5.wasm", + "text": "cannot define struct types", + "module_type": "binary" + } + ] +} \ No newline at end of file diff --git a/tests/snapshots/local/missing-features/gc/gc-types-disabled.wast/2.print b/tests/snapshots/local/missing-features/gc/gc-types-disabled.wast/2.print new file mode 100644 index 0000000000..7f62195a8d --- /dev/null +++ b/tests/snapshots/local/missing-features/gc/gc-types-disabled.wast/2.print @@ -0,0 +1,3 @@ +(module + (table (;0;) 1 funcref) +) diff --git a/tests/snapshots/local/missing-features/gc/gc-types-disabled.wast/3.print b/tests/snapshots/local/missing-features/gc/gc-types-disabled.wast/3.print new file mode 100644 index 0000000000..7e017d6dd5 --- /dev/null +++ b/tests/snapshots/local/missing-features/gc/gc-types-disabled.wast/3.print @@ -0,0 +1,4 @@ +(module + (type $t (;0;) (func)) + (table (;0;) 1 (ref null $t)) +)