1 | /* Copyright 2018 Mozilla Foundation |
2 | * |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); |
4 | * you may not use this file except in compliance with the License. |
5 | * You may obtain a copy of the License at |
6 | * |
7 | * http://www.apache.org/licenses/LICENSE-2.0 |
8 | * |
9 | * Unless required by applicable law or agreed to in writing, software |
10 | * distributed under the License is distributed on an "AS IS" BASIS, |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | * See the License for the specific language governing permissions and |
13 | * limitations under the License. |
14 | */ |
15 | |
16 | use crate::prelude::*; |
17 | use crate::{ |
18 | limits::*, AbstractHeapType, BinaryReaderError, Encoding, FromReader, FunctionBody, HeapType, |
19 | Parser, Payload, RefType, Result, SectionLimited, ValType, WasmFeatures, WASM_MODULE_VERSION, |
20 | }; |
21 | use ::core::mem; |
22 | use ::core::ops::Range; |
23 | use ::core::sync::atomic::{AtomicUsize, Ordering}; |
24 | use alloc::sync::Arc; |
25 | |
26 | /// Test whether the given buffer contains a valid WebAssembly module or component, |
27 | /// analogous to [`WebAssembly.validate`][js] in the JS API. |
28 | /// |
29 | /// This functions requires the bytes to validate are entirely resident in memory. |
30 | /// Additionally this validates the given bytes with the default set of WebAssembly |
31 | /// features implemented by `wasmparser`. |
32 | /// |
33 | /// For more fine-tuned control over validation it's recommended to review the |
34 | /// documentation of [`Validator`]. |
35 | /// |
36 | /// Upon success, the type information for the top-level module or component will |
37 | /// be returned. |
38 | /// |
39 | /// [js]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WebAssembly/validate |
40 | pub fn validate(bytes: &[u8]) -> Result<Types> { |
41 | Validator::new().validate_all(bytes) |
42 | } |
43 | |
44 | #[test ] |
45 | fn test_validate() { |
46 | assert!(validate(&[0x0, 0x61, 0x73, 0x6d, 0x1, 0x0, 0x0, 0x0]).is_ok()); |
47 | assert!(validate(&[0x0, 0x61, 0x73, 0x6d, 0x2, 0x0, 0x0, 0x0]).is_err()); |
48 | } |
49 | |
50 | #[cfg (feature = "component-model" )] |
51 | mod component; |
52 | #[cfg (feature = "component-model" )] |
53 | pub mod component_types; |
54 | mod core; |
55 | mod func; |
56 | #[cfg (feature = "component-model" )] |
57 | pub mod names; |
58 | mod operators; |
59 | pub mod types; |
60 | |
61 | #[cfg (feature = "component-model" )] |
62 | use self::component::*; |
63 | pub use self::core::ValidatorResources; |
64 | use self::core::*; |
65 | use self::types::{TypeAlloc, Types, TypesRef}; |
66 | pub use func::{FuncToValidate, FuncValidator, FuncValidatorAllocations}; |
67 | pub use operators::Frame; |
68 | |
69 | fn check_max(cur_len: usize, amt_added: u32, max: usize, desc: &str, offset: usize) -> Result<()> { |
70 | if maxOption |
71 | .checked_sub(cur_len) |
72 | .and_then(|amt: usize| amt.checked_sub(amt_added as usize)) |
73 | .is_none() |
74 | { |
75 | if max == 1 { |
76 | bail!(offset, "multiple {desc}" ); |
77 | } |
78 | |
79 | bail!(offset, " {desc} count exceeds limit of {max}" ); |
80 | } |
81 | |
82 | Ok(()) |
83 | } |
84 | |
85 | fn combine_type_sizes(a: u32, b: u32, offset: usize) -> Result<u32> { |
86 | match a.checked_add(b) { |
87 | Some(sum: u32) if sum < MAX_WASM_TYPE_SIZE => Ok(sum), |
88 | _ => Err(format_err!( |
89 | offset, |
90 | "effective type size exceeds the limit of {MAX_WASM_TYPE_SIZE}" , |
91 | )), |
92 | } |
93 | } |
94 | |
95 | /// A unique identifier for a particular `Validator`. |
96 | /// |
97 | /// Allows you to save the `ValidatorId` of the [`Validator`][crate::Validator] |
98 | /// you get identifiers out of (e.g. [`CoreTypeId`][crate::types::CoreTypeId]) |
99 | /// and then later assert that you are pairing those identifiers with the same |
100 | /// `Validator` instance when accessing the identifier's associated data. |
101 | #[derive (Clone, Copy, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)] |
102 | pub struct ValidatorId(usize); |
103 | |
104 | impl Default for ValidatorId { |
105 | #[inline ] |
106 | fn default() -> Self { |
107 | static ID_COUNTER: AtomicUsize = AtomicUsize::new(0); |
108 | ValidatorId(ID_COUNTER.fetch_add(val:1, order:Ordering::AcqRel)) |
109 | } |
110 | } |
111 | |
112 | /// Validator for a WebAssembly binary module or component. |
113 | /// |
114 | /// This structure encapsulates state necessary to validate a WebAssembly |
115 | /// binary. This implements validation as defined by the [core |
116 | /// specification][core]. A `Validator` is designed, like |
117 | /// [`Parser`], to accept incremental input over time. |
118 | /// Additionally a `Validator` is also designed for parallel validation of |
119 | /// functions as they are received. |
120 | /// |
121 | /// It's expected that you'll be using a [`Parser`] in tandem with a |
122 | /// `Validator`. As each [`Payload`](crate::Payload) is received from a |
123 | /// [`Parser`] you'll pass it into a `Validator` to test the validity of the |
124 | /// payload. Note that all payloads received from a [`Parser`] are expected to |
125 | /// be passed to a [`Validator`]. For example if you receive |
126 | /// [`Payload::TypeSection`](crate::Payload) you'll call |
127 | /// [`Validator::type_section`] to validate this. |
128 | /// |
129 | /// The design of [`Validator`] is intended that you'll interleave, in your own |
130 | /// application's processing, calls to validation. Each variant, after it's |
131 | /// received, will be validated and then your application would proceed as |
132 | /// usual. At all times, however, you'll have access to the [`Validator`] and |
133 | /// the validation context up to that point. This enables applications to check |
134 | /// the types of functions and learn how many globals there are, for example. |
135 | /// |
136 | /// [core]: https://webassembly.github.io/spec/core/valid/index.html |
137 | #[derive (Default)] |
138 | pub struct Validator { |
139 | id: ValidatorId, |
140 | |
141 | /// The current state of the validator. |
142 | state: State, |
143 | |
144 | /// The global type space used by the validator and any sub-validators. |
145 | types: TypeAlloc, |
146 | |
147 | /// The module state when parsing a WebAssembly module. |
148 | module: Option<ModuleState>, |
149 | |
150 | /// With the component model enabled, this stores the pushed component states. |
151 | /// The top of the stack is the current component state. |
152 | #[cfg (feature = "component-model" )] |
153 | components: Vec<ComponentState>, |
154 | |
155 | /// Enabled WebAssembly feature flags, dictating what's valid and what |
156 | /// isn't. |
157 | features: WasmFeatures, |
158 | } |
159 | |
160 | #[derive (Debug, Clone, Copy, Eq, PartialEq)] |
161 | enum State { |
162 | /// A header has not yet been parsed. |
163 | /// |
164 | /// The value is the expected encoding for the header. |
165 | Unparsed(Option<Encoding>), |
166 | /// A module header has been parsed. |
167 | /// |
168 | /// The associated module state is available via [`Validator::module`]. |
169 | Module, |
170 | /// A component header has been parsed. |
171 | /// |
172 | /// The associated component state exists at the top of the |
173 | /// validator's [`Validator::components`] stack. |
174 | #[cfg (feature = "component-model" )] |
175 | Component, |
176 | /// The parse has completed and no more data is expected. |
177 | End, |
178 | } |
179 | |
180 | impl State { |
181 | fn ensure_parsable(&self, offset: usize) -> Result<()> { |
182 | match self { |
183 | Self::Module => Ok(()), |
184 | #[cfg (feature = "component-model" )] |
185 | Self::Component => Ok(()), |
186 | Self::Unparsed(_) => Err(BinaryReaderError::new( |
187 | "unexpected section before header was parsed" , |
188 | offset, |
189 | )), |
190 | Self::End => Err(BinaryReaderError::new( |
191 | "unexpected section after parsing has completed" , |
192 | offset, |
193 | )), |
194 | } |
195 | } |
196 | |
197 | fn ensure_module(&self, section: &str, offset: usize) -> Result<()> { |
198 | self.ensure_parsable(offset)?; |
199 | let _ = section; |
200 | |
201 | match self { |
202 | Self::Module => Ok(()), |
203 | #[cfg (feature = "component-model" )] |
204 | Self::Component => Err(format_err!( |
205 | offset, |
206 | "unexpected module {section} section while parsing a component" , |
207 | )), |
208 | _ => unreachable!(), |
209 | } |
210 | } |
211 | |
212 | #[cfg (feature = "component-model" )] |
213 | fn ensure_component(&self, section: &str, offset: usize) -> Result<()> { |
214 | self.ensure_parsable(offset)?; |
215 | |
216 | match self { |
217 | Self::Component => Ok(()), |
218 | Self::Module => Err(format_err!( |
219 | offset, |
220 | "unexpected component {section} section while parsing a module" , |
221 | )), |
222 | _ => unreachable!(), |
223 | } |
224 | } |
225 | } |
226 | |
227 | impl Default for State { |
228 | fn default() -> Self { |
229 | Self::Unparsed(None) |
230 | } |
231 | } |
232 | |
233 | impl WasmFeatures { |
234 | /// NOTE: This only checks that the value type corresponds to the feature set!! |
235 | /// |
236 | /// To check that reference types are valid, we need access to the module |
237 | /// types. Use module.check_value_type. |
238 | pub(crate) fn check_value_type(&self, ty: ValType) -> Result<(), &'static str> { |
239 | match ty { |
240 | ValType::I32 | ValType::I64 => Ok(()), |
241 | ValType::F32 | ValType::F64 => { |
242 | if self.floats() { |
243 | Ok(()) |
244 | } else { |
245 | Err("floating-point support is disabled" ) |
246 | } |
247 | } |
248 | ValType::Ref(r) => self.check_ref_type(r), |
249 | ValType::V128 => { |
250 | if self.simd() { |
251 | Ok(()) |
252 | } else { |
253 | Err("SIMD support is not enabled" ) |
254 | } |
255 | } |
256 | } |
257 | } |
258 | |
259 | pub(crate) fn check_ref_type(&self, r: RefType) -> Result<(), &'static str> { |
260 | if !self.reference_types() { |
261 | return Err("reference types support is not enabled" ); |
262 | } |
263 | match r.heap_type() { |
264 | HeapType::Concrete(_) => { |
265 | // Note that `self.gc_types()` is not checked here because |
266 | // concrete pointers to function types are allowed. GC types |
267 | // are disallowed by instead rejecting the definition of |
268 | // array/struct types and only allowing the definition of |
269 | // function types. |
270 | |
271 | // Indexed types require either the function-references or gc |
272 | // proposal as gc implies function references here. |
273 | if self.function_references() || self.gc() { |
274 | Ok(()) |
275 | } else { |
276 | Err("function references required for index reference types" ) |
277 | } |
278 | } |
279 | HeapType::Abstract { shared, ty } => { |
280 | use AbstractHeapType::*; |
281 | if shared && !self.shared_everything_threads() { |
282 | return Err( |
283 | "shared reference types require the shared-everything-threads proposal" , |
284 | ); |
285 | } |
286 | |
287 | // Apply the "gc-types" feature which disallows all heap types |
288 | // except exnref/funcref. |
289 | if !self.gc_types() && ty != Func && ty != Exn { |
290 | return Err("gc types are disallowed but found type which requires gc" ); |
291 | } |
292 | |
293 | match (ty, r.is_nullable()) { |
294 | // funcref/externref only require `reference-types`. |
295 | (Func, true) | (Extern, true) => Ok(()), |
296 | |
297 | // Non-nullable func/extern references requires the |
298 | // `function-references` proposal. |
299 | (Func | Extern, false) => { |
300 | if self.function_references() { |
301 | Ok(()) |
302 | } else { |
303 | Err("function references required for non-nullable types" ) |
304 | } |
305 | } |
306 | |
307 | // These types were added in the gc proposal. |
308 | (Any | None | Eq | Struct | Array | I31 | NoExtern | NoFunc, _) => { |
309 | if self.gc() { |
310 | Ok(()) |
311 | } else { |
312 | Err("heap types not supported without the gc feature" ) |
313 | } |
314 | } |
315 | |
316 | // These types were added in the exception-handling proposal. |
317 | (Exn | NoExn, _) => { |
318 | if self.exceptions() { |
319 | Ok(()) |
320 | } else { |
321 | Err("exception refs not supported without the exception handling feature" ) |
322 | } |
323 | } |
324 | |
325 | // These types were added in the stack switching proposal. |
326 | (Cont | NoCont, _) => { |
327 | if self.stack_switching() { |
328 | Ok(()) |
329 | } else { |
330 | Err("continuation refs not supported without the stack switching feature" ) |
331 | } |
332 | } |
333 | } |
334 | } |
335 | } |
336 | } |
337 | } |
338 | |
339 | /// Possible return values from [`Validator::payload`]. |
340 | #[allow (clippy::large_enum_variant)] |
341 | pub enum ValidPayload<'a> { |
342 | /// The payload validated, no further action need be taken. |
343 | Ok, |
344 | /// The payload validated, but it started a nested module or component. |
345 | /// |
346 | /// This result indicates that the specified parser should be used instead |
347 | /// of the currently-used parser until this returned one ends. |
348 | Parser(Parser), |
349 | /// A function was found to be validate. |
350 | Func(FuncToValidate<ValidatorResources>, FunctionBody<'a>), |
351 | /// The end payload was validated and the types known to the validator |
352 | /// are provided. |
353 | End(Types), |
354 | } |
355 | |
356 | impl Validator { |
357 | /// Creates a new [`Validator`] ready to validate a WebAssembly module |
358 | /// or component. |
359 | /// |
360 | /// The new validator will receive payloads parsed from |
361 | /// [`Parser`], and expects the first payload received to be |
362 | /// the version header from the parser. |
363 | pub fn new() -> Validator { |
364 | Validator::default() |
365 | } |
366 | |
367 | /// Creates a new [`Validator`] which has the specified set of wasm |
368 | /// features activated for validation. |
369 | /// |
370 | /// This function is the same as [`Validator::new`] except it also allows |
371 | /// you to customize the active wasm features in use for validation. This |
372 | /// can allow enabling experimental proposals or also turning off |
373 | /// on-by-default wasm proposals. |
374 | pub fn new_with_features(features: WasmFeatures) -> Validator { |
375 | let mut ret = Validator::new(); |
376 | ret.features = features; |
377 | ret |
378 | } |
379 | |
380 | /// Returns the wasm features used for this validator. |
381 | pub fn features(&self) -> &WasmFeatures { |
382 | &self.features |
383 | } |
384 | |
385 | /// Reset this validator's state such that it is ready to validate a new |
386 | /// Wasm module or component. |
387 | /// |
388 | /// This does *not* clear or reset the internal state keeping track of |
389 | /// validated (and deduplicated and canonicalized) types, allowing you to |
390 | /// use the same type identifiers (such as |
391 | /// [`CoreTypeId`][crate::types::CoreTypeId]) for the same types that are |
392 | /// defined multiple times across different modules and components. |
393 | /// |
394 | /// ``` |
395 | /// fn foo() -> anyhow::Result<()> { |
396 | /// use wasmparser::Validator; |
397 | /// |
398 | /// let mut validator = Validator::default(); |
399 | /// |
400 | /// // Two wasm modules, both of which define the same type, but at |
401 | /// // different indices in their respective types index spaces. |
402 | /// let wasm1 = wat::parse_str(" |
403 | /// (module |
404 | /// (type $same_type (func (param i32) (result f64))) |
405 | /// ) |
406 | /// " )?; |
407 | /// let wasm2 = wat::parse_str(" |
408 | /// (module |
409 | /// (type $different_type (func)) |
410 | /// (type $same_type (func (param i32) (result f64))) |
411 | /// ) |
412 | /// " )?; |
413 | /// |
414 | /// // Validate the first Wasm module and get the ID of its type. |
415 | /// let types = validator.validate_all(&wasm1)?; |
416 | /// let id1 = types.as_ref().core_type_at_in_module(0); |
417 | /// |
418 | /// // Reset the validator so we can parse the second wasm module inside |
419 | /// // this validator's same context. |
420 | /// validator.reset(); |
421 | /// |
422 | /// // Validate the second Wasm module and get the ID of its second type, |
423 | /// // which is the same type as the first Wasm module's only type. |
424 | /// let types = validator.validate_all(&wasm2)?; |
425 | /// let id2 = types.as_ref().core_type_at_in_module(1); |
426 | /// |
427 | /// // Because both modules were processed in the same `Validator`, they |
428 | /// // share the same types context and therefore the same type defined |
429 | /// // multiple times across different modules will be deduplicated and |
430 | /// // assigned the same identifier! |
431 | /// assert_eq!(id1, id2); |
432 | /// assert_eq!(types[id1], types[id2]); |
433 | /// # Ok(()) |
434 | /// # } |
435 | /// # foo().unwrap() |
436 | /// ``` |
437 | pub fn reset(&mut self) { |
438 | let Validator { |
439 | // Not changing the identifier; users should be able to observe that |
440 | // they are using the same validation context, even after resetting. |
441 | id: _, |
442 | |
443 | // Don't mess with `types`, we specifically want to reuse canonicalizations. |
444 | types: _, |
445 | |
446 | // Also leave features as they are. While this is perhaps not |
447 | // strictly necessary, it helps us avoid weird bugs where we have |
448 | // different views of what is or is not a valid type at different |
449 | // times, despite using the same `TypeList` and hash consing |
450 | // context, and therefore there could be moments in time where we |
451 | // have "invalid" types inside our current types list. |
452 | features: _, |
453 | |
454 | state, |
455 | module, |
456 | #[cfg (feature = "component-model" )] |
457 | components, |
458 | } = self; |
459 | |
460 | assert!( |
461 | matches!(state, State::End), |
462 | "cannot reset a validator that did not successfully complete validation" |
463 | ); |
464 | assert!(module.is_none()); |
465 | #[cfg (feature = "component-model" )] |
466 | assert!(components.is_empty()); |
467 | |
468 | *state = State::default(); |
469 | } |
470 | |
471 | /// Get this validator's unique identifier. |
472 | /// |
473 | /// Allows you to assert that you are always working with the same |
474 | /// `Validator` instance, when you can't otherwise statically ensure that |
475 | /// property by e.g. storing a reference to the validator inside your |
476 | /// structure. |
477 | pub fn id(&self) -> ValidatorId { |
478 | self.id |
479 | } |
480 | |
481 | /// Validates an entire in-memory module or component with this validator. |
482 | /// |
483 | /// This function will internally create a [`Parser`] to parse the `bytes` |
484 | /// provided. The entire module or component specified by `bytes` will be |
485 | /// parsed and validated. |
486 | /// |
487 | /// Upon success, the type information for the top-level module or component |
488 | /// will be returned. |
489 | pub fn validate_all(&mut self, bytes: &[u8]) -> Result<Types> { |
490 | let mut functions_to_validate = Vec::new(); |
491 | let mut last_types = None; |
492 | let mut parser = Parser::new(0); |
493 | let _ = &mut parser; |
494 | #[cfg (feature = "features" )] |
495 | parser.set_features(self.features); |
496 | for payload in parser.parse_all(bytes) { |
497 | match self.payload(&payload?)? { |
498 | ValidPayload::Func(a, b) => { |
499 | functions_to_validate.push((a, b)); |
500 | } |
501 | ValidPayload::End(types) => { |
502 | // Only the last (top-level) type information will be returned |
503 | last_types = Some(types); |
504 | } |
505 | _ => {} |
506 | } |
507 | } |
508 | |
509 | let mut allocs = FuncValidatorAllocations::default(); |
510 | for (func, body) in functions_to_validate { |
511 | let mut validator = func.into_validator(allocs); |
512 | validator.validate(&body)?; |
513 | allocs = validator.into_allocations(); |
514 | } |
515 | |
516 | Ok(last_types.unwrap()) |
517 | } |
518 | |
519 | /// Gets the types known by the validator so far within the |
520 | /// module/component `level` modules/components up from the |
521 | /// module/component currently being parsed. |
522 | /// |
523 | /// For instance, calling `validator.types(0)` will get the types of the |
524 | /// module/component currently being parsed, and `validator.types(1)` will |
525 | /// get the types of the component containing that module/component. |
526 | /// |
527 | /// Returns `None` if there is no module/component that many levels up. |
528 | pub fn types(&self, mut level: usize) -> Option<TypesRef> { |
529 | if let Some(module) = &self.module { |
530 | if level == 0 { |
531 | return Some(TypesRef::from_module(self.id, &self.types, &module.module)); |
532 | } else { |
533 | level -= 1; |
534 | let _ = level; |
535 | } |
536 | } |
537 | |
538 | #[cfg (feature = "component-model" )] |
539 | return self |
540 | .components |
541 | .iter() |
542 | .nth_back(level) |
543 | .map(|component| TypesRef::from_component(self.id, &self.types, component)); |
544 | #[cfg (not(feature = "component-model" ))] |
545 | return None; |
546 | } |
547 | |
548 | /// Convenience function to validate a single [`Payload`]. |
549 | /// |
550 | /// This function is intended to be used as a convenience. It will |
551 | /// internally perform any validation necessary to validate the [`Payload`] |
552 | /// provided. The convenience part is that you're likely already going to |
553 | /// be matching on [`Payload`] in your application, at which point it's more |
554 | /// appropriate to call the individual methods on [`Validator`] per-variant |
555 | /// in [`Payload`], such as [`Validator::type_section`]. |
556 | /// |
557 | /// This function returns a [`ValidPayload`] variant on success, indicating |
558 | /// one of a few possible actions that need to be taken after a payload is |
559 | /// validated. For example function contents are not validated here, they're |
560 | /// returned through [`ValidPayload`] for validation by the caller. |
561 | pub fn payload<'a>(&mut self, payload: &Payload<'a>) -> Result<ValidPayload<'a>> { |
562 | use crate::Payload::*; |
563 | match payload { |
564 | Version { |
565 | num, |
566 | encoding, |
567 | range, |
568 | } => self.version(*num, *encoding, range)?, |
569 | |
570 | // Module sections |
571 | TypeSection(s) => self.type_section(s)?, |
572 | ImportSection(s) => self.import_section(s)?, |
573 | FunctionSection(s) => self.function_section(s)?, |
574 | TableSection(s) => self.table_section(s)?, |
575 | MemorySection(s) => self.memory_section(s)?, |
576 | TagSection(s) => self.tag_section(s)?, |
577 | GlobalSection(s) => self.global_section(s)?, |
578 | ExportSection(s) => self.export_section(s)?, |
579 | StartSection { func, range } => self.start_section(*func, range)?, |
580 | ElementSection(s) => self.element_section(s)?, |
581 | DataCountSection { count, range } => self.data_count_section(*count, range)?, |
582 | CodeSectionStart { |
583 | count, |
584 | range, |
585 | size: _, |
586 | } => self.code_section_start(*count, range)?, |
587 | CodeSectionEntry(body) => { |
588 | let func_validator = self.code_section_entry(body)?; |
589 | return Ok(ValidPayload::Func(func_validator, body.clone())); |
590 | } |
591 | DataSection(s) => self.data_section(s)?, |
592 | |
593 | // Component sections |
594 | #[cfg (feature = "component-model" )] |
595 | ModuleSection { |
596 | parser, |
597 | unchecked_range: range, |
598 | .. |
599 | } => { |
600 | self.module_section(range)?; |
601 | return Ok(ValidPayload::Parser(parser.clone())); |
602 | } |
603 | #[cfg (feature = "component-model" )] |
604 | InstanceSection(s) => self.instance_section(s)?, |
605 | #[cfg (feature = "component-model" )] |
606 | CoreTypeSection(s) => self.core_type_section(s)?, |
607 | #[cfg (feature = "component-model" )] |
608 | ComponentSection { |
609 | parser, |
610 | unchecked_range: range, |
611 | .. |
612 | } => { |
613 | self.component_section(range)?; |
614 | return Ok(ValidPayload::Parser(parser.clone())); |
615 | } |
616 | #[cfg (feature = "component-model" )] |
617 | ComponentInstanceSection(s) => self.component_instance_section(s)?, |
618 | #[cfg (feature = "component-model" )] |
619 | ComponentAliasSection(s) => self.component_alias_section(s)?, |
620 | #[cfg (feature = "component-model" )] |
621 | ComponentTypeSection(s) => self.component_type_section(s)?, |
622 | #[cfg (feature = "component-model" )] |
623 | ComponentCanonicalSection(s) => self.component_canonical_section(s)?, |
624 | #[cfg (feature = "component-model" )] |
625 | ComponentStartSection { start, range } => self.component_start_section(start, range)?, |
626 | #[cfg (feature = "component-model" )] |
627 | ComponentImportSection(s) => self.component_import_section(s)?, |
628 | #[cfg (feature = "component-model" )] |
629 | ComponentExportSection(s) => self.component_export_section(s)?, |
630 | |
631 | End(offset) => return Ok(ValidPayload::End(self.end(*offset)?)), |
632 | |
633 | CustomSection { .. } => {} // no validation for custom sections |
634 | UnknownSection { id, range, .. } => self.unknown_section(*id, range)?, |
635 | } |
636 | Ok(ValidPayload::Ok) |
637 | } |
638 | |
639 | /// Validates [`Payload::Version`](crate::Payload). |
640 | pub fn version(&mut self, num: u16, encoding: Encoding, range: &Range<usize>) -> Result<()> { |
641 | match &self.state { |
642 | State::Unparsed(expected) => { |
643 | if let Some(expected) = expected { |
644 | if *expected != encoding { |
645 | bail!( |
646 | range.start, |
647 | "expected a version header for a {}" , |
648 | match expected { |
649 | Encoding::Module => "module" , |
650 | Encoding::Component => "component" , |
651 | } |
652 | ); |
653 | } |
654 | } |
655 | } |
656 | _ => { |
657 | return Err(BinaryReaderError::new( |
658 | "wasm version header out of order" , |
659 | range.start, |
660 | )) |
661 | } |
662 | } |
663 | |
664 | self.state = match encoding { |
665 | Encoding::Module => { |
666 | if num == WASM_MODULE_VERSION { |
667 | assert!(self.module.is_none()); |
668 | self.module = Some(ModuleState::default()); |
669 | State::Module |
670 | } else { |
671 | bail!(range.start, "unknown binary version: {num:#x}" ); |
672 | } |
673 | } |
674 | Encoding::Component => { |
675 | if !self.features.component_model() { |
676 | bail!( |
677 | range.start, |
678 | "unknown binary version and encoding combination: {num:#x} and 0x1, \ |
679 | note: encoded as a component but the WebAssembly component model feature \ |
680 | is not enabled - enable the feature to allow component validation" , |
681 | ); |
682 | } |
683 | #[cfg (feature = "component-model" )] |
684 | if num == crate::WASM_COMPONENT_VERSION { |
685 | self.components |
686 | .push(ComponentState::new(ComponentKind::Component)); |
687 | State::Component |
688 | } else if num < crate::WASM_COMPONENT_VERSION { |
689 | bail!(range.start, "unsupported component version: {num:#x}" ); |
690 | } else { |
691 | bail!(range.start, "unknown component version: {num:#x}" ); |
692 | } |
693 | #[cfg (not(feature = "component-model" ))] |
694 | bail!( |
695 | range.start, |
696 | "component model validation support disabled \ |
697 | at compile time" |
698 | ); |
699 | } |
700 | }; |
701 | |
702 | Ok(()) |
703 | } |
704 | |
705 | /// Validates [`Payload::TypeSection`](crate::Payload). |
706 | pub fn type_section(&mut self, section: &crate::TypeSectionReader<'_>) -> Result<()> { |
707 | self.process_module_section( |
708 | Order::Type, |
709 | section, |
710 | "type" , |
711 | |state, _, _types, count, offset| { |
712 | check_max( |
713 | state.module.types.len(), |
714 | count, |
715 | MAX_WASM_TYPES, |
716 | "types" , |
717 | offset, |
718 | )?; |
719 | state.module.assert_mut().types.reserve(count as usize); |
720 | Ok(()) |
721 | }, |
722 | |state, features, types, rec_group, offset| { |
723 | state |
724 | .module |
725 | .assert_mut() |
726 | .add_types(rec_group, features, types, offset, true)?; |
727 | Ok(()) |
728 | }, |
729 | ) |
730 | } |
731 | |
732 | /// Validates [`Payload::ImportSection`](crate::Payload). |
733 | /// |
734 | /// This method should only be called when parsing a module. |
735 | pub fn import_section(&mut self, section: &crate::ImportSectionReader<'_>) -> Result<()> { |
736 | self.process_module_section( |
737 | Order::Import, |
738 | section, |
739 | "import" , |
740 | |state, _, _, count, offset| { |
741 | check_max( |
742 | state.module.imports.len(), |
743 | count, |
744 | MAX_WASM_IMPORTS, |
745 | "imports" , |
746 | offset, |
747 | )?; |
748 | state.module.assert_mut().imports.reserve(count as usize); |
749 | Ok(()) |
750 | }, |
751 | |state, features, types, import, offset| { |
752 | state |
753 | .module |
754 | .assert_mut() |
755 | .add_import(import, features, types, offset) |
756 | }, |
757 | ) |
758 | } |
759 | |
760 | /// Validates [`Payload::FunctionSection`](crate::Payload). |
761 | /// |
762 | /// This method should only be called when parsing a module. |
763 | pub fn function_section(&mut self, section: &crate::FunctionSectionReader<'_>) -> Result<()> { |
764 | self.process_module_section( |
765 | Order::Function, |
766 | section, |
767 | "function" , |
768 | |state, _, _, count, offset| { |
769 | check_max( |
770 | state.module.functions.len(), |
771 | count, |
772 | MAX_WASM_FUNCTIONS, |
773 | "functions" , |
774 | offset, |
775 | )?; |
776 | state.module.assert_mut().functions.reserve(count as usize); |
777 | debug_assert!(state.expected_code_bodies.is_none()); |
778 | state.expected_code_bodies = Some(count); |
779 | Ok(()) |
780 | }, |
781 | |state, _, types, ty, offset| state.module.assert_mut().add_function(ty, types, offset), |
782 | ) |
783 | } |
784 | |
785 | /// Validates [`Payload::TableSection`](crate::Payload). |
786 | /// |
787 | /// This method should only be called when parsing a module. |
788 | pub fn table_section(&mut self, section: &crate::TableSectionReader<'_>) -> Result<()> { |
789 | let features = self.features; |
790 | self.process_module_section( |
791 | Order::Table, |
792 | section, |
793 | "table" , |
794 | |state, _, _, count, offset| { |
795 | check_max( |
796 | state.module.tables.len(), |
797 | count, |
798 | state.module.max_tables(&features), |
799 | "tables" , |
800 | offset, |
801 | )?; |
802 | state.module.assert_mut().tables.reserve(count as usize); |
803 | Ok(()) |
804 | }, |
805 | |state, features, types, table, offset| state.add_table(table, features, types, offset), |
806 | ) |
807 | } |
808 | |
809 | /// Validates [`Payload::MemorySection`](crate::Payload). |
810 | /// |
811 | /// This method should only be called when parsing a module. |
812 | pub fn memory_section(&mut self, section: &crate::MemorySectionReader<'_>) -> Result<()> { |
813 | self.process_module_section( |
814 | Order::Memory, |
815 | section, |
816 | "memory" , |
817 | |state, features, _, count, offset| { |
818 | check_max( |
819 | state.module.memories.len(), |
820 | count, |
821 | state.module.max_memories(features), |
822 | "memories" , |
823 | offset, |
824 | )?; |
825 | state.module.assert_mut().memories.reserve(count as usize); |
826 | Ok(()) |
827 | }, |
828 | |state, features, _, ty, offset| { |
829 | state.module.assert_mut().add_memory(ty, features, offset) |
830 | }, |
831 | ) |
832 | } |
833 | |
834 | /// Validates [`Payload::TagSection`](crate::Payload). |
835 | /// |
836 | /// This method should only be called when parsing a module. |
837 | pub fn tag_section(&mut self, section: &crate::TagSectionReader<'_>) -> Result<()> { |
838 | if !self.features.exceptions() { |
839 | return Err(BinaryReaderError::new( |
840 | "exceptions proposal not enabled" , |
841 | section.range().start, |
842 | )); |
843 | } |
844 | |
845 | self.process_module_section( |
846 | Order::Tag, |
847 | section, |
848 | "tag" , |
849 | |state, _, _, count, offset| { |
850 | check_max( |
851 | state.module.tags.len(), |
852 | count, |
853 | MAX_WASM_TAGS, |
854 | "tags" , |
855 | offset, |
856 | )?; |
857 | state.module.assert_mut().tags.reserve(count as usize); |
858 | Ok(()) |
859 | }, |
860 | |state, features, types, ty, offset| { |
861 | state |
862 | .module |
863 | .assert_mut() |
864 | .add_tag(ty, features, types, offset) |
865 | }, |
866 | ) |
867 | } |
868 | |
869 | /// Validates [`Payload::GlobalSection`](crate::Payload). |
870 | /// |
871 | /// This method should only be called when parsing a module. |
872 | pub fn global_section(&mut self, section: &crate::GlobalSectionReader<'_>) -> Result<()> { |
873 | self.process_module_section( |
874 | Order::Global, |
875 | section, |
876 | "global" , |
877 | |state, _, _, count, offset| { |
878 | check_max( |
879 | state.module.globals.len(), |
880 | count, |
881 | MAX_WASM_GLOBALS, |
882 | "globals" , |
883 | offset, |
884 | )?; |
885 | state.module.assert_mut().globals.reserve(count as usize); |
886 | Ok(()) |
887 | }, |
888 | |state, features, types, global, offset| { |
889 | state.add_global(global, features, types, offset) |
890 | }, |
891 | ) |
892 | } |
893 | |
894 | /// Validates [`Payload::ExportSection`](crate::Payload). |
895 | /// |
896 | /// This method should only be called when parsing a module. |
897 | pub fn export_section(&mut self, section: &crate::ExportSectionReader<'_>) -> Result<()> { |
898 | self.process_module_section( |
899 | Order::Export, |
900 | section, |
901 | "export" , |
902 | |state, _, _, count, offset| { |
903 | check_max( |
904 | state.module.exports.len(), |
905 | count, |
906 | MAX_WASM_EXPORTS, |
907 | "exports" , |
908 | offset, |
909 | )?; |
910 | state.module.assert_mut().exports.reserve(count as usize); |
911 | Ok(()) |
912 | }, |
913 | |state, features, types, e, offset| { |
914 | let state = state.module.assert_mut(); |
915 | let ty = state.export_to_entity_type(&e, offset)?; |
916 | state.add_export( |
917 | e.name, ty, features, offset, false, /* checked above */ |
918 | types, |
919 | ) |
920 | }, |
921 | ) |
922 | } |
923 | |
924 | /// Validates [`Payload::StartSection`](crate::Payload). |
925 | /// |
926 | /// This method should only be called when parsing a module. |
927 | pub fn start_section(&mut self, func: u32, range: &Range<usize>) -> Result<()> { |
928 | let offset = range.start; |
929 | self.state.ensure_module("start" , offset)?; |
930 | let state = self.module.as_mut().unwrap(); |
931 | state.update_order(Order::Start, offset)?; |
932 | |
933 | let ty = state.module.get_func_type(func, &self.types, offset)?; |
934 | if !ty.params().is_empty() || !ty.results().is_empty() { |
935 | return Err(BinaryReaderError::new( |
936 | "invalid start function type" , |
937 | offset, |
938 | )); |
939 | } |
940 | |
941 | Ok(()) |
942 | } |
943 | |
944 | /// Validates [`Payload::ElementSection`](crate::Payload). |
945 | /// |
946 | /// This method should only be called when parsing a module. |
947 | pub fn element_section(&mut self, section: &crate::ElementSectionReader<'_>) -> Result<()> { |
948 | self.process_module_section( |
949 | Order::Element, |
950 | section, |
951 | "element" , |
952 | |state, _, _, count, offset| { |
953 | check_max( |
954 | state.module.element_types.len(), |
955 | count, |
956 | MAX_WASM_ELEMENT_SEGMENTS, |
957 | "element segments" , |
958 | offset, |
959 | )?; |
960 | state |
961 | .module |
962 | .assert_mut() |
963 | .element_types |
964 | .reserve(count as usize); |
965 | Ok(()) |
966 | }, |
967 | |state, features, types, e, offset| { |
968 | state.add_element_segment(e, features, types, offset) |
969 | }, |
970 | ) |
971 | } |
972 | |
973 | /// Validates [`Payload::DataCountSection`](crate::Payload). |
974 | /// |
975 | /// This method should only be called when parsing a module. |
976 | pub fn data_count_section(&mut self, count: u32, range: &Range<usize>) -> Result<()> { |
977 | let offset = range.start; |
978 | self.state.ensure_module("data count" , offset)?; |
979 | |
980 | let state = self.module.as_mut().unwrap(); |
981 | state.update_order(Order::DataCount, offset)?; |
982 | |
983 | if count > MAX_WASM_DATA_SEGMENTS as u32 { |
984 | return Err(BinaryReaderError::new( |
985 | "data count section specifies too many data segments" , |
986 | offset, |
987 | )); |
988 | } |
989 | |
990 | state.module.assert_mut().data_count = Some(count); |
991 | Ok(()) |
992 | } |
993 | |
994 | /// Validates [`Payload::CodeSectionStart`](crate::Payload). |
995 | /// |
996 | /// This method should only be called when parsing a module. |
997 | pub fn code_section_start(&mut self, count: u32, range: &Range<usize>) -> Result<()> { |
998 | let offset = range.start; |
999 | self.state.ensure_module("code" , offset)?; |
1000 | |
1001 | let state = self.module.as_mut().unwrap(); |
1002 | state.update_order(Order::Code, offset)?; |
1003 | |
1004 | match state.expected_code_bodies.take() { |
1005 | Some(n) if n == count => {} |
1006 | Some(_) => { |
1007 | return Err(BinaryReaderError::new( |
1008 | "function and code section have inconsistent lengths" , |
1009 | offset, |
1010 | )); |
1011 | } |
1012 | // empty code sections are allowed even if the function section is |
1013 | // missing |
1014 | None if count == 0 => {} |
1015 | None => { |
1016 | return Err(BinaryReaderError::new( |
1017 | "code section without function section" , |
1018 | offset, |
1019 | )) |
1020 | } |
1021 | } |
1022 | |
1023 | // Take a snapshot of the types when we start the code section. |
1024 | state.module.assert_mut().snapshot = Some(Arc::new(self.types.commit())); |
1025 | |
1026 | Ok(()) |
1027 | } |
1028 | |
1029 | /// Validates [`Payload::CodeSectionEntry`](crate::Payload). |
1030 | /// |
1031 | /// This function will prepare a [`FuncToValidate`] which can be used to |
1032 | /// create a [`FuncValidator`] to validate the function. The function body |
1033 | /// provided will not be parsed or validated by this function. |
1034 | /// |
1035 | /// Note that the returned [`FuncToValidate`] is "connected" to this |
1036 | /// [`Validator`] in that it uses the internal context of this validator for |
1037 | /// validating the function. The [`FuncToValidate`] can be sent to another |
1038 | /// thread, for example, to offload actual processing of functions |
1039 | /// elsewhere. |
1040 | /// |
1041 | /// This method should only be called when parsing a module. |
1042 | pub fn code_section_entry( |
1043 | &mut self, |
1044 | body: &crate::FunctionBody, |
1045 | ) -> Result<FuncToValidate<ValidatorResources>> { |
1046 | let offset = body.range().start; |
1047 | self.state.ensure_module("code" , offset)?; |
1048 | |
1049 | let state = self.module.as_mut().unwrap(); |
1050 | |
1051 | let (index, ty) = state.next_code_index_and_type(offset)?; |
1052 | Ok(FuncToValidate { |
1053 | index, |
1054 | ty, |
1055 | resources: ValidatorResources(state.module.arc().clone()), |
1056 | features: self.features, |
1057 | }) |
1058 | } |
1059 | |
1060 | /// Validates [`Payload::DataSection`](crate::Payload). |
1061 | /// |
1062 | /// This method should only be called when parsing a module. |
1063 | pub fn data_section(&mut self, section: &crate::DataSectionReader<'_>) -> Result<()> { |
1064 | self.process_module_section( |
1065 | Order::Data, |
1066 | section, |
1067 | "data" , |
1068 | |state, _, _, count, offset| { |
1069 | state.data_segment_count = count; |
1070 | check_max(0, count, MAX_WASM_DATA_SEGMENTS, "data segments" , offset) |
1071 | }, |
1072 | |state, features, types, d, offset| state.add_data_segment(d, features, types, offset), |
1073 | ) |
1074 | } |
1075 | |
1076 | /// Validates [`Payload::ModuleSection`](crate::Payload). |
1077 | /// |
1078 | /// This method should only be called when parsing a component. |
1079 | #[cfg (feature = "component-model" )] |
1080 | pub fn module_section(&mut self, range: &Range<usize>) -> Result<()> { |
1081 | self.state.ensure_component("module" , range.start)?; |
1082 | |
1083 | let current = self.components.last_mut().unwrap(); |
1084 | check_max( |
1085 | current.core_modules.len(), |
1086 | 1, |
1087 | MAX_WASM_MODULES, |
1088 | "modules" , |
1089 | range.start, |
1090 | )?; |
1091 | |
1092 | match mem::replace(&mut self.state, State::Unparsed(Some(Encoding::Module))) { |
1093 | State::Component => {} |
1094 | _ => unreachable!(), |
1095 | } |
1096 | |
1097 | Ok(()) |
1098 | } |
1099 | |
1100 | /// Validates [`Payload::InstanceSection`](crate::Payload). |
1101 | /// |
1102 | /// This method should only be called when parsing a component. |
1103 | #[cfg (feature = "component-model" )] |
1104 | pub fn instance_section(&mut self, section: &crate::InstanceSectionReader) -> Result<()> { |
1105 | self.process_component_section( |
1106 | section, |
1107 | "core instance" , |
1108 | |components, _, count, offset| { |
1109 | let current = components.last_mut().unwrap(); |
1110 | check_max( |
1111 | current.instance_count(), |
1112 | count, |
1113 | MAX_WASM_INSTANCES, |
1114 | "instances" , |
1115 | offset, |
1116 | )?; |
1117 | current.core_instances.reserve(count as usize); |
1118 | Ok(()) |
1119 | }, |
1120 | |components, types, _, instance, offset| { |
1121 | components |
1122 | .last_mut() |
1123 | .unwrap() |
1124 | .add_core_instance(instance, types, offset) |
1125 | }, |
1126 | ) |
1127 | } |
1128 | |
1129 | /// Validates [`Payload::CoreTypeSection`](crate::Payload). |
1130 | /// |
1131 | /// This method should only be called when parsing a component. |
1132 | #[cfg (feature = "component-model" )] |
1133 | pub fn core_type_section(&mut self, section: &crate::CoreTypeSectionReader<'_>) -> Result<()> { |
1134 | self.process_component_section( |
1135 | section, |
1136 | "core type" , |
1137 | |components, _types, count, offset| { |
1138 | let current = components.last_mut().unwrap(); |
1139 | check_max(current.type_count(), count, MAX_WASM_TYPES, "types" , offset)?; |
1140 | current.core_types.reserve(count as usize); |
1141 | Ok(()) |
1142 | }, |
1143 | |components, types, features, ty, offset| { |
1144 | ComponentState::add_core_type( |
1145 | components, ty, features, types, offset, false, /* checked above */ |
1146 | ) |
1147 | }, |
1148 | ) |
1149 | } |
1150 | |
1151 | /// Validates [`Payload::ComponentSection`](crate::Payload). |
1152 | /// |
1153 | /// This method should only be called when parsing a component. |
1154 | #[cfg (feature = "component-model" )] |
1155 | pub fn component_section(&mut self, range: &Range<usize>) -> Result<()> { |
1156 | self.state.ensure_component("component" , range.start)?; |
1157 | |
1158 | let current = self.components.last_mut().unwrap(); |
1159 | check_max( |
1160 | current.components.len(), |
1161 | 1, |
1162 | MAX_WASM_COMPONENTS, |
1163 | "components" , |
1164 | range.start, |
1165 | )?; |
1166 | |
1167 | match mem::replace(&mut self.state, State::Unparsed(Some(Encoding::Component))) { |
1168 | State::Component => {} |
1169 | _ => unreachable!(), |
1170 | } |
1171 | |
1172 | Ok(()) |
1173 | } |
1174 | |
1175 | /// Validates [`Payload::ComponentInstanceSection`](crate::Payload). |
1176 | /// |
1177 | /// This method should only be called when parsing a component. |
1178 | #[cfg (feature = "component-model" )] |
1179 | pub fn component_instance_section( |
1180 | &mut self, |
1181 | section: &crate::ComponentInstanceSectionReader, |
1182 | ) -> Result<()> { |
1183 | self.process_component_section( |
1184 | section, |
1185 | "instance" , |
1186 | |components, _, count, offset| { |
1187 | let current = components.last_mut().unwrap(); |
1188 | check_max( |
1189 | current.instance_count(), |
1190 | count, |
1191 | MAX_WASM_INSTANCES, |
1192 | "instances" , |
1193 | offset, |
1194 | )?; |
1195 | current.instances.reserve(count as usize); |
1196 | Ok(()) |
1197 | }, |
1198 | |components, types, features, instance, offset| { |
1199 | components |
1200 | .last_mut() |
1201 | .unwrap() |
1202 | .add_instance(instance, features, types, offset) |
1203 | }, |
1204 | ) |
1205 | } |
1206 | |
1207 | /// Validates [`Payload::ComponentAliasSection`](crate::Payload). |
1208 | /// |
1209 | /// This method should only be called when parsing a component. |
1210 | #[cfg (feature = "component-model" )] |
1211 | pub fn component_alias_section( |
1212 | &mut self, |
1213 | section: &crate::ComponentAliasSectionReader<'_>, |
1214 | ) -> Result<()> { |
1215 | self.process_component_section( |
1216 | section, |
1217 | "alias" , |
1218 | |_, _, _, _| Ok(()), // maximums checked via `add_alias` |
1219 | |components, types, features, alias, offset| -> Result<(), BinaryReaderError> { |
1220 | ComponentState::add_alias(components, alias, features, types, offset) |
1221 | }, |
1222 | ) |
1223 | } |
1224 | |
1225 | /// Validates [`Payload::ComponentTypeSection`](crate::Payload). |
1226 | /// |
1227 | /// This method should only be called when parsing a component. |
1228 | #[cfg (feature = "component-model" )] |
1229 | pub fn component_type_section( |
1230 | &mut self, |
1231 | section: &crate::ComponentTypeSectionReader, |
1232 | ) -> Result<()> { |
1233 | self.process_component_section( |
1234 | section, |
1235 | "type" , |
1236 | |components, _types, count, offset| { |
1237 | let current = components.last_mut().unwrap(); |
1238 | check_max(current.type_count(), count, MAX_WASM_TYPES, "types" , offset)?; |
1239 | current.types.reserve(count as usize); |
1240 | Ok(()) |
1241 | }, |
1242 | |components, types, features, ty, offset| { |
1243 | ComponentState::add_type( |
1244 | components, ty, features, types, offset, false, /* checked above */ |
1245 | ) |
1246 | }, |
1247 | ) |
1248 | } |
1249 | |
1250 | /// Validates [`Payload::ComponentCanonicalSection`](crate::Payload). |
1251 | /// |
1252 | /// This method should only be called when parsing a component. |
1253 | #[cfg (feature = "component-model" )] |
1254 | pub fn component_canonical_section( |
1255 | &mut self, |
1256 | section: &crate::ComponentCanonicalSectionReader, |
1257 | ) -> Result<()> { |
1258 | self.process_component_section( |
1259 | section, |
1260 | "function" , |
1261 | |components, _, count, offset| { |
1262 | let current = components.last_mut().unwrap(); |
1263 | check_max( |
1264 | current.function_count(), |
1265 | count, |
1266 | MAX_WASM_FUNCTIONS, |
1267 | "functions" , |
1268 | offset, |
1269 | )?; |
1270 | current.funcs.reserve(count as usize); |
1271 | Ok(()) |
1272 | }, |
1273 | |components, types, features, func, offset| { |
1274 | let current = components.last_mut().unwrap(); |
1275 | match func { |
1276 | crate::CanonicalFunction::Lift { |
1277 | core_func_index, |
1278 | type_index, |
1279 | options, |
1280 | } => current.lift_function( |
1281 | core_func_index, |
1282 | type_index, |
1283 | options.into_vec(), |
1284 | types, |
1285 | offset, |
1286 | features, |
1287 | ), |
1288 | crate::CanonicalFunction::Lower { |
1289 | func_index, |
1290 | options, |
1291 | } => current.lower_function( |
1292 | func_index, |
1293 | options.into_vec(), |
1294 | types, |
1295 | offset, |
1296 | features, |
1297 | ), |
1298 | crate::CanonicalFunction::ResourceNew { resource } => { |
1299 | current.resource_new(resource, types, offset) |
1300 | } |
1301 | crate::CanonicalFunction::ResourceDrop { resource } => { |
1302 | current.resource_drop(resource, types, offset) |
1303 | } |
1304 | crate::CanonicalFunction::ResourceRep { resource } => { |
1305 | current.resource_rep(resource, types, offset) |
1306 | } |
1307 | crate::CanonicalFunction::ThreadSpawn { func_ty_index } => { |
1308 | current.thread_spawn(func_ty_index, types, offset, features) |
1309 | } |
1310 | crate::CanonicalFunction::ThreadHwConcurrency => { |
1311 | current.thread_hw_concurrency(types, offset, features) |
1312 | } |
1313 | crate::CanonicalFunction::TaskBackpressure => { |
1314 | current.task_backpressure(types, offset, features) |
1315 | } |
1316 | crate::CanonicalFunction::TaskReturn { type_index } => { |
1317 | current.task_return(type_index, types, offset, features) |
1318 | } |
1319 | crate::CanonicalFunction::TaskWait { async_, memory } => { |
1320 | current.task_wait(async_, memory, types, offset, features) |
1321 | } |
1322 | crate::CanonicalFunction::TaskPoll { async_, memory } => { |
1323 | current.task_poll(async_, memory, types, offset, features) |
1324 | } |
1325 | crate::CanonicalFunction::TaskYield { async_ } => { |
1326 | current.task_yield(async_, types, offset, features) |
1327 | } |
1328 | crate::CanonicalFunction::SubtaskDrop => { |
1329 | current.subtask_drop(types, offset, features) |
1330 | } |
1331 | crate::CanonicalFunction::StreamNew { ty } => { |
1332 | current.stream_new(ty, types, offset, features) |
1333 | } |
1334 | crate::CanonicalFunction::StreamRead { ty, options } => { |
1335 | current.stream_read(ty, options.into_vec(), types, offset, features) |
1336 | } |
1337 | crate::CanonicalFunction::StreamWrite { ty, options } => { |
1338 | current.stream_write(ty, options.into_vec(), types, offset, features) |
1339 | } |
1340 | crate::CanonicalFunction::StreamCancelRead { ty, async_ } => { |
1341 | current.stream_cancel_read(ty, async_, types, offset, features) |
1342 | } |
1343 | crate::CanonicalFunction::StreamCancelWrite { ty, async_ } => { |
1344 | current.stream_cancel_write(ty, async_, types, offset, features) |
1345 | } |
1346 | crate::CanonicalFunction::StreamCloseReadable { ty } => { |
1347 | current.stream_close_readable(ty, types, offset, features) |
1348 | } |
1349 | crate::CanonicalFunction::StreamCloseWritable { ty } => { |
1350 | current.stream_close_writable(ty, types, offset, features) |
1351 | } |
1352 | crate::CanonicalFunction::FutureNew { ty } => { |
1353 | current.future_new(ty, types, offset, features) |
1354 | } |
1355 | crate::CanonicalFunction::FutureRead { ty, options } => { |
1356 | current.future_read(ty, options.into_vec(), types, offset, features) |
1357 | } |
1358 | crate::CanonicalFunction::FutureWrite { ty, options } => { |
1359 | current.future_write(ty, options.into_vec(), types, offset, features) |
1360 | } |
1361 | crate::CanonicalFunction::FutureCancelRead { ty, async_ } => { |
1362 | current.future_cancel_read(ty, async_, types, offset, features) |
1363 | } |
1364 | crate::CanonicalFunction::FutureCancelWrite { ty, async_ } => { |
1365 | current.future_cancel_write(ty, async_, types, offset, features) |
1366 | } |
1367 | crate::CanonicalFunction::FutureCloseReadable { ty } => { |
1368 | current.future_close_readable(ty, types, offset, features) |
1369 | } |
1370 | crate::CanonicalFunction::FutureCloseWritable { ty } => { |
1371 | current.future_close_writable(ty, types, offset, features) |
1372 | } |
1373 | crate::CanonicalFunction::ErrorContextNew { options } => { |
1374 | current.error_context_new(options.into_vec(), types, offset, features) |
1375 | } |
1376 | crate::CanonicalFunction::ErrorContextDebugMessage { options } => current |
1377 | .error_context_debug_message(options.into_vec(), types, offset, features), |
1378 | crate::CanonicalFunction::ErrorContextDrop => { |
1379 | current.error_context_drop(types, offset, features) |
1380 | } |
1381 | } |
1382 | }, |
1383 | ) |
1384 | } |
1385 | |
1386 | /// Validates [`Payload::ComponentStartSection`](crate::Payload). |
1387 | /// |
1388 | /// This method should only be called when parsing a component. |
1389 | #[cfg (feature = "component-model" )] |
1390 | pub fn component_start_section( |
1391 | &mut self, |
1392 | f: &crate::ComponentStartFunction, |
1393 | range: &Range<usize>, |
1394 | ) -> Result<()> { |
1395 | self.state.ensure_component("start" , range.start)?; |
1396 | |
1397 | self.components.last_mut().unwrap().add_start( |
1398 | f.func_index, |
1399 | &f.arguments, |
1400 | f.results, |
1401 | &self.features, |
1402 | &mut self.types, |
1403 | range.start, |
1404 | ) |
1405 | } |
1406 | |
1407 | /// Validates [`Payload::ComponentImportSection`](crate::Payload). |
1408 | /// |
1409 | /// This method should only be called when parsing a component. |
1410 | #[cfg (feature = "component-model" )] |
1411 | pub fn component_import_section( |
1412 | &mut self, |
1413 | section: &crate::ComponentImportSectionReader, |
1414 | ) -> Result<()> { |
1415 | self.process_component_section( |
1416 | section, |
1417 | "import" , |
1418 | |_, _, _, _| Ok(()), // add_import will check limits |
1419 | |components, types, features, import, offset| { |
1420 | components |
1421 | .last_mut() |
1422 | .unwrap() |
1423 | .add_import(import, features, types, offset) |
1424 | }, |
1425 | ) |
1426 | } |
1427 | |
1428 | /// Validates [`Payload::ComponentExportSection`](crate::Payload). |
1429 | /// |
1430 | /// This method should only be called when parsing a component. |
1431 | #[cfg (feature = "component-model" )] |
1432 | pub fn component_export_section( |
1433 | &mut self, |
1434 | section: &crate::ComponentExportSectionReader, |
1435 | ) -> Result<()> { |
1436 | self.process_component_section( |
1437 | section, |
1438 | "export" , |
1439 | |components, _, count, offset| { |
1440 | let current = components.last_mut().unwrap(); |
1441 | check_max( |
1442 | current.exports.len(), |
1443 | count, |
1444 | MAX_WASM_EXPORTS, |
1445 | "exports" , |
1446 | offset, |
1447 | )?; |
1448 | current.exports.reserve(count as usize); |
1449 | Ok(()) |
1450 | }, |
1451 | |components, types, features, export, offset| { |
1452 | let current = components.last_mut().unwrap(); |
1453 | let ty = current.export_to_entity_type(&export, features, types, offset)?; |
1454 | current.add_export( |
1455 | export.name, |
1456 | ty, |
1457 | features, |
1458 | types, |
1459 | offset, |
1460 | false, /* checked above */ |
1461 | ) |
1462 | }, |
1463 | ) |
1464 | } |
1465 | |
1466 | /// Validates [`Payload::UnknownSection`](crate::Payload). |
1467 | /// |
1468 | /// Currently always returns an error. |
1469 | pub fn unknown_section(&mut self, id: u8, range: &Range<usize>) -> Result<()> { |
1470 | Err(format_err!(range.start, "malformed section id: {id}" )) |
1471 | } |
1472 | |
1473 | /// Validates [`Payload::End`](crate::Payload). |
1474 | /// |
1475 | /// Returns the types known to the validator for the module or component. |
1476 | pub fn end(&mut self, offset: usize) -> Result<Types> { |
1477 | match mem::replace(&mut self.state, State::End) { |
1478 | State::Unparsed(_) => Err(BinaryReaderError::new( |
1479 | "cannot call `end` before a header has been parsed" , |
1480 | offset, |
1481 | )), |
1482 | State::End => Err(BinaryReaderError::new( |
1483 | "cannot call `end` after parsing has completed" , |
1484 | offset, |
1485 | )), |
1486 | State::Module => { |
1487 | let mut state = self.module.take().unwrap(); |
1488 | state.validate_end(offset)?; |
1489 | |
1490 | // If there's a parent component, we'll add a module to the parent state |
1491 | // and continue to validate the component |
1492 | #[cfg (feature = "component-model" )] |
1493 | if let Some(parent) = self.components.last_mut() { |
1494 | parent.add_core_module(&state.module, &mut self.types, offset)?; |
1495 | self.state = State::Component; |
1496 | } |
1497 | |
1498 | Ok(Types::from_module( |
1499 | self.id, |
1500 | self.types.commit(), |
1501 | state.module.arc().clone(), |
1502 | )) |
1503 | } |
1504 | #[cfg (feature = "component-model" )] |
1505 | State::Component => { |
1506 | let mut component = self.components.pop().unwrap(); |
1507 | |
1508 | // Validate that all values were used for the component |
1509 | if let Some(index) = component.values.iter().position(|(_, used)| !*used) { |
1510 | bail!( |
1511 | offset, |
1512 | "value index {index} was not used as part of an \ |
1513 | instantiation, start function, or export" |
1514 | ); |
1515 | } |
1516 | |
1517 | // If there's a parent component, pop the stack, add it to the parent, |
1518 | // and continue to validate the component |
1519 | let ty = component.finish(&mut self.types, offset)?; |
1520 | if let Some(parent) = self.components.last_mut() { |
1521 | parent.add_component(ty, &mut self.types)?; |
1522 | self.state = State::Component; |
1523 | } |
1524 | |
1525 | Ok(Types::from_component( |
1526 | self.id, |
1527 | self.types.commit(), |
1528 | component, |
1529 | )) |
1530 | } |
1531 | } |
1532 | } |
1533 | |
1534 | fn process_module_section<'a, T>( |
1535 | &mut self, |
1536 | order: Order, |
1537 | section: &SectionLimited<'a, T>, |
1538 | name: &str, |
1539 | validate_section: impl FnOnce( |
1540 | &mut ModuleState, |
1541 | &WasmFeatures, |
1542 | &mut TypeAlloc, |
1543 | u32, |
1544 | usize, |
1545 | ) -> Result<()>, |
1546 | mut validate_item: impl FnMut( |
1547 | &mut ModuleState, |
1548 | &WasmFeatures, |
1549 | &mut TypeAlloc, |
1550 | T, |
1551 | usize, |
1552 | ) -> Result<()>, |
1553 | ) -> Result<()> |
1554 | where |
1555 | T: FromReader<'a>, |
1556 | { |
1557 | let offset = section.range().start; |
1558 | self.state.ensure_module(name, offset)?; |
1559 | |
1560 | let state = self.module.as_mut().unwrap(); |
1561 | state.update_order(order, offset)?; |
1562 | |
1563 | validate_section( |
1564 | state, |
1565 | &self.features, |
1566 | &mut self.types, |
1567 | section.count(), |
1568 | offset, |
1569 | )?; |
1570 | |
1571 | for item in section.clone().into_iter_with_offsets() { |
1572 | let (offset, item) = item?; |
1573 | validate_item(state, &self.features, &mut self.types, item, offset)?; |
1574 | } |
1575 | |
1576 | Ok(()) |
1577 | } |
1578 | |
1579 | #[cfg (feature = "component-model" )] |
1580 | fn process_component_section<'a, T>( |
1581 | &mut self, |
1582 | section: &SectionLimited<'a, T>, |
1583 | name: &str, |
1584 | validate_section: impl FnOnce( |
1585 | &mut Vec<ComponentState>, |
1586 | &mut TypeAlloc, |
1587 | u32, |
1588 | usize, |
1589 | ) -> Result<()>, |
1590 | mut validate_item: impl FnMut( |
1591 | &mut Vec<ComponentState>, |
1592 | &mut TypeAlloc, |
1593 | &WasmFeatures, |
1594 | T, |
1595 | usize, |
1596 | ) -> Result<()>, |
1597 | ) -> Result<()> |
1598 | where |
1599 | T: FromReader<'a>, |
1600 | { |
1601 | let offset = section.range().start; |
1602 | |
1603 | if !self.features.component_model() { |
1604 | return Err(BinaryReaderError::new( |
1605 | "component model feature is not enabled" , |
1606 | offset, |
1607 | )); |
1608 | } |
1609 | |
1610 | self.state.ensure_component(name, offset)?; |
1611 | validate_section( |
1612 | &mut self.components, |
1613 | &mut self.types, |
1614 | section.count(), |
1615 | offset, |
1616 | )?; |
1617 | |
1618 | for item in section.clone().into_iter_with_offsets() { |
1619 | let (offset, item) = item?; |
1620 | validate_item( |
1621 | &mut self.components, |
1622 | &mut self.types, |
1623 | &self.features, |
1624 | item, |
1625 | offset, |
1626 | )?; |
1627 | } |
1628 | |
1629 | Ok(()) |
1630 | } |
1631 | } |
1632 | |
1633 | #[cfg (test)] |
1634 | mod tests { |
1635 | use crate::{GlobalType, MemoryType, RefType, TableType, ValType, Validator, WasmFeatures}; |
1636 | use anyhow::Result; |
1637 | |
1638 | #[test ] |
1639 | fn test_module_type_information() -> Result<()> { |
1640 | let bytes = wat::parse_str( |
1641 | r#" |
1642 | (module |
1643 | (type (func (param i32 i64) (result i32))) |
1644 | (memory 1 5) |
1645 | (table 10 funcref) |
1646 | (global (mut i32) (i32.const 0)) |
1647 | (func (type 0) (i32.const 0)) |
1648 | (tag (param i64 i32)) |
1649 | (elem funcref (ref.func 0)) |
1650 | ) |
1651 | "# , |
1652 | )?; |
1653 | |
1654 | let mut validator = |
1655 | Validator::new_with_features(WasmFeatures::default() | WasmFeatures::EXCEPTIONS); |
1656 | |
1657 | let types = validator.validate_all(&bytes)?; |
1658 | let types = types.as_ref(); |
1659 | |
1660 | assert_eq!(types.core_type_count_in_module(), 2); |
1661 | assert_eq!(types.memory_count(), 1); |
1662 | assert_eq!(types.table_count(), 1); |
1663 | assert_eq!(types.global_count(), 1); |
1664 | assert_eq!(types.function_count(), 1); |
1665 | assert_eq!(types.tag_count(), 1); |
1666 | assert_eq!(types.element_count(), 1); |
1667 | assert_eq!(types.module_count(), 0); |
1668 | assert_eq!(types.component_count(), 0); |
1669 | assert_eq!(types.core_instance_count(), 0); |
1670 | assert_eq!(types.value_count(), 0); |
1671 | |
1672 | let id = types.core_type_at_in_module(0); |
1673 | let ty = types[id].unwrap_func(); |
1674 | assert_eq!(ty.params(), [ValType::I32, ValType::I64]); |
1675 | assert_eq!(ty.results(), [ValType::I32]); |
1676 | |
1677 | let id = types.core_type_at_in_module(1); |
1678 | let ty = types[id].unwrap_func(); |
1679 | assert_eq!(ty.params(), [ValType::I64, ValType::I32]); |
1680 | assert_eq!(ty.results(), []); |
1681 | |
1682 | assert_eq!( |
1683 | types.memory_at(0), |
1684 | MemoryType { |
1685 | memory64: false, |
1686 | shared: false, |
1687 | initial: 1, |
1688 | maximum: Some(5), |
1689 | page_size_log2: None, |
1690 | } |
1691 | ); |
1692 | |
1693 | assert_eq!( |
1694 | types.table_at(0), |
1695 | TableType { |
1696 | initial: 10, |
1697 | maximum: None, |
1698 | element_type: RefType::FUNCREF, |
1699 | table64: false, |
1700 | shared: false, |
1701 | } |
1702 | ); |
1703 | |
1704 | assert_eq!( |
1705 | types.global_at(0), |
1706 | GlobalType { |
1707 | content_type: ValType::I32, |
1708 | mutable: true, |
1709 | shared: false |
1710 | } |
1711 | ); |
1712 | |
1713 | let id = types.core_function_at(0); |
1714 | let ty = types[id].unwrap_func(); |
1715 | assert_eq!(ty.params(), [ValType::I32, ValType::I64]); |
1716 | assert_eq!(ty.results(), [ValType::I32]); |
1717 | |
1718 | let ty = types.tag_at(0); |
1719 | let ty = types[ty].unwrap_func(); |
1720 | assert_eq!(ty.params(), [ValType::I64, ValType::I32]); |
1721 | assert_eq!(ty.results(), []); |
1722 | |
1723 | assert_eq!(types.element_at(0), RefType::FUNCREF); |
1724 | |
1725 | Ok(()) |
1726 | } |
1727 | |
1728 | #[test ] |
1729 | fn test_type_id_aliasing() -> Result<()> { |
1730 | let bytes = wat::parse_str( |
1731 | r#" |
1732 | (component |
1733 | (type $T (list string)) |
1734 | (alias outer 0 $T (type $A1)) |
1735 | (alias outer 0 $T (type $A2)) |
1736 | ) |
1737 | "# , |
1738 | )?; |
1739 | |
1740 | let mut validator = |
1741 | Validator::new_with_features(WasmFeatures::default() | WasmFeatures::COMPONENT_MODEL); |
1742 | |
1743 | let types = validator.validate_all(&bytes)?; |
1744 | let types = types.as_ref(); |
1745 | |
1746 | let t_id = types.component_defined_type_at(0); |
1747 | let a1_id = types.component_defined_type_at(1); |
1748 | let a2_id = types.component_defined_type_at(2); |
1749 | |
1750 | // The ids should all be the same |
1751 | assert!(t_id == a1_id); |
1752 | assert!(t_id == a2_id); |
1753 | assert!(a1_id == a2_id); |
1754 | |
1755 | // However, they should all point to the same type |
1756 | assert!(std::ptr::eq(&types[t_id], &types[a1_id],)); |
1757 | assert!(std::ptr::eq(&types[t_id], &types[a2_id],)); |
1758 | |
1759 | Ok(()) |
1760 | } |
1761 | |
1762 | #[test ] |
1763 | fn test_type_id_exports() -> Result<()> { |
1764 | let bytes = wat::parse_str( |
1765 | r#" |
1766 | (component |
1767 | (type $T (list string)) |
1768 | (export $A1 "A1" (type $T)) |
1769 | (export $A2 "A2" (type $T)) |
1770 | ) |
1771 | "# , |
1772 | )?; |
1773 | |
1774 | let mut validator = |
1775 | Validator::new_with_features(WasmFeatures::default() | WasmFeatures::COMPONENT_MODEL); |
1776 | |
1777 | let types = validator.validate_all(&bytes)?; |
1778 | let types = types.as_ref(); |
1779 | |
1780 | let t_id = types.component_defined_type_at(0); |
1781 | let a1_id = types.component_defined_type_at(1); |
1782 | let a2_id = types.component_defined_type_at(2); |
1783 | |
1784 | // The ids should all be the same |
1785 | assert!(t_id != a1_id); |
1786 | assert!(t_id != a2_id); |
1787 | assert!(a1_id != a2_id); |
1788 | |
1789 | // However, they should all point to the same type |
1790 | assert!(std::ptr::eq(&types[t_id], &types[a1_id],)); |
1791 | assert!(std::ptr::eq(&types[t_id], &types[a2_id],)); |
1792 | |
1793 | Ok(()) |
1794 | } |
1795 | } |
1796 | |