diff --git a/crates/wasm-shrink/src/lib.rs b/crates/wasm-shrink/src/lib.rs index bc3b868fbc..c64fc71450 100755 --- a/crates/wasm-shrink/src/lib.rs +++ b/crates/wasm-shrink/src/lib.rs @@ -322,6 +322,9 @@ impl WasmShrink { memory64: true, relaxed_simd: true, extended_const: true, + mutable_global: true, + saturating_float_to_int: true, + sign_extension: true, // We'll never enable this here. deterministic_only: false, diff --git a/crates/wasmparser/benches/benchmark.rs b/crates/wasmparser/benches/benchmark.rs index 84ca783597..ec65cd1828 100644 --- a/crates/wasmparser/benches/benchmark.rs +++ b/crates/wasmparser/benches/benchmark.rs @@ -220,6 +220,9 @@ fn validate_benchmark(c: &mut Criterion) { memory64: true, extended_const: true, deterministic_only: false, + mutable_global: true, + saturating_float_to_int: true, + sign_extension: true, }); return ret; } diff --git a/crates/wasmparser/src/operators_validator.rs b/crates/wasmparser/src/operators_validator.rs index 7b83223bdf..59fd20d5ab 100644 --- a/crates/wasmparser/src/operators_validator.rs +++ b/crates/wasmparser/src/operators_validator.rs @@ -1180,27 +1180,57 @@ impl OperatorValidator { self.push_operand(Type::F64)?; } Operator::I32TruncSatF32S | Operator::I32TruncSatF32U => { + if !self.features.saturating_float_to_int { + return Err(OperatorValidatorError::new( + "saturating float to int conversions support is not enabled", + )); + } self.pop_operand(Some(Type::F32))?; self.push_operand(Type::I32)?; } Operator::I32TruncSatF64S | Operator::I32TruncSatF64U => { + if !self.features.saturating_float_to_int { + return Err(OperatorValidatorError::new( + "saturating float to int conversions support is not enabled", + )); + } self.pop_operand(Some(Type::F64))?; self.push_operand(Type::I32)?; } Operator::I64TruncSatF32S | Operator::I64TruncSatF32U => { + if !self.features.saturating_float_to_int { + return Err(OperatorValidatorError::new( + "saturating float to int conversions support is not enabled", + )); + } self.pop_operand(Some(Type::F32))?; self.push_operand(Type::I64)?; } Operator::I64TruncSatF64S | Operator::I64TruncSatF64U => { + if !self.features.saturating_float_to_int { + return Err(OperatorValidatorError::new( + "saturating float to int conversions support is not enabled", + )); + } self.pop_operand(Some(Type::F64))?; self.push_operand(Type::I64)?; } Operator::I32Extend16S | Operator::I32Extend8S => { + if !self.features.sign_extension { + return Err(OperatorValidatorError::new( + "sign extension operations support is not enabled", + )); + } self.pop_operand(Some(Type::I32))?; self.push_operand(Type::I32)?; } Operator::I64Extend32S | Operator::I64Extend16S | Operator::I64Extend8S => { + if !self.features.sign_extension { + return Err(OperatorValidatorError::new( + "sign extension operations support is not enabled", + )); + } self.pop_operand(Some(Type::I64))?; self.push_operand(Type::I64)?; } diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index d41db1afb4..e0dd8a5ed8 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -157,6 +157,12 @@ struct ModuleState { /// Flags for features that are enabled for validation. #[derive(Hash, Debug, Copy, Clone)] pub struct WasmFeatures { + /// The WebAssembly `mutable-global` proposal (enabled by default) + pub mutable_global: bool, + /// The WebAssembly `nontrapping-float-to-int-conversions` proposal (enabled by default) + pub saturating_float_to_int: bool, + /// The WebAssembly `sign-extension-ops` proposal (enabled by default) + pub sign_extension: bool, /// The WebAssembly reference types proposal (enabled by default) pub reference_types: bool, /// The WebAssembly multi-value proposal (enabled by default) @@ -200,6 +206,9 @@ impl Default for WasmFeatures { deterministic_only: cfg!(feature = "deterministic"), // on-by-default features + mutable_global: true, + saturating_float_to_int: true, + sign_extension: true, bulk_memory: true, multi_value: true, reference_types: true, @@ -819,6 +828,9 @@ impl Validator { (state.tags.len(), MAX_WASM_TAGS, "tags") } ImportSectionEntryType::Global(ty) => { + if !self.features.mutable_global && ty.mutable { + return self.create_error("mutable global support is not enabled"); + } state.globals.push(ty); state.num_imported_globals += 1; (state.globals.len(), MAX_WASM_GLOBALS, "globals") @@ -1325,6 +1337,13 @@ impl Validator { return me.create_error("cannot export types"); } let ty = me.check_external_kind("exported", e.kind, e.index)?; + if !me.features.mutable_global { + if let EntityType::Global(global_type) = ty { + if global_type.mutable { + return me.create_error("mutable global support is not enabled"); + } + } + } let state = me.cur.state.assert_mut(); state .exports diff --git a/fuzz/fuzz_targets/validate.rs b/fuzz/fuzz_targets/validate.rs index a24118fff7..2961881533 100644 --- a/fuzz/fuzz_targets/validate.rs +++ b/fuzz/fuzz_targets/validate.rs @@ -27,6 +27,9 @@ fuzz_target!(|data: &[u8]| { exceptions: (byte2 & 0b0000_0100) != 0, relaxed_simd: (byte2 & 0b0000_1000) != 0, extended_const: (byte2 & 0b0001_0000) != 0, + mutable_global: (byte2 & 0b0010_0000) != 0, + saturating_float_to_int: (byte2 & 0b0100_0000) != 0, + sign_extension: (byte2 & 0b1000_0000) != 0, }); drop(validator.validate_all(&data[2..])); diff --git a/src/bin/wasm-tools/validate.rs b/src/bin/wasm-tools/validate.rs index f1dfde432e..9634a13f79 100644 --- a/src/bin/wasm-tools/validate.rs +++ b/src/bin/wasm-tools/validate.rs @@ -98,6 +98,9 @@ fn parse_features(arg: &str) -> Result { ("memory64", |f| &mut f.memory64), ("extended-const", |f| &mut f.extended_const), ("deterministic", |f| &mut f.deterministic_only), + ("saturating-float-to-int", |f| &mut f.saturating_float_to_int), + ("sign-extension", |f| &mut f.sign_extension), + ("mutable-global", |f| &mut f.mutable_global), ]; for part in arg.split(',').map(|s| s.trim()).filter(|s| !s.is_empty()) { diff --git a/tests/local/missing-features/mutable-global-disabled.wast b/tests/local/missing-features/mutable-global-disabled.wast new file mode 100644 index 0000000000..36d6b8cd0b --- /dev/null +++ b/tests/local/missing-features/mutable-global-disabled.wast @@ -0,0 +1,14 @@ +(assert_invalid + (module + (import "m0" "g0" (global (mut i32))) + ) + "mutable global support is not enabled" +) + +(assert_invalid + (module + (global $g0 (mut i32) (i32.const 0)) + (export "g0" (global $g0)) + ) + "mutable global support is not enabled" +) diff --git a/tests/local/missing-features/saturating-float-to-int-disabled.wast b/tests/local/missing-features/saturating-float-to-int-disabled.wast new file mode 100644 index 0000000000..3b8e8a1ae9 --- /dev/null +++ b/tests/local/missing-features/saturating-float-to-int-disabled.wast @@ -0,0 +1,79 @@ +(assert_invalid + (module + (func (param f32) (result i32) + local.get 0 + i32.trunc_sat_f32_s + ) + ) + "saturating float to int conversions support is not enabled" +) + +(assert_invalid + (module + (func (param f32) (result i32) + local.get 0 + i32.trunc_sat_f32_u + ) + ) + "saturating float to int conversions support is not enabled" +) + +(assert_invalid + (module + (func (param f64) (result i32) + local.get 0 + i32.trunc_sat_f64_s + ) + ) + "saturating float to int conversions support is not enabled" +) + +(assert_invalid + (module + (func (param f64) (result i32) + local.get 0 + i32.trunc_sat_f64_u + ) + ) + "saturating float to int conversions support is not enabled" +) + +(assert_invalid + (module + (func (param f32) (result i64) + local.get 0 + i64.trunc_sat_f32_s + ) + ) + "saturating float to int conversions support is not enabled" +) + +(assert_invalid + (module + (func (param f32) (result i64) + local.get 0 + i64.trunc_sat_f32_u + ) + ) + "saturating float to int conversions support is not enabled" +) + +(assert_invalid + (module + (func (param f64) (result i64) + local.get 0 + i64.trunc_sat_f64_s + ) + ) + "saturating float to int conversions support is not enabled" +) + +(assert_invalid + (module + (func (param f64) (result i64) + local.get 0 + i64.trunc_sat_f64_u + ) + ) + "saturating float to int conversions support is not enabled" +) diff --git a/tests/local/missing-features/sign-extension-disabled.wast b/tests/local/missing-features/sign-extension-disabled.wast new file mode 100644 index 0000000000..36904dc3b8 --- /dev/null +++ b/tests/local/missing-features/sign-extension-disabled.wast @@ -0,0 +1,49 @@ +(assert_invalid + (module + (func (param i32) (result i32) + local.get 0 + i32.extend8_s + ) + ) + "sign extension operations support is not enabled" +) + +(assert_invalid + (module + (func (param i32) (result i32) + local.get 0 + i32.extend16_s + ) + ) + "sign extension operations support is not enabled" +) + +(assert_invalid + (module + (func (param i64) (result i64) + local.get 0 + i64.extend8_s + ) + ) + "sign extension operations support is not enabled" +) + +(assert_invalid + (module + (func (param i64) (result i64) + local.get 0 + i64.extend16_s + ) + ) + "sign extension operations support is not enabled" +) + +(assert_invalid + (module + (func (param i64) (result i64) + local.get 0 + i64.extend32_s + ) + ) + "sign extension operations support is not enabled" +) diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs index d8d8b1efbc..e71e8c5062 100644 --- a/tests/roundtrip.rs +++ b/tests/roundtrip.rs @@ -558,6 +558,9 @@ impl TestState { multi_memory: true, memory64: true, extended_const: true, + saturating_float_to_int: true, + sign_extension: true, + mutable_global: true, }; for part in test.iter().filter_map(|t| t.to_str()) { match part { @@ -567,6 +570,9 @@ impl TestState { features.simd = false; features.reference_types = false; features.multi_value = false; + features.sign_extension = false; + features.saturating_float_to_int = false; + features.mutable_global = false; } "threads" => { features.threads = true;