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