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