diff --git a/crates/wasm-encoder/src/aliases.rs b/crates/wasm-encoder/src/aliases.rs index 4ccb8b375c..f729c5118d 100644 --- a/crates/wasm-encoder/src/aliases.rs +++ b/crates/wasm-encoder/src/aliases.rs @@ -13,7 +13,7 @@ use super::*; /// use wasm_encoder::{Module, AliasSection, ItemKind}; /// /// let mut aliases = AliasSection::new(); -/// aliases.parent_type(2); +/// aliases.outer_type(0, 2); /// aliases.instance_export(0, ItemKind::Function, "foo"); /// /// let mut module = Module::new(); @@ -50,18 +50,20 @@ impl AliasSection { self } - /// Define an alias that references a parent's type. - pub fn parent_type(&mut self, ty: u32) -> &mut Self { + /// Define an alias that references an outer module's type. + pub fn outer_type(&mut self, depth: u32, ty: u32) -> &mut Self { self.bytes.push(0x01); + self.bytes.extend(encoders::u32(depth)); self.bytes.push(0x07); self.bytes.extend(encoders::u32(ty)); self.num_added += 1; self } - /// Define an alias that references a parent's module. - pub fn parent_module(&mut self, module: u32) -> &mut Self { + /// Define an alias that references an outer module's module. + pub fn outer_module(&mut self, depth: u32, module: u32) -> &mut Self { self.bytes.push(0x01); + self.bytes.extend(encoders::u32(depth)); self.bytes.push(ItemKind::Module as u8); self.bytes.extend(encoders::u32(module)); self.num_added += 1; diff --git a/crates/wasm-encoder/src/instances.rs b/crates/wasm-encoder/src/instances.rs index 12725bd8e4..af9bc63a7a 100644 --- a/crates/wasm-encoder/src/instances.rs +++ b/crates/wasm-encoder/src/instances.rs @@ -14,9 +14,9 @@ use super::*; /// /// let mut instances = InstanceSection::new(); /// instances.instantiate(0, vec![ -/// ("x", None, Export::Function(0)), -/// ("", Some("y"), Export::Module(2)), -/// ("foo", None, Export::Global(0)), +/// ("x", Export::Function(0)), +/// ("", Export::Module(2)), +/// ("foo", Export::Global(0)), /// ]); /// /// let mut module = Module::new(); @@ -42,7 +42,7 @@ impl InstanceSection { /// arguments to the instantiation. pub fn instantiate<'a, I>(&mut self, module: u32, args: I) -> &mut Self where - I: IntoIterator, Export)>, + I: IntoIterator, I::IntoIter: ExactSizeIterator, { let args = args.into_iter(); @@ -51,17 +51,8 @@ impl InstanceSection { self.bytes.extend(encoders::u32(module)); self.bytes .extend(encoders::u32(u32::try_from(args.len()).unwrap())); - for (name, field, export) in args { + for (name, export) in args { self.bytes.extend(encoders::str(name)); - match field { - Some(field) => { - self.bytes.push(0x01); - self.bytes.extend(encoders::str(field)); - } - None => { - self.bytes.push(0x00); - } - } export.encode(&mut self.bytes); } self.num_added += 1; diff --git a/crates/wasm-smith/src/encode.rs b/crates/wasm-smith/src/encode.rs index b5ddc7b8d1..633d3c540b 100644 --- a/crates/wasm-smith/src/encode.rs +++ b/crates/wasm-smith/src/encode.rs @@ -103,11 +103,11 @@ where } => { section.instance_export(*instance, translate_item_kind(kind), name); } - Alias::ParentType(ty) => { - section.parent_type(*ty); + Alias::OuterType { depth, index } => { + section.outer_type(*depth, *index); } - Alias::ParentModule(m) => { - section.parent_module(*m); + Alias::OuterModule { depth, index } => { + section.outer_module(*depth, *index); } } } @@ -119,9 +119,10 @@ where for instance in list { section.instantiate( instance.module, - instance.args.iter().map(|(name, field, export)| { - (name.as_str(), field.as_deref(), translate_export(export)) - }), + instance + .args + .iter() + .map(|(name, export)| (name.as_str(), translate_export(export))), ); } module.section(§ion); diff --git a/crates/wasm-smith/src/lib.rs b/crates/wasm-smith/src/lib.rs index a7d40167da..39f20c5da9 100644 --- a/crates/wasm-smith/src/lib.rs +++ b/crates/wasm-smith/src/lib.rs @@ -94,9 +94,11 @@ where C: Config, { config: C, - depth: usize, valtypes: Vec, + /// Outer modules, if any (used for module linking) + outers: Vec, + /// The initial sections of this wasm module, including types and imports. /// This is stored as a list-of-lists where each `InitialSection` represents /// a whole section, so this `initial_sections` list represents a list of @@ -119,15 +121,15 @@ where /// second-level import names that have been generated so far. import_names: HashMap>>, - /// Indices into `initializers` which are types. The pair `(i, j)` means - /// that `initializers[i]` is a type section, and we are referring to the - /// `j`th type within that type section. - types: Vec<(usize, usize)>, + /// Where within the `instances` array each implicit instance's type is + /// defined. + implicit_instance_types: HashMap, + + /// All types locally defined in this module (available in the type index + /// space). + types: Vec, /// Indices within `types` that are function types. func_types: Vec, - /// Map from function signature to indices that have that function - /// signature. - func_map: HashMap, Vec>, /// Indices within `types` that are module types. module_types: Vec, /// Indices within `types` that are instance types. @@ -252,7 +254,7 @@ struct FuncType { results: Vec, } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] struct InstanceType { exports: indexmap::IndexMap, } @@ -260,6 +262,7 @@ struct InstanceType { #[derive(Clone, Debug)] struct ModuleType { imports: Vec<(String, Option, EntityType)>, + import_types: indexmap::IndexMap, /// The list of exports can be found in the `InstanceType` indirection here, /// and this struct layout is used to ease the instantiation process where /// we record an instance's signature. @@ -332,16 +335,20 @@ enum Alias { kind: ItemKind, name: String, }, - #[allow(dead_code)] // TODO: not constructed yet - ParentType(u32), - #[allow(dead_code)] // TODO: not constructed yet - ParentModule(u32), + OuterType { + depth: u32, + index: u32, + }, + OuterModule { + depth: u32, + index: u32, + }, } #[derive(Clone, Debug)] struct Instance { module: u32, - args: Vec<(String, Option, Export)>, + args: Vec<(String, Export)>, } #[derive(Copy, Clone, Debug)] @@ -712,7 +719,7 @@ where choices.push(|u, m, _, _| m.arbitrary_imports(0, u)); } if self.modules.len() < self.config.max_modules() - && self.depth < self.config.max_nesting_depth() + && self.outers.len() < self.config.max_nesting_depth() { choices.push(|u, m, _, _| m.arbitrary_modules(u)); } @@ -752,23 +759,15 @@ where self.initial_sections.push(InitialSection::Type(Vec::new())); arbitrary_loop(u, min, self.config.max_types() - self.types.len(), |u| { let ty = self.arbitrary_type(u)?; - let list = match &ty { - Type::Func(f) => { - self.func_map - .entry(f.clone()) - .or_insert(Vec::new()) - .push(self.types.len() as u32); - &mut self.func_types - } - Type::Module(_) => &mut self.module_types, - Type::Instance(_) => &mut self.instance_types, - }; - list.push(self.types.len() as u32); + self.record_type(&ty); let types = match self.initial_sections.last_mut().unwrap() { InitialSection::Type(list) => list, _ => unreachable!(), }; - self.types.push((section_idx, types.len())); + self.types.push(LocalType::Defined { + section: section_idx, + nth: types.len(), + }); types.push(ty); Ok(true) })?; @@ -782,6 +781,15 @@ where Ok(()) } + fn record_type(&mut self, ty: &Type) { + let list = match &ty { + Type::Func(_) => &mut self.func_types, + Type::Module(_) => &mut self.module_types, + Type::Instance(_) => &mut self.instance_types, + }; + list.push(self.types.len() as u32); + } + fn arbitrary_type(&mut self, u: &mut Unstructured) -> Result { if !self.config.module_linking_enabled() { return Ok(Type::Func(self.arbitrary_func_type(u)?)); @@ -814,16 +822,33 @@ where ) -> Result> { let exports = self.arbitrary_instance_type(u, entities)?; let mut imports = Vec::new(); + let mut import_types = indexmap::IndexMap::new(); let mut names = HashMap::new(); if !entities.max_reached(&self.config) { arbitrary_loop(u, 0, self.config.max_imports(), |u| { let (module, name) = unique_import_strings(1_000, &mut names, true, u)?; let ty = self.arbitrary_entity_type(u, entities)?; + if let Some(name) = &name { + let ity = import_types.entry(module.clone()).or_insert_with(|| { + EntityType::Instance(u32::max_value(), Default::default()) + }); + let ity = match ity { + EntityType::Instance(_, ty) => Rc::get_mut(ty).unwrap(), + _ => unreachable!(), + }; + ity.exports.insert(name.clone(), ty.clone()); + } else { + import_types.insert(module.clone(), ty.clone()); + } imports.push((module, name, ty)); Ok(!entities.max_reached(&self.config)) })?; } - Ok(Rc::new(ModuleType { imports, exports })) + Ok(Rc::new(ModuleType { + imports, + import_types, + exports, + })) } fn arbitrary_instance_type( @@ -983,15 +1008,37 @@ where return Ok(false); } - let (module, name) = unique_import_strings( - 1_000, - &mut self.import_names, - self.config.module_linking_enabled() || self.depth > 0, - u, - )?; + // Generate an arbitrary module/name pair to name this import. Note + // that if module-linking is enabled and `name` is present, then we + // might be implicitly generating an instance. If that's the case + // then we need to record the type of this instance. + let module_linking = self.config.module_linking_enabled() || self.outers.len() > 0; + let (module, name) = + unique_import_strings(1_000, &mut self.import_names, module_linking, u)?; + if module_linking + && name.is_some() + && self.import_names[&module].as_ref().unwrap().len() == 1 + { + // This is the first time this module name is imported from, so + // generate a new instance type. + self.implicit_instance_types + .insert(module.clone(), self.instances.len()); + self.instances.push(Rc::new(InstanceType::default())); + } let f = u.choose(&choices)?; let ty = f(u, self)?; + if let Some(name) = &name { + if module_linking { + let idx = self.implicit_instance_types[&module]; + let instance_ty = &mut self.instances[idx]; + Rc::get_mut(instance_ty) + .expect("shouldn't be aliased yet") + .exports + .insert(name.clone(), ty.clone()); + } + } + self.num_imports += 1; imports.push((module, name, ty)); Ok(true) @@ -1073,8 +1120,15 @@ where } } } - Alias::ParentType(_) => unimplemented!(), - Alias::ParentModule(_) => unimplemented!(), + Alias::OuterType { depth, index } => { + let ty = self.outers[*depth as usize].types[*index as usize].clone(); + self.record_type(&ty); + self.types.push(LocalType::Aliased(ty)); + } + Alias::OuterModule { depth, index } => { + let ty = self.outers[*depth as usize].modules[*index as usize].clone(); + self.modules.push(ty); + } } available.update(self); self.num_aliases += 1; @@ -1105,9 +1159,8 @@ where args: choice .args .iter() - .map(|(name, field, candidates)| { - u.choose(candidates) - .map(|e| (name.clone(), field.clone(), e.clone())) + .map(|(name, candidates)| { + u.choose(candidates).map(|e| (name.clone(), e.clone())) }) .collect::>>()?, }); @@ -1128,14 +1181,22 @@ where let mut modules = Vec::new(); arbitrary_loop(u, 0, self.config.max_modules(), |u| { let mut module = ConfiguredModule::::default(); - module.depth = self.depth + 1; + module.outers = self.outers.clone(); + let parent = Outer { + types: (0..self.types.len()) + .map(|i| self.ty(i as u32).clone()) + .collect(), + modules: self.modules.clone(), + }; + module.outers.insert(0, parent); module.config = self.config.clone(); module.build(u, false)?; // After we've generated the `module`, we create `ty` which is its // own type signature of itself. let mut imports = Vec::with_capacity(module.num_imports); - for (module, name, ty) in module + let mut import_types = indexmap::IndexMap::with_capacity(module.num_imports); + for (name, field, ty) in module .initial_sections .iter() .filter_map(|section| match section { @@ -1144,7 +1205,17 @@ where }) .flat_map(|a| a) { - imports.push((module.clone(), name.clone(), ty.clone())); + if field.is_none() { + // If the field is none then `ty` matches the import type + // exactly. + import_types.insert(name.clone(), ty.clone()); + } else if import_types.get(name).is_none() { + // Otherwise if we haven't already recorded the implicit + // type of `name` then we do so here. + let ty = module.instances[module.implicit_instance_types[name]].clone(); + import_types.insert(name.clone(), EntityType::Instance(u32::max_value(), ty)); + } + imports.push((name.clone(), field.clone(), ty.clone())); } let mut exports = indexmap::IndexMap::with_capacity(module.exports.len()); for (name, export) in module.exports.iter() { @@ -1153,6 +1224,7 @@ where } let ty = Rc::new(ModuleType { imports, + import_types, exports: Rc::new(InstanceType { exports }), }); @@ -1188,6 +1260,18 @@ where } } + fn ty(&self, idx: u32) -> &Type { + match &self.types[idx as usize] { + LocalType::Defined { section, nth } => { + if let InitialSection::Type(list) = &self.initial_sections[*section] { + return &list[*nth]; + } + panic!("looked up a type with the wrong index") + } + LocalType::Aliased(ty) => ty, + } + } + fn func_types<'a>(&'a self) -> impl Iterator + 'a { self.func_types .iter() @@ -1196,31 +1280,22 @@ where } fn func_type(&self, idx: u32) -> &Rc { - let (i, j) = self.types[idx as usize]; - if let InitialSection::Type(list) = &self.initial_sections[i] { - if let Type::Func(f) = &list[j] { - return f; - } + if let Type::Func(f) = self.ty(idx) { + return f; } panic!("looked up a function type with the wrong index") } fn instance_type(&self, idx: u32) -> &Rc { - let (i, j) = self.types[idx as usize]; - if let InitialSection::Type(list) = &self.initial_sections[i] { - if let Type::Instance(f) = &list[j] { - return f; - } + if let Type::Instance(f) = self.ty(idx) { + return f; } panic!("looked up an instance type with the wrong index") } fn module_type(&self, idx: u32) -> &Rc { - let (i, j) = self.types[idx as usize]; - if let InitialSection::Type(list) = &self.initial_sections[i] { - if let Type::Module(f) = &list[j] { - return f; - } + if let Type::Module(f) = self.ty(idx) { + return f; } panic!("looked up an instance type with the wrong index") } @@ -1396,6 +1471,20 @@ where }); } + if self.instances.len() > 0 { + choices.push(|u, m| { + let idx = u.int_in_range(0..=m.instances.len() - 1)?; + Ok(Export::Instance(idx as u32)) + }); + } + + if self.modules.len() > 0 { + choices.push(|u, m| { + let idx = u.int_in_range(0..=m.modules.len() - 1)?; + Ok(Export::Module(idx as u32)) + }); + } + if choices.is_empty() { return Ok(()); } @@ -1912,6 +2001,7 @@ fn arbitrary_vec_u8(u: &mut Unstructured) -> Result> { struct AvailableAliases { aliases: Vec, instances_added: usize, + outers_processed: bool, } impl AvailableAliases { @@ -1973,6 +2063,26 @@ impl AvailableAliases { } } + // Then add in our all parent's alias candidates, if there are any + // outers. + if !self.outers_processed { + for (i, parent) in module.outers.iter().enumerate() { + for j in 0..parent.types.len() { + self.aliases.push(Alias::OuterType { + depth: i as u32, + index: j as u32, + }); + } + for j in 0..parent.modules.len() { + self.aliases.push(Alias::OuterModule { + depth: i as u32, + index: j as u32, + }); + } + } + self.outers_processed = true; + } + // And afterwards we need to discard alias candidates that create items // which, if created, would exceed our maximum limits. self.aliases.retain(|alias| match alias { @@ -2000,8 +2110,8 @@ impl AvailableAliases { kind: ItemKind::Module, .. } => module.modules.len() < module.config.max_modules(), - Alias::ParentType(_) => module.types.len() < module.config.max_types(), - Alias::ParentModule(_) => module.modules.len() < module.config.max_modules(), + Alias::OuterType { .. } => module.types.len() < module.config.max_types(), + Alias::OuterModule { .. } => module.modules.len() < module.config.max_modules(), }); } } @@ -2047,7 +2157,7 @@ struct Instantiation { /// (global $g2 (mut i32) (i32.const 42)) /// ) /// ``` - args: Vec<(String, Option, Vec)>, + args: Vec<(String, Vec)>, } impl AvailableInstantiations { @@ -2055,7 +2165,7 @@ impl AvailableInstantiations { self.choices.clear(); 'outer: for (i, ty) in module.modules.iter().enumerate() { let mut args = Vec::new(); - for (name, field, import) in ty.imports.iter() { + for (name, import) in ty.import_types.iter() { let candidates = module.subtypes(import); // If nothing in our module up to this point can satisfy this // import then we can't instantiate this module. That means we @@ -2063,7 +2173,7 @@ impl AvailableInstantiations { if candidates.is_empty() { continue 'outer; } - args.push((name.clone(), field.clone(), candidates)); + args.push((name.clone(), candidates)); } self.choices.push(Instantiation { @@ -2096,3 +2206,26 @@ impl Entities { || self.instances >= config.max_instances() } } + +#[derive(Clone, Debug)] +enum LocalType { + /// A type that's locally defined in a module via a type section. + Defined { + /// The section (index within `ConfiguredModule::initializers` that this + /// type is defined. + section: usize, + /// Which element within the section definition this type corresponds + /// to. + nth: usize, + }, + + /// A type that's aliased from another outer module to be defined in a + /// module. The type's definition is copied inline here. + Aliased(Type), +} + +#[derive(Debug, Clone)] +struct Outer { + types: Vec, + modules: Vec>, +} diff --git a/fuzz/fuzz_targets/validate-valid-module.rs b/fuzz/fuzz_targets/validate-valid-module.rs index f1c6292e26..1767821937 100755 --- a/fuzz/fuzz_targets/validate-valid-module.rs +++ b/fuzz/fuzz_targets/validate-valid-module.rs @@ -21,7 +21,10 @@ fuzz_target!(|m: ConfiguredModule| { ..wasmparser::WasmFeatures::default() }); if let Err(e) = validator.validate_all(&bytes) { - std::fs::write("test.wasm", bytes).unwrap(); + std::fs::write("test.wasm", &bytes).unwrap(); + if let Ok(wat) = wasmprinter::print_bytes(&bytes) { + std::fs::write("test.wat", wat).unwrap(); + } panic!("Invalid module: {}", e); } });