xref: /wasmtime-44.0.1/crates/wizer/src/parse.rs (revision 0464f6ea)
1 use crate::info::{
2     types_interner::{EntityType, InstanceType, Type, TypeId},
3     Module, ModuleContext,
4 };
5 use crate::stack_ext::StackExt;
6 use anyhow::{Context, Result};
7 use std::convert::TryFrom;
8 use wasm_encoder::SectionId;
9 use wasmparser::{SectionReader, SectionWithLimitedItems};
10 
11 struct StackEntry {
12     parser: wasmparser::Parser,
13     module: Module,
14 }
15 
16 /// Parse the given Wasm bytes into a `ModuleInfo` tree.
17 pub(crate) fn parse<'a>(full_wasm: &'a [u8]) -> anyhow::Result<ModuleContext<'a>> {
18     log::debug!("Parsing the input Wasm");
19 
20     let mut cx = ModuleContext::new();
21 
22     // The wasm we are currently parsing. This is advanced as the parser
23     // consumes input.
24     let mut wasm = full_wasm;
25 
26     let mut stack = vec![StackEntry {
27         parser: wasmparser::Parser::new(0),
28         module: cx.root(),
29     }];
30 
31     loop {
32         let (payload, consumed) = match stack
33             .top_mut()
34             .parser
35             .parse(wasm, true)
36             .context("failed to parse Wasm")?
37         {
38             wasmparser::Chunk::NeedMoreData(_) => unreachable!(),
39             wasmparser::Chunk::Parsed { payload, consumed } => (payload, consumed),
40         };
41         wasm = &wasm[consumed..];
42 
43         use wasmparser::Payload::*;
44         match payload {
45             Version { .. } => {}
46             TypeSection(types) => type_section(&mut cx, &mut stack, full_wasm, types)?,
47             ImportSection(imports) => import_section(&mut cx, &mut stack, full_wasm, imports)?,
48             AliasSection(aliases) => alias_section(&mut cx, &mut stack, full_wasm, aliases)?,
49             InstanceSection(instances) => {
50                 instance_section(&mut cx, &mut stack, full_wasm, instances)?
51             }
52             ModuleSectionStart {
53                 range,
54                 size: _,
55                 count: _,
56             } => {
57                 stack.top_mut().module.add_raw_section(
58                     &mut cx,
59                     SectionId::Module,
60                     range,
61                     full_wasm,
62                 );
63             }
64             ModuleSectionEntry { parser, range: _ } => {
65                 stack.push(StackEntry {
66                     parser,
67                     module: Module::new_defined(&mut cx),
68                 });
69             }
70             FunctionSection(funcs) => function_section(&mut cx, &mut stack, full_wasm, funcs)?,
71             TableSection(tables) => table_section(&mut cx, &mut stack, full_wasm, tables)?,
72             MemorySection(mems) => memory_section(&mut cx, &mut stack, full_wasm, mems)?,
73             GlobalSection(globals) => global_section(&mut cx, &mut stack, full_wasm, globals)?,
74             ExportSection(exports) => export_section(&mut cx, &mut stack, full_wasm, exports)?,
75             StartSection { func: _, range } => {
76                 stack
77                     .top_mut()
78                     .module
79                     .add_raw_section(&mut cx, SectionId::Start, range, full_wasm)
80             }
81             ElementSection(elems) => stack.top_mut().module.add_raw_section(
82                 &mut cx,
83                 SectionId::Element,
84                 elems.range(),
85                 full_wasm,
86             ),
87             DataCountSection { range, .. } => stack.top_mut().module.add_raw_section(
88                 &mut cx,
89                 SectionId::DataCount,
90                 range,
91                 full_wasm,
92             ),
93             DataSection(data) => stack.top_mut().module.add_raw_section(
94                 &mut cx,
95                 SectionId::Data,
96                 data.range(),
97                 full_wasm,
98             ),
99             CustomSection { range, .. } => {
100                 stack
101                     .top_mut()
102                     .module
103                     .add_raw_section(&mut cx, SectionId::Custom, range, full_wasm)
104             }
105             CodeSectionStart {
106                 range,
107                 count: _,
108                 size,
109             } => {
110                 wasm = &wasm[usize::try_from(size).unwrap()..];
111                 let entry = stack.top_mut();
112                 entry.parser.skip_section();
113                 entry
114                     .module
115                     .add_raw_section(&mut cx, SectionId::Code, range, full_wasm)
116             }
117             CodeSectionEntry(_) => unreachable!(),
118             UnknownSection { .. } => anyhow::bail!("unknown section"),
119             EventSection(_) => anyhow::bail!("exceptions are not supported yet"),
120             End => {
121                 let entry = stack.pop().unwrap();
122 
123                 // If we finished parsing the root Wasm module, then we're done.
124                 if entry.module.is_root() {
125                     assert!(stack.is_empty());
126                     return Ok(cx);
127                 }
128 
129                 // Otherwise, we need to add this module to its parent's module
130                 // section.
131                 let parent = stack.top_mut();
132                 parent.module.push_child_module(&mut cx, entry.module);
133             }
134         }
135     }
136 }
137 
138 fn type_section<'a>(
139     cx: &mut ModuleContext<'a>,
140     stack: &mut Vec<StackEntry>,
141     full_wasm: &'a [u8],
142     mut types: wasmparser::TypeSectionReader<'a>,
143 ) -> anyhow::Result<()> {
144     let module = stack.top().module;
145     module.add_raw_section(cx, SectionId::Type, types.range(), full_wasm);
146 
147     // Parse out types, as we will need them later when processing
148     // instance imports.
149     let count = usize::try_from(types.get_count()).unwrap();
150     for _ in 0..count {
151         let ty = types.read()?;
152         match ty {
153             wasmparser::TypeDef::Func(_) | wasmparser::TypeDef::Instance(_) => {
154                 module.push_type(cx, ty);
155             }
156 
157             // We need to disallow module imports, even within nested modules
158             // that only ever have other nested modules supplied as
159             // arguments. Two different modules could be supplied for two
160             // different instantiations of the module-importing module, but then
161             // after we export all modules' globals in our instrumentation
162             // phase, those two different module arguments could become
163             // type-incompatible with each other:
164             //
165             // ```
166             // (module
167             //
168             //   ;; Module A exports and `f` function. Internally it has
169             //   ;; one global.
170             //   (module $A
171             //     (global $g ...)
172             //     (func (export "f") ...))
173             //
174             //   ;; Module B has an identical interface as A. Internally
175             //   ;; it has two globals.
176             //   (module $B
177             //     (global $g ...)
178             //     (global $h ...)
179             //     (func (export "f") ...))
180             //
181             //   ;; Module C imports any module that exports an `f`
182             //   ;; function. It instantiates this imported module.
183             //   (module $C
184             //     (import "env" "module"
185             //       (module (export "f" (func)))
186             //     (instance 0)))
187             //
188             //   ;; C is instantiated with both A and B.
189             //   (instance $C (import "env" "module" $A))
190             //   (instance $C (import "env" "module" $B))
191             // )
192             // ```
193             //
194             // After this instrumentation pass, we need to make module C
195             // transitively export all of the globals from its inner
196             // instances. Which means that the module type used in the module
197             // import needs to specify how many modules are in the imported
198             // module, but in our two instantiations, we have two different
199             // numbers of globals defined in each module! The only way to
200             // resolve this would be to duplicate and specialize module C for
201             // each instantiation, which we don't want to do for complexity and
202             // code size reasons.
203             //
204             // Since module types are only used with importing and exporting
205             // modules, which we don't intend to support as described above, we
206             // can disallow module types to reject all of them in one fell
207             // swoop.
208             wasmparser::TypeDef::Module(_) => Err(anyhow::anyhow!(
209                 "wizer does not support importing or exporting modules"
210             )
211             .context("module types are not supported"))?,
212         }
213     }
214 
215     Ok(())
216 }
217 
218 fn import_section<'a>(
219     cx: &mut ModuleContext<'a>,
220     stack: &mut Vec<StackEntry>,
221     full_wasm: &'a [u8],
222     mut imports: wasmparser::ImportSectionReader<'a>,
223 ) -> anyhow::Result<()> {
224     let module = stack.top().module;
225     stack
226         .top_mut()
227         .module
228         .add_raw_section(cx, SectionId::Import, imports.range(), full_wasm);
229 
230     let mut instance_import_count = 0;
231 
232     // Two-level imports implicitly create an instance import. That is, this
233     //
234     //    (import "env" "f" (func))
235     //    (import "env" "g" (func))
236     //
237     // is implicitly translated into roughly
238     //
239     //    (import "env" (instance (export "f" (func))
240     //                            (export "g" (func))))
241     //    (alias 0 "f")
242     //    (alias 0 "g")
243     //
244     // However not that this is _not_ a WAT-level desugaring where we only have
245     // to deal with the expanded form! We have to perform this translation
246     // ourselves as we parse the imports.
247     //
248     // This variable keeps track of the implicit instance import that we are
249     // currently building. Whenever we see a consecutive run of two-level
250     // imports for the same module, we coalesce them into an implicit instance
251     // import.
252     let mut implicit_instance_import: Option<(&str, InstanceType)> = None;
253 
254     // Check that we can properly handle all imports.
255     let count = imports.get_count();
256     for _ in 0..count {
257         let imp = imports.read()?;
258 
259         if imp.module.starts_with("__wizer_")
260             || imp.field.map_or(false, |f| f.starts_with("__wizer_"))
261         {
262             anyhow::bail!(
263                 "input Wasm module already imports entities named with the `__wizer_*` prefix"
264             );
265         }
266 
267         match (implicit_instance_import.as_mut(), imp.field) {
268             (Some((implicit_module, instance_ty)), Some(field))
269                 if *implicit_module == imp.module =>
270             {
271                 let ty = module.entity_type(cx, imp.ty);
272                 let old = instance_ty.exports.insert(field.into(), ty);
273                 debug_assert!(old.is_none(), "checked by validation");
274             }
275             _ => {
276                 if let Some((_, instance_ty)) = implicit_instance_import.take() {
277                     module.push_implicit_instance(cx, instance_ty);
278                     instance_import_count += 1;
279                 }
280                 if let Some(field) = imp.field {
281                     let field_ty = module.entity_type(cx, imp.ty);
282                     let instance_ty = InstanceType {
283                         exports: Some((field.into(), field_ty)).into_iter().collect(),
284                     };
285                     implicit_instance_import = Some((imp.module, instance_ty));
286                 }
287             }
288         }
289 
290         check_import_type(
291             cx,
292             stack.top().module.types(cx),
293             stack.top().module.is_root(),
294             &module.entity_type(cx, imp.ty),
295         )?;
296         if let wasmparser::ImportSectionEntryType::Instance(_) = imp.ty {
297             instance_import_count += 1;
298         }
299         module.push_import(cx, imp);
300     }
301 
302     if let Some((_, instance_ty)) = implicit_instance_import.take() {
303         module.push_implicit_instance(cx, instance_ty);
304         instance_import_count += 1;
305     }
306 
307     module.push_instance_import_count(cx, instance_import_count);
308     Ok(())
309 }
310 
311 fn check_import_type(
312     cx: &ModuleContext,
313     types: &[TypeId],
314     is_root: bool,
315     ty: &EntityType,
316 ) -> Result<()> {
317     match ty {
318         EntityType::Function(_) => Ok(()),
319         EntityType::Instance(inst_ty) => {
320             // We allow importing instances that only export things that are
321             // acceptable imports. This is equivalent to a two-layer import.
322             match cx.types().get(*inst_ty) {
323                 Type::Instance(inst_ty) => {
324                     for ty in inst_ty.exports.values() {
325                         check_import_type(cx, types, is_root, ty)?;
326                     }
327                     Ok(())
328                 }
329                 _ => unreachable!(),
330             }
331         }
332         EntityType::Memory(mem_ty) => match mem_ty {
333             wasmparser::MemoryType::M32 { limits: _, shared } => {
334                 anyhow::ensure!(!shared, "shared memories are not supported by Wizer yet");
335                 anyhow::ensure!(
336                     !is_root,
337                     "memory imports are not allowed in the root Wasm module"
338                 );
339                 Ok(())
340             }
341             wasmparser::MemoryType::M64 { .. } => {
342                 anyhow::bail!("the memory64 proposal is not supported by Wizer yet")
343             }
344         },
345         EntityType::Table(_) | EntityType::Global(_) => {
346             anyhow::ensure!(
347                 !is_root,
348                 "table and global imports are not allowed in the root Wasm module"
349             );
350             Ok(())
351         }
352         EntityType::Module(_) => {
353             unreachable!();
354         }
355     }
356 }
357 
358 fn alias_section<'a>(
359     cx: &mut ModuleContext<'a>,
360     stack: &mut Vec<StackEntry>,
361     full_wasm: &'a [u8],
362     mut aliases: wasmparser::AliasSectionReader<'a>,
363 ) -> anyhow::Result<()> {
364     let module = stack.top().module;
365     module.add_raw_section(cx, SectionId::Alias, aliases.range(), full_wasm);
366 
367     // Clone any aliases over into this module's index spaces.
368     for _ in 0..aliases.get_count() {
369         let alias = aliases.read()?;
370         match &alias {
371             wasmparser::Alias::OuterType {
372                 relative_depth,
373                 index,
374             } => {
375                 let relative_depth = usize::try_from(*relative_depth).unwrap();
376                 // NB: `- 2` rather than `- 1` because
377                 // `relative_depth=0` means this module's immediate
378                 // parent, not this module itself.
379                 let ty = stack[stack.len() - 2 - relative_depth]
380                     .module
381                     .type_id_at(cx, *index);
382                 module.push_aliased_type(cx, ty);
383             }
384             wasmparser::Alias::OuterModule {
385                 relative_depth,
386                 index,
387             } => {
388                 let relative_depth = usize::try_from(*relative_depth).unwrap();
389                 // Ditto regarding `- 2`.
390                 let alias_of = stack[stack.len() - 2 - relative_depth]
391                     .module
392                     .child_module_at(cx, *index);
393                 let aliased = Module::new_aliased(cx, alias_of);
394                 module.push_child_module(cx, aliased);
395             }
396             wasmparser::Alias::InstanceExport {
397                 instance,
398                 kind,
399                 export,
400             } => match kind {
401                 wasmparser::ExternalKind::Module => {
402                     anyhow::bail!("exported modules are not supported yet")
403                 }
404                 wasmparser::ExternalKind::Instance => {
405                     let inst_ty = match module.instance_export(cx, *instance, export) {
406                         Some(EntityType::Instance(i)) => *i,
407                         _ => unreachable!(),
408                     };
409                     module.push_aliased_instance(cx, inst_ty);
410                 }
411                 wasmparser::ExternalKind::Function => {
412                     let func_ty = match module.instance_export(cx, *instance, export) {
413                         Some(EntityType::Function(ty)) => *ty,
414                         _ => unreachable!(),
415                     };
416                     module.push_function(cx, func_ty);
417                 }
418                 wasmparser::ExternalKind::Table => {
419                     let table_ty = match module.instance_export(cx, *instance, export) {
420                         Some(EntityType::Table(ty)) => *ty,
421                         _ => unreachable!(),
422                     };
423                     module.push_table(cx, table_ty);
424                 }
425                 wasmparser::ExternalKind::Memory => {
426                     let ty = match module.instance_export(cx, *instance, export) {
427                         Some(EntityType::Memory(ty)) => *ty,
428                         _ => unreachable!(),
429                     };
430                     module.push_imported_memory(cx, ty);
431                 }
432                 wasmparser::ExternalKind::Global => {
433                     let ty = match module.instance_export(cx, *instance, export) {
434                         Some(EntityType::Global(ty)) => *ty,
435                         _ => unreachable!(),
436                     };
437                     module.push_imported_global(cx, ty);
438                 }
439                 wasmparser::ExternalKind::Event => {
440                     unreachable!("validation should reject the exceptions proposal")
441                 }
442                 wasmparser::ExternalKind::Type => unreachable!("can't export types"),
443             },
444         }
445         module.push_alias(cx, alias);
446     }
447 
448     Ok(())
449 }
450 
451 fn instance_section<'a>(
452     cx: &mut ModuleContext<'a>,
453     stack: &mut Vec<StackEntry>,
454     full_wasm: &'a [u8],
455     mut instances: wasmparser::InstanceSectionReader<'a>,
456 ) -> anyhow::Result<()> {
457     let module = stack.top().module;
458     module.add_raw_section(cx, SectionId::Instance, instances.range(), full_wasm);
459 
460     // Record the instantiations made in this module, and which modules were
461     // instantiated.
462     for _ in 0..instances.get_count() {
463         let inst = instances.read()?;
464         let module_index = inst.module();
465         let child_module = module.child_module_at(cx, module_index);
466         let inst_ty = child_module.define_instance_type(cx);
467 
468         let mut instance_args_reader = inst.args()?;
469         let instance_args_count = usize::try_from(instance_args_reader.get_count()).unwrap();
470         let mut instance_args = Vec::with_capacity(instance_args_count);
471         for _ in 0..instance_args_count {
472             instance_args.push(instance_args_reader.read()?);
473         }
474 
475         module.push_defined_instance(cx, inst_ty, child_module, instance_args);
476     }
477 
478     Ok(())
479 }
480 
481 fn function_section<'a>(
482     cx: &mut ModuleContext<'a>,
483     stack: &mut Vec<StackEntry>,
484     full_wasm: &'a [u8],
485     mut funcs: wasmparser::FunctionSectionReader<'a>,
486 ) -> anyhow::Result<()> {
487     let module = stack.top().module;
488     module.add_raw_section(cx, SectionId::Function, funcs.range(), full_wasm);
489 
490     let count = usize::try_from(funcs.get_count()).unwrap();
491     for _ in 0..count {
492         let ty_idx = funcs.read()?;
493         let ty = module.type_id_at(cx, ty_idx);
494         module.push_function(cx, ty);
495     }
496     Ok(())
497 }
498 
499 fn table_section<'a>(
500     cx: &mut ModuleContext<'a>,
501     stack: &mut Vec<StackEntry>,
502     full_wasm: &'a [u8],
503     mut tables: wasmparser::TableSectionReader<'a>,
504 ) -> anyhow::Result<()> {
505     let module = stack.top().module;
506     module.add_raw_section(cx, SectionId::Table, tables.range(), full_wasm);
507 
508     let count = usize::try_from(tables.get_count()).unwrap();
509     for _ in 0..count {
510         module.push_table(cx, tables.read()?);
511     }
512     Ok(())
513 }
514 
515 fn memory_section<'a>(
516     cx: &mut ModuleContext<'a>,
517     stack: &mut Vec<StackEntry>,
518     full_wasm: &'a [u8],
519     mut mems: wasmparser::MemorySectionReader<'a>,
520 ) -> anyhow::Result<()> {
521     let module = stack.top().module;
522     module.add_raw_section(cx, SectionId::Memory, mems.range(), full_wasm);
523 
524     let count = usize::try_from(mems.get_count()).unwrap();
525     for _ in 0..count {
526         let m = mems.read()?;
527         module.push_defined_memory(cx, m);
528     }
529     Ok(())
530 }
531 
532 fn global_section<'a>(
533     cx: &mut ModuleContext<'a>,
534     stack: &mut Vec<StackEntry>,
535     full_wasm: &'a [u8],
536     mut globals: wasmparser::GlobalSectionReader<'a>,
537 ) -> anyhow::Result<()> {
538     let module = stack.top().module;
539     module.add_raw_section(cx, SectionId::Global, globals.range(), full_wasm);
540 
541     let count = usize::try_from(globals.get_count()).unwrap();
542     for _ in 0..count {
543         let g = globals.read()?;
544         module.push_defined_global(cx, g.ty);
545     }
546     Ok(())
547 }
548 
549 fn export_section<'a>(
550     cx: &mut ModuleContext<'a>,
551     stack: &mut Vec<StackEntry>,
552     full_wasm: &'a [u8],
553     mut exports: wasmparser::ExportSectionReader<'a>,
554 ) -> anyhow::Result<()> {
555     let module = stack.top().module;
556     module.add_raw_section(cx, SectionId::Export, exports.range(), full_wasm);
557 
558     for _ in 0..exports.get_count() {
559         let export = exports.read()?;
560 
561         if export.field.starts_with("__wizer_") {
562             anyhow::bail!(
563                 "input Wasm module already exports entities named with the `__wizer_*` prefix"
564             );
565         }
566 
567         match export.kind {
568             wasmparser::ExternalKind::Module => {
569                 anyhow::bail!("Wizer does not support importing and exporting modules")
570             }
571             wasmparser::ExternalKind::Type | wasmparser::ExternalKind::Event => {
572                 unreachable!("checked in validation")
573             }
574             wasmparser::ExternalKind::Function
575             | wasmparser::ExternalKind::Table
576             | wasmparser::ExternalKind::Memory
577             | wasmparser::ExternalKind::Global
578             | wasmparser::ExternalKind::Instance => {
579                 module.push_export(cx, export);
580             }
581         }
582     }
583     Ok(())
584 }
585