diff --git a/crates/wasmparser/src/binary_reader.rs b/crates/wasmparser/src/binary_reader.rs index 00a417fafc..92fb1b841c 100644 --- a/crates/wasmparser/src/binary_reader.rs +++ b/crates/wasmparser/src/binary_reader.rs @@ -407,6 +407,47 @@ impl<'a> BinaryReader<'a> { BinaryReaderError::eof(self.original_position(), 1) } + /// Advances the `BinaryReader` up to four bytes to parse a variable + /// length integer as a `u16`. + /// + /// # Errors + /// + /// If `BinaryReader` has less than one or up to two bytes remaining, or + /// the integer is larger than 16 bits. + #[inline] + pub fn read_var_u16(&mut self) -> Result { + // Optimization for single byte u16. + let byte = self.read_u8()?; + if (byte & 0x80) == 0 { + Ok(u16::from(byte)) + } else { + self.read_var_u16_big(byte) + } + } + + fn read_var_u16_big(&mut self, byte: u8) -> Result { + let mut result = (byte & 0x7F) as u16; + let mut shift = 7; + loop { + let byte = self.read_u8()?; + result |= ((byte & 0x7F) as u16) << shift; + if shift >= 9 && (byte >> (16 - shift)) != 0 { + let msg = if byte & 0x80 != 0 { + "invalid var_u16: integer representation too long" + } else { + "invalid var_u16: integer too large" + }; + // The continuation bit or unused bits are set. + return Err(BinaryReaderError::new(msg, self.original_position() - 1)); + } + shift += 7; + if (byte & 0x80) == 0 { + break; + } + } + Ok(result) + } + /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a `u32`. /// @@ -519,6 +560,50 @@ impl<'a> BinaryReader<'a> { Ok(()) } + /// Advances the `BinaryReader` up to four bytes to parse a variable + /// length integer as a `i16`. + /// # Errors + /// If `BinaryReader` has less than one or up to two bytes remaining, or + /// the integer is larger than 16 bits. + #[inline] + pub fn read_var_i16(&mut self) -> Result { + // Optimization for single byte i16. + let byte = self.read_u8()?; + if (byte & 0x80) == 0 { + Ok(((byte as i16) << 9) >> 9) + } else { + self.read_var_i16_big(byte) + } + } + + fn read_var_i16_big(&mut self, byte: u8) -> Result { + let mut result = (byte & 0x7F) as i16; + let mut shift = 7; + loop { + let byte = self.read_u8()?; + result |= ((byte & 0x7F) as i16) << shift; + if shift >= 9 { + let continuation_bit = (byte & 0x80) != 0; + let sign_and_unused_bit = (byte << 1) as i8 >> (16 - shift); + if continuation_bit || (sign_and_unused_bit != 0 && sign_and_unused_bit != -1) { + let msg = if continuation_bit { + "invalid var_i16: integer representation too long" + } else { + "invalid var_i16: integer too large" + }; + return Err(BinaryReaderError::new(msg, self.original_position() - 1)); + } + return Ok(result); + } + shift += 7; + if (byte & 0x80) == 0 { + break; + } + } + let ashift = 16 - shift; + Ok((result << ashift) >> ashift) + } + /// Advances the `BinaryReader` up to four bytes to parse a variable /// length integer as a `i32`. /// # Errors diff --git a/crates/wasmparser/src/parser.rs b/crates/wasmparser/src/parser.rs index 75afed4239..8635e39722 100644 --- a/crates/wasmparser/src/parser.rs +++ b/crates/wasmparser/src/parser.rs @@ -4,10 +4,11 @@ use crate::CoreTypeSectionReader; use crate::{ limits::MAX_WASM_MODULE_SIZE, BinaryReader, BinaryReaderError, ComponentCanonicalSectionReader, ComponentExportSectionReader, ComponentImportSectionReader, ComponentInstanceSectionReader, - ComponentStartFunction, ComponentTypeSectionReader, CustomSectionReader, DataSectionReader, - ElementSectionReader, ExportSectionReader, FromReader, FunctionBody, FunctionSectionReader, - GlobalSectionReader, ImportSectionReader, InstanceSectionReader, MemorySectionReader, Result, - SectionLimited, TableSectionReader, TagSectionReader, TypeSectionReader, + ComponentStartFunction, ComponentTypeSectionReader, ComponentValueSectionReader, + CustomSectionReader, DataSectionReader, ElementSectionReader, ExportSectionReader, FromReader, + FunctionBody, FunctionSectionReader, GlobalSectionReader, ImportSectionReader, + InstanceSectionReader, MemorySectionReader, Result, SectionLimited, TableSectionReader, + TagSectionReader, TypeSectionReader, }; use core::fmt; use core::iter; @@ -275,6 +276,9 @@ pub enum Payload<'a> { /// A component export section was received, and the provided reader can be /// used to parse the contents of the component export section. ComponentExportSection(ComponentExportSectionReader<'a>), + /// A component value section was received, and the provided reader can be + /// used to parse the contents of the component export section. + ComponentValueSection(ComponentValueSectionReader<'a>), /// A module or component custom section was received. CustomSection(CustomSectionReader<'a>), @@ -328,6 +332,7 @@ const COMPONENT_CANONICAL_SECTION: u8 = 8; const COMPONENT_START_SECTION: u8 = 9; const COMPONENT_IMPORT_SECTION: u8 = 10; const COMPONENT_EXPORT_SECTION: u8 = 11; +const COMPONENT_VALUE_SECTION: u8 = 12; impl Parser { /// Creates a new parser. @@ -491,6 +496,7 @@ impl Parser { /// ComponentStartSection { .. } => { /* ... */ } /// ComponentImportSection(_) => { /* ... */ } /// ComponentExportSection(_) => { /* ... */ } + /// ComponentValueSection(_) => { /* ... */ } /// /// ModuleSection { parser, .. } /// | ComponentSection { parser, .. } => { @@ -748,6 +754,12 @@ impl Parser { ComponentExportSectionReader::new, ComponentExportSection, ), + (Encoding::Component, COMPONENT_VALUE_SECTION) => section( + reader, + len, + ComponentValueSectionReader::new, + ComponentValueSection, + ), (_, id) => { let offset = reader.original_position(); let contents = reader.read_bytes(len as usize)?; @@ -875,6 +887,7 @@ impl Parser { /// ComponentStartSection { .. } => { /* ... */ } /// ComponentImportSection(_) => { /* ... */ } /// ComponentExportSection(_) => { /* ... */ } + /// ComponentValueSection(_) => { /* ... */} /// /// CustomSection(_) => { /* ... */ } /// @@ -1128,6 +1141,7 @@ impl Payload<'_> { ComponentStartSection { range, .. } => Some((COMPONENT_START_SECTION, range.clone())), ComponentImportSection(s) => Some((COMPONENT_IMPORT_SECTION, s.range())), ComponentExportSection(s) => Some((COMPONENT_EXPORT_SECTION, s.range())), + ComponentValueSection(s) => Some((COMPONENT_VALUE_SECTION, s.range())), CustomSection(c) => Some((CUSTOM_SECTION, c.range())), @@ -1224,6 +1238,10 @@ impl fmt::Debug for Payload<'_> { .debug_tuple("ComponentExportSection") .field(&"...") .finish(), + ComponentValueSection(_) => f + .debug_tuple("ComponentValueSection") + .field(&"...") + .finish(), CustomSection(c) => f.debug_tuple("CustomSection").field(c).finish(), diff --git a/crates/wasmparser/src/readers/component.rs b/crates/wasmparser/src/readers/component.rs index 24b490d0c3..ccc94dc333 100644 --- a/crates/wasmparser/src/readers/component.rs +++ b/crates/wasmparser/src/readers/component.rs @@ -6,6 +6,7 @@ mod instances; mod names; mod start; mod types; +mod values; pub use self::aliases::*; pub use self::canonicals::*; @@ -15,3 +16,4 @@ pub use self::instances::*; pub use self::names::*; pub use self::start::*; pub use self::types::*; +pub use self::values::*; diff --git a/crates/wasmparser/src/readers/component/values.rs b/crates/wasmparser/src/readers/component/values.rs new file mode 100644 index 0000000000..ce49e5837a --- /dev/null +++ b/crates/wasmparser/src/readers/component/values.rs @@ -0,0 +1,405 @@ +use crate::types::{ComponentDefinedType, ComponentValType, TypesRef}; +use crate::{ + BinaryReader, BinaryReaderError, FromReader, Ieee32, Ieee64, PrimitiveValType, Result, + SectionLimited, +}; + +/// A component value with its type. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ComponentValue<'a> { + /// The type of this value. + pub ty: crate::ComponentValType, + bytes: &'a [u8], + original_offset: usize, +} + +impl<'a> ComponentValue<'a> { + /// Visits a component model value. + /// Expects the types from the component it belongs to. + pub fn val(&self, types: TypesRef, visitor: V) -> Result<()> { + let ty = match self.ty { + crate::ComponentValType::Primitive(prim_ty) => ComponentValType::Primitive(prim_ty), + crate::ComponentValType::Type(idx) => { + ComponentValType::Type(types.component_defined_type_at(idx)) + } + }; + read_val( + &mut BinaryReader::new_with_offset(self.bytes, self.original_offset), + ty, + types, + visitor, + ) + } +} + +/// A primitive value. +#[derive(Debug, Clone, Eq, PartialEq)] +pub enum PrimitiveValue<'a> { + /// A boolean value. + Bool(bool), + /// A signed 8-bit integer. + S8(i8), + /// An unsigned 8-bit integer. + U8(u8), + /// An signed 16-bit integer. + S16(i16), + /// An unsigned 16-bit integer. + U16(u16), + /// A signed 32-bit integer. + S32(i32), + /// An unsigned 32-bit integer. + U32(u32), + /// A signed 64-bit integer. + S64(i64), + /// An unsigned 64-bit integer. + U64(u64), + /// A 32-bit floating point number. + F32(Ieee32), + /// A 64-bit floating point number. + F64(Ieee64), + /// A Unicode scalar value. + Char(char), + /// A Unicode string. + String(&'a str), +} + +/// A value visitor. +pub trait Val: Sized { + /// A record visitor. + type R: Record; + /// A list visitor. + type L: List; + /// A tuple visitor. + type T: Tuple; + /// A flags visitor. + type F: Flags; + /// A primitive value. + fn primitive(self, v: PrimitiveValue); + /// A record. + fn record(self, length: u32) -> Self::R; + /// A variant case with a given value. + fn variant_case(self, label_index: u32, name: &str) -> Self; + /// A variant case without a value. + fn variant_case_empty(self, label_index: u32, name: &str); + /// A list. + fn list(self, length: u32) -> Self::L; + /// A tuple. + fn tuple(self, length: u32) -> Self::T; + /// A flags value. + fn flags(self, length: u32) -> Self::F; + /// An enum case. + fn enum_case(self, label_index: u32, name: &str); + /// A none case of an option. + fn none(self); + /// A some case of an option with a given value. + fn some(self) -> Self; + /// An ok case of a result with a given value. + fn ok(self) -> Self; + /// An ok case of a result without a value. + fn ok_empty(self); + /// An error case of a result with a given value. + fn error(self) -> Self; + /// An error case of a result without a given value. + fn error_empty(self); +} + +/// A visitor for record fields. +pub trait Record: Sized { + /// Visitor for the next record field. + fn field(&mut self, name: &str) -> V; + /// No more fields. + fn end(self); +} + +/// A visitor for list elements. +pub trait List: Sized { + /// Visitor for the next list element. + fn element(&mut self) -> V; + /// No more elements. + fn end(self); +} + +/// A visitor for tuple fields. +pub trait Tuple: Sized { + /// Visitor for the next tuple field. + fn field(&mut self) -> V; + /// No more fields. + fn end(self); +} + +/// A visitor for flags fields. +pub trait Flags: Sized { + /// The next flags field. + fn field(&mut self, name: &str, v: bool); + /// No more fields. + fn end(self); +} + +/// A val visitor intended for validation. +pub struct VacuousVisitor; + +impl Val for VacuousVisitor { + type R = Self; + type T = Self; + type L = Self; + type F = Self; + + fn primitive(self, _v: PrimitiveValue) {} + + fn record(self, _length: u32) -> Self::R { + VacuousVisitor + } + + fn variant_case(self, _label_index: u32, _name: &str) -> Self { + VacuousVisitor + } + + fn variant_case_empty(self, _label_index: u32, _name: &str) {} + + fn list(self, _length: u32) -> Self::L { + VacuousVisitor + } + + fn tuple(self, _length: u32) -> Self::T { + VacuousVisitor + } + + fn flags(self, _length: u32) -> Self::F { + VacuousVisitor + } + + fn enum_case(self, _label_index: u32, _name: &str) {} + + fn none(self) {} + + fn some(self) -> Self { + VacuousVisitor + } + + fn ok(self) -> Self { + VacuousVisitor + } + + fn ok_empty(self) {} + + fn error(self) -> Self { + VacuousVisitor + } + + fn error_empty(self) {} +} + +impl Record for VacuousVisitor { + fn field(&mut self, _name: &str) -> VacuousVisitor { + VacuousVisitor + } + + fn end(self) {} +} + +impl List for VacuousVisitor { + fn element(&mut self) -> VacuousVisitor { + VacuousVisitor + } + + fn end(self) {} +} + +impl Tuple for VacuousVisitor { + fn field(&mut self) -> VacuousVisitor { + VacuousVisitor + } + + fn end(self) {} +} + +impl Flags for VacuousVisitor { + fn field(&mut self, _name: &str, _v: bool) {} + + fn end(self) {} +} + +/// A reader for the value section of a WebAssembly component. +pub type ComponentValueSectionReader<'a> = SectionLimited<'a, ComponentValue<'a>>; + +impl<'a> FromReader<'a> for ComponentValue<'a> { + fn from_reader(reader: &mut BinaryReader<'a>) -> Result { + let ty = crate::ComponentValType::from_reader(reader)?; + let len = reader.read_var_u32()?; + let original_offset = reader.original_position(); + let bytes = reader.read_bytes(len as usize)?; + Ok(ComponentValue { + ty, + bytes, + original_offset, + }) + } +} + +fn read_val( + reader: &mut BinaryReader, + ty: ComponentValType, + types: TypesRef, + visitor: V, +) -> Result<()> { + let ty = get_defined_type(ty, types, reader.original_position())?; + match ty { + ComponentDefinedType::Primitive(prim_ty) => { + visitor.primitive(read_primitive_value(reader, prim_ty)?); + } + ComponentDefinedType::Record(record_ty) => { + let mut record = visitor.record(record_ty.fields.len() as u32); + for (name, field_ty) in record_ty.fields.iter() { + read_val(reader, *field_ty, types, record.field(name))?; + } + record.end(); + } + ComponentDefinedType::Variant(variant_ty) => { + let label = reader.read_var_u32()?; + if let Some((name, case_ty)) = variant_ty.cases.get_index(label as usize) { + if let Some(case_ty) = case_ty.ty { + read_val(reader, case_ty, types, visitor.variant_case(label, name))?; + } else { + visitor.variant_case_empty(label, name); + } + } else { + bail!( + reader.original_position(), + "invalid variant case label: {label}" + ); + } + } + ComponentDefinedType::List(element_ty) => { + let len = reader.read_var_u32()?; + let mut list = visitor.list(len); + for _ in 0..len { + read_val(reader, element_ty, types, list.element())?; + } + list.end(); + } + ComponentDefinedType::Tuple(tuple_ty) => { + let mut tuple = visitor.tuple(tuple_ty.types.len() as u32); + for field_ty in tuple_ty.types.iter() { + read_val(reader, *field_ty, types, tuple.field())?; + } + tuple.end(); + } + ComponentDefinedType::Flags(flags_ty) => { + let data = reader.read_bytes(flags_ty.len().div_ceil(8))?; + let mut flags = visitor.flags(flags_ty.len() as u32); + for (i, name) in flags_ty.iter().enumerate() { + let v = (data[i / 8] >> (i % 8) & 1) == 1; + flags.field(name, v); + } + flags.end(); + } + ComponentDefinedType::Enum(enum_ty) => { + let label = reader.read_var_u32()?; + if let Some(name) = enum_ty.get_index(label as usize) { + visitor.enum_case(label, name); + } else { + bail!( + reader.original_position(), + "invalid enum case label: {label}" + ); + } + } + ComponentDefinedType::Option(option_ty) => match reader.read_u8()? { + 0x0 => { + visitor.none(); + } + 0x1 => { + read_val(reader, option_ty, types, visitor.some())?; + } + x => return reader.invalid_leading_byte(x, "invalid option label"), + }, + ComponentDefinedType::Result { + ok: ok_ty, + err: err_ty, + } => { + let label = reader.read_u8()?; + match label { + 0x0 => { + if let Some(ok_ty) = ok_ty { + read_val(reader, ok_ty, types, visitor.ok())?; + } else { + visitor.ok_empty(); + } + } + 0x1 => { + if let Some(err_ty) = err_ty { + read_val(reader, err_ty, types, visitor.error())?; + } else { + visitor.error_empty(); + } + } + x => return reader.invalid_leading_byte(x, "invalid result label"), + } + } + ComponentDefinedType::Own(_) | ComponentDefinedType::Borrow(_) => { + bail!( + reader.original_position(), + "resource handles not supported in value section" + ) + } + } + Ok(()) +} + +fn read_primitive_value<'a>( + reader: &'a mut BinaryReader, + ty: PrimitiveValType, +) -> Result> { + Ok(match ty { + PrimitiveValType::Bool => PrimitiveValue::Bool(match reader.read_u8()? { + 0x0 => false, + 0x1 => true, + x => return reader.invalid_leading_byte(x, "invalid bool value: {n}"), + }), + PrimitiveValType::S8 => PrimitiveValue::S8(reader.read_u8()? as i8), + PrimitiveValType::U8 => PrimitiveValue::U8(reader.read_u8()?), + PrimitiveValType::S16 => PrimitiveValue::S16(reader.read_var_i16()?), + PrimitiveValType::U16 => PrimitiveValue::U16(reader.read_var_u16()?), + PrimitiveValType::S32 => PrimitiveValue::S32(reader.read_var_i32()?), + PrimitiveValType::U32 => PrimitiveValue::U32(reader.read_var_u32()?), + PrimitiveValType::S64 => PrimitiveValue::S64(reader.read_var_i64()?), + PrimitiveValType::U64 => PrimitiveValue::U64(reader.read_var_u64()?), + PrimitiveValType::F32 => PrimitiveValue::F32({ + let value = reader.read_f32()?; + if f32::from_bits(value.0).is_nan() && value.0 != 0x7f_c0_00_00 { + bail!(reader.original_position(), "invalid f32: non canonical NaN"); + } + value + }), + PrimitiveValType::F64 => PrimitiveValue::F64({ + let value = reader.read_f64()?; + if f64::from_bits(value.0).is_nan() && value.0 != 0x7f_f8_00_00_00_00_00_00 { + bail!(reader.original_position(), "invalid f64: non canonical NaN"); + } + value + }), + PrimitiveValType::Char => { + PrimitiveValue::Char(char::from_u32(reader.read_var_u32()?).ok_or( + BinaryReaderError::new("invalid Unicode scalar value", reader.original_position()), + )?) + } + PrimitiveValType::String => PrimitiveValue::String(reader.read_string()?), + }) +} + +fn get_defined_type( + ty: ComponentValType, + types: TypesRef, + offset: usize, +) -> Result { + Ok(match ty { + ComponentValType::Primitive(prim_ty) => ComponentDefinedType::Primitive(prim_ty), + ComponentValType::Type(id) => { + if let Some(def_ty) = types.get(id) { + def_ty.clone() + } else { + bail!(offset, "invalid type"); + } + } + }) +} diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs index 75e5798032..20d4e81f80 100644 --- a/crates/wasmparser/src/validator.rs +++ b/crates/wasmparser/src/validator.rs @@ -16,7 +16,8 @@ use crate::prelude::*; use crate::{ limits::*, BinaryReaderError, Encoding, FromReader, FunctionBody, HeapType, Parser, Payload, - RefType, Result, SectionLimited, ValType, WASM_COMPONENT_VERSION, WASM_MODULE_VERSION, + RefType, Result, SectionLimited, VacuousVisitor, ValType, WASM_COMPONENT_VERSION, + WASM_MODULE_VERSION, }; use ::core::mem; use ::core::ops::Range; @@ -728,6 +729,7 @@ impl Validator { ComponentStartSection { start, range } => self.component_start_section(start, range)?, ComponentImportSection(s) => self.component_import_section(s)?, ComponentExportSection(s) => self.component_export_section(s)?, + ComponentValueSection(s) => self.component_value_section(s)?, End(offset) => return Ok(ValidPayload::End(self.end(*offset)?)), @@ -1455,6 +1457,45 @@ impl Validator { ) } + /// Validates [`Payload::ComponentValueSection`](crate::Payload). + /// + /// This method should only be called when parsing a component. + pub fn component_value_section( + &mut self, + section: &crate::ComponentValueSectionReader, + ) -> Result<()> { + if !self.features.contains(WasmFeatures::COMPONENT_MODEL_VALUES) { + bail!( + section.range().start, + "support for component model `value`s is not enabled" + ); + } + let validator_id = self.id; + self.process_component_section( + section, + "value", + |components, _types, count, offset| { + let current = components.last_mut().unwrap(); + check_max( + current.values.len(), + count, + MAX_WASM_VALUES, + "values", + offset, + )?; + current.values.reserve(count as usize); + Ok(()) + }, + |components, types, features, value, offset| { + let current = components.last_mut().unwrap(); + let types = TypesRef::from_component(validator_id, types, current); + value.val(types, VacuousVisitor)?; + current.add_value(value, features, offset)?; + Ok(()) + }, + ) + } + /// Validates [`Payload::UnknownSection`](crate::Payload). /// /// Currently always returns an error. diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs index 13f13a497a..1708829ab0 100644 --- a/crates/wasmparser/src/validator/component.rs +++ b/crates/wasmparser/src/validator/component.rs @@ -20,9 +20,9 @@ use crate::{ TupleType, TypeInfo, VariantType, }, BinaryReaderError, CanonicalOption, ComponentExportName, ComponentExternalKind, - ComponentOuterAliasKind, ComponentTypeRef, CompositeType, ExternalKind, FuncType, GlobalType, - InstantiationArgKind, MemoryType, RecGroup, Result, SubType, TableType, TypeBounds, ValType, - WasmFeatures, + ComponentOuterAliasKind, ComponentTypeRef, ComponentValue, CompositeType, ExternalKind, + FuncType, GlobalType, InstantiationArgKind, MemoryType, RecGroup, Result, SubType, TableType, + TypeBounds, ValType, WasmFeatures, }; use core::mem; use indexmap::map::Entry; @@ -1242,6 +1242,23 @@ impl ComponentState { Ok(()) } + pub fn add_value( + &mut self, + value: ComponentValue, + features: &WasmFeatures, + offset: usize, + ) -> Result<()> { + if !features.contains(WasmFeatures::COMPONENT_MODEL_VALUES) { + bail!( + offset, + "support for component model `value`s is not enabled" + ); + } + let ty = self.create_component_val_type(value.ty, offset)?; + self.values.push((ty, false)); + Ok(()) + } + fn check_options( &self, core_ty: Option<&FuncType>,