wasmparser/validator/func.rs
1use super::operators::{Frame, OperatorValidator, OperatorValidatorAllocations};
2use crate::{BinaryReader, Result, ValType, VisitOperator};
3use crate::{FrameStack, FunctionBody, ModuleArity, Operator, WasmFeatures, WasmModuleResources};
4
5/// Resources necessary to perform validation of a function.
6///
7/// This structure is created by
8/// [`Validator::code_section_entry`](crate::Validator::code_section_entry) and
9/// is created per-function in a WebAssembly module. This structure is suitable
10/// for sending to other threads while the original
11/// [`Validator`](crate::Validator) continues processing other functions.
12#[derive(Debug)]
13pub struct FuncToValidate<T> {
14 /// Reusable, heap allocated resources to drive the Wasm validation.
15 pub resources: T,
16 /// The core Wasm function index being validated.
17 pub index: u32,
18 /// The core Wasm type index of the function being validated,
19 /// defining the results and parameters to the function.
20 pub ty: u32,
21 /// The Wasm features enabled to validate the function.
22 pub features: WasmFeatures,
23}
24
25impl<T: WasmModuleResources> FuncToValidate<T> {
26 /// Converts this [`FuncToValidate`] into a [`FuncValidator`] using the
27 /// `allocs` provided.
28 ///
29 /// This method, in conjunction with [`FuncValidator::into_allocations`],
30 /// provides a means to reuse allocations across validation of each
31 /// individual function. Note that it is also sufficient to call this
32 /// method with `Default::default()` if no prior allocations are
33 /// available.
34 ///
35 /// # Panics
36 ///
37 /// If a `FuncToValidate` was created with an invalid `ty` index then this
38 /// function will panic.
39 pub fn into_validator(self, allocs: FuncValidatorAllocations) -> FuncValidator<T> {
40 let FuncToValidate {
41 resources,
42 index,
43 ty,
44 features,
45 } = self;
46 let validator =
47 OperatorValidator::new_func(ty, 0, &features, &resources, allocs.0).unwrap();
48 FuncValidator {
49 validator,
50 resources,
51 index,
52 }
53 }
54}
55
56/// Validation context for a WebAssembly function.
57///
58/// This is a finalized validator which is ready to process a [`FunctionBody`].
59/// This is created from the [`FuncToValidate::into_validator`] method.
60pub struct FuncValidator<T> {
61 validator: OperatorValidator,
62 resources: T,
63 index: u32,
64}
65
66impl<T: WasmModuleResources> ModuleArity for FuncValidator<T> {
67 fn sub_type_at(&self, type_idx: u32) -> Option<&crate::SubType> {
68 self.resources.sub_type_at(type_idx)
69 }
70
71 fn tag_type_arity(&self, at: u32) -> Option<(u32, u32)> {
72 let ty = self.resources.tag_at(at)?;
73 Some((
74 u32::try_from(ty.params().len()).unwrap(),
75 u32::try_from(ty.results().len()).unwrap(),
76 ))
77 }
78
79 fn type_index_of_function(&self, func_idx: u32) -> Option<u32> {
80 self.resources.type_index_of_function(func_idx)
81 }
82
83 fn func_type_of_cont_type(&self, cont_ty: &crate::ContType) -> Option<&crate::FuncType> {
84 let id = cont_ty.0.as_core_type_id()?;
85 Some(self.resources.sub_type_at_id(id).unwrap_func())
86 }
87
88 fn sub_type_of_ref_type(&self, rt: &crate::RefType) -> Option<&crate::SubType> {
89 let id = rt.type_index()?.as_core_type_id()?;
90 Some(self.resources.sub_type_at_id(id))
91 }
92
93 fn control_stack_height(&self) -> u32 {
94 u32::try_from(self.validator.control_stack_height()).unwrap()
95 }
96
97 fn label_block(&self, depth: u32) -> Option<(crate::BlockType, crate::FrameKind)> {
98 self.validator.jump(depth)
99 }
100}
101
102/// External handle to the internal allocations used during function validation.
103///
104/// This is created with either the `Default` implementation or with
105/// [`FuncValidator::into_allocations`]. It is then passed as an argument to
106/// [`FuncToValidate::into_validator`] to provide a means of reusing allocations
107/// between each function.
108#[derive(Default)]
109pub struct FuncValidatorAllocations(OperatorValidatorAllocations);
110
111impl<T: WasmModuleResources> FuncValidator<T> {
112 /// Convenience function to validate an entire function's body.
113 ///
114 /// You may not end up using this in final implementations because you'll
115 /// often want to interleave validation with parsing.
116 pub fn validate(&mut self, body: &FunctionBody<'_>) -> Result<()> {
117 let mut reader = body.get_binary_reader();
118 self.read_locals(&mut reader)?;
119 #[cfg(feature = "features")]
120 {
121 reader.set_features(self.validator.features);
122 }
123 while !reader.eof() {
124 // In a debug build, verify that the validator's pops and pushes to and from
125 // the operand stack match the operator's arity.
126 #[cfg(debug_assertions)]
127 let (ops_before, arity) = {
128 let op = reader.peek_operator(&self.visitor(reader.original_position()))?;
129 let arity = op.operator_arity(&self.visitor(reader.original_position()));
130 (reader.clone(), arity)
131 };
132
133 reader.visit_operator(&mut self.visitor(reader.original_position()))??;
134
135 #[cfg(debug_assertions)]
136 {
137 let (params, results) = arity.ok_or(format_err!(
138 reader.original_position(),
139 "could not calculate operator arity"
140 ))?;
141
142 // Analyze the log to determine the actual, externally visible
143 // pop/push count. This allows us to hide the fact that we might
144 // push and then pop a temporary while validating an
145 // instruction, which shouldn't be visible from the outside.
146 let mut pop_count = 0;
147 let mut push_count = 0;
148 for op in self.validator.pop_push_log.drain(..) {
149 match op {
150 true => push_count += 1,
151 false if push_count > 0 => push_count -= 1,
152 false => pop_count += 1,
153 }
154 }
155
156 if pop_count != params || push_count != results {
157 panic!(
158 "\
159arity mismatch in validation
160 operator: {:?}
161 expected: {params} -> {results}
162 got {pop_count} -> {push_count}",
163 ops_before.peek_operator(&self.visitor(ops_before.original_position()))?,
164 );
165 }
166 }
167 }
168 reader.finish_expression(&self.visitor(reader.original_position()))
169 }
170
171 /// Reads the local definitions from the given `BinaryReader`, often sourced
172 /// from a `FunctionBody`.
173 ///
174 /// This function will automatically advance the `BinaryReader` forward,
175 /// leaving reading operators up to the caller afterwards.
176 pub fn read_locals(&mut self, reader: &mut BinaryReader<'_>) -> Result<()> {
177 for _ in 0..reader.read_var_u32()? {
178 let offset = reader.original_position();
179 let cnt = reader.read()?;
180 let ty = reader.read()?;
181 self.define_locals(offset, cnt, ty)?;
182 }
183 Ok(())
184 }
185
186 /// Defines locals into this validator.
187 ///
188 /// This should be used if the application is already reading local
189 /// definitions and there's no need to re-parse the function again.
190 pub fn define_locals(&mut self, offset: usize, count: u32, ty: ValType) -> Result<()> {
191 self.validator
192 .define_locals(offset, count, ty, &self.resources)
193 }
194
195 /// Validates the next operator in a function.
196 ///
197 /// This functions is expected to be called once-per-operator in a
198 /// WebAssembly function. Each operator's offset in the original binary and
199 /// the operator itself are passed to this function to provide more useful
200 /// error messages.
201 pub fn op(&mut self, offset: usize, operator: &Operator<'_>) -> Result<()> {
202 self.visitor(offset).visit_operator(operator)
203 }
204
205 /// Get the operator visitor for the next operator in the function.
206 ///
207 /// The returned visitor is intended to visit just one instruction at the `offset`.
208 ///
209 /// # Example
210 ///
211 /// ```
212 /// # use wasmparser::{WasmModuleResources, FuncValidator, FunctionBody, Result};
213 /// pub fn validate<R>(validator: &mut FuncValidator<R>, body: &FunctionBody<'_>) -> Result<()>
214 /// where R: WasmModuleResources
215 /// {
216 /// let mut operator_reader = body.get_binary_reader_for_operators()?;
217 /// while !operator_reader.eof() {
218 /// let mut visitor = validator.visitor(operator_reader.original_position());
219 /// operator_reader.visit_operator(&mut visitor)??;
220 /// }
221 /// operator_reader.finish_expression(&validator.visitor(operator_reader.original_position()))
222 /// }
223 /// ```
224 pub fn visitor<'this, 'a: 'this>(
225 &'this mut self,
226 offset: usize,
227 ) -> impl VisitOperator<'a, Output = Result<()>> + ModuleArity + FrameStack + 'this {
228 self.validator.with_resources(&self.resources, offset)
229 }
230
231 /// Same as [`FuncValidator::visitor`] except that the returned type
232 /// implements the [`VisitSimdOperator`](crate::VisitSimdOperator) trait as
233 /// well.
234 #[cfg(feature = "simd")]
235 pub fn simd_visitor<'this, 'a: 'this>(
236 &'this mut self,
237 offset: usize,
238 ) -> impl crate::VisitSimdOperator<'a, Output = Result<()>> + ModuleArity + 'this {
239 self.validator.with_resources_simd(&self.resources, offset)
240 }
241
242 /// Returns the Wasm features enabled for this validator.
243 pub fn features(&self) -> &WasmFeatures {
244 &self.validator.features
245 }
246
247 /// Returns the underlying module resources that this validator is using.
248 pub fn resources(&self) -> &T {
249 &self.resources
250 }
251
252 /// The index of the function within the module's function index space that
253 /// is being validated.
254 pub fn index(&self) -> u32 {
255 self.index
256 }
257
258 /// Returns the number of defined local variables in the function.
259 pub fn len_locals(&self) -> u32 {
260 self.validator.locals.len_locals()
261 }
262
263 /// Returns the type of the local variable at the given `index` if any.
264 pub fn get_local_type(&self, index: u32) -> Option<ValType> {
265 self.validator.locals.get(index)
266 }
267
268 /// Get the current height of the operand stack.
269 ///
270 /// This returns the height of the whole operand stack for this function,
271 /// not just for the current control frame.
272 pub fn operand_stack_height(&self) -> u32 {
273 self.validator.operand_stack_height() as u32
274 }
275
276 /// Returns the optional value type of the value operand at the given
277 /// `depth` from the top of the operand stack.
278 ///
279 /// - Returns `None` if the `depth` is out of bounds.
280 /// - Returns `Some(None)` if there is a value with unknown type
281 /// at the given `depth`.
282 ///
283 /// # Note
284 ///
285 /// A `depth` of 0 will refer to the last operand on the stack.
286 pub fn get_operand_type(&self, depth: usize) -> Option<Option<ValType>> {
287 self.validator.peek_operand_at(depth)
288 }
289
290 /// Returns the number of frames on the control flow stack.
291 ///
292 /// This returns the height of the whole control stack for this function,
293 /// not just for the current control frame.
294 pub fn control_stack_height(&self) -> u32 {
295 self.validator.control_stack_height() as u32
296 }
297
298 /// Returns a shared reference to the control flow [`Frame`] of the
299 /// control flow stack at the given `depth` if any.
300 ///
301 /// Returns `None` if the `depth` is out of bounds.
302 ///
303 /// # Note
304 ///
305 /// A `depth` of 0 will refer to the last frame on the stack.
306 pub fn get_control_frame(&self, depth: usize) -> Option<&Frame> {
307 self.validator.get_frame(depth)
308 }
309
310 /// Consumes this validator and returns the underlying allocations that
311 /// were used during the validation process.
312 ///
313 /// The returned value here can be paired with
314 /// [`FuncToValidate::into_validator`] to reuse the allocations already
315 /// created by this validator.
316 pub fn into_allocations(self) -> FuncValidatorAllocations {
317 FuncValidatorAllocations(self.validator.into_allocations())
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324 use crate::types::CoreTypeId;
325 use crate::{HeapType, Parser, RefType, Validator};
326 use alloc::vec::Vec;
327
328 struct EmptyResources(crate::SubType);
329
330 impl Default for EmptyResources {
331 fn default() -> Self {
332 EmptyResources(crate::SubType {
333 supertype_idx: None,
334 is_final: true,
335 composite_type: crate::CompositeType {
336 inner: crate::CompositeInnerType::Func(crate::FuncType::new([], [])),
337 shared: false,
338 descriptor_idx: None,
339 describes_idx: None,
340 },
341 })
342 }
343 }
344
345 impl WasmModuleResources for EmptyResources {
346 fn table_at(&self, _at: u32) -> Option<crate::TableType> {
347 todo!()
348 }
349 fn memory_at(&self, _at: u32) -> Option<crate::MemoryType> {
350 todo!()
351 }
352 fn tag_at(&self, _at: u32) -> Option<&crate::FuncType> {
353 todo!()
354 }
355 fn global_at(&self, _at: u32) -> Option<crate::GlobalType> {
356 todo!()
357 }
358 fn sub_type_at(&self, _type_idx: u32) -> Option<&crate::SubType> {
359 Some(&self.0)
360 }
361 fn sub_type_at_id(&self, _id: CoreTypeId) -> &crate::SubType {
362 todo!()
363 }
364 fn type_id_of_function(&self, _at: u32) -> Option<CoreTypeId> {
365 todo!()
366 }
367 fn type_index_of_function(&self, _at: u32) -> Option<u32> {
368 todo!()
369 }
370 fn check_heap_type(&self, _t: &mut HeapType, _offset: usize) -> Result<()> {
371 Ok(())
372 }
373 fn top_type(&self, _heap_type: &HeapType) -> HeapType {
374 todo!()
375 }
376 fn element_type_at(&self, _at: u32) -> Option<crate::RefType> {
377 todo!()
378 }
379 fn is_subtype(&self, _t1: ValType, _t2: ValType) -> bool {
380 todo!()
381 }
382 fn is_shared(&self, _ty: RefType) -> bool {
383 todo!()
384 }
385 fn element_count(&self) -> u32 {
386 todo!()
387 }
388 fn data_count(&self) -> Option<u32> {
389 todo!()
390 }
391 fn is_function_referenced(&self, _idx: u32) -> bool {
392 todo!()
393 }
394 fn has_function_exact_type(&self, _idx: u32) -> bool {
395 todo!()
396 }
397 }
398
399 #[test]
400 fn operand_stack_height() {
401 let mut v = FuncToValidate {
402 index: 0,
403 ty: 0,
404 resources: EmptyResources::default(),
405 features: Default::default(),
406 }
407 .into_validator(Default::default());
408
409 // Initially zero values on the stack.
410 assert_eq!(v.operand_stack_height(), 0);
411
412 // Pushing a constant value makes use have one value on the stack.
413 assert!(v.op(0, &Operator::I32Const { value: 0 }).is_ok());
414 assert_eq!(v.operand_stack_height(), 1);
415
416 // Entering a new control block does not affect the stack height.
417 assert!(
418 v.op(
419 1,
420 &Operator::Block {
421 blockty: crate::BlockType::Empty
422 }
423 )
424 .is_ok()
425 );
426 assert_eq!(v.operand_stack_height(), 1);
427
428 // Pushing another constant value makes use have two values on the stack.
429 assert!(v.op(2, &Operator::I32Const { value: 99 }).is_ok());
430 assert_eq!(v.operand_stack_height(), 2);
431 }
432
433 fn assert_arity(wat: &str, expected: Vec<Vec<(u32, u32)>>) {
434 let wasm = wat::parse_str(wat).unwrap();
435 assert!(Validator::new().validate_all(&wasm).is_ok());
436
437 let parser = Parser::new(0);
438 let mut validator = Validator::new();
439
440 let mut actual = vec![];
441
442 for payload in parser.parse_all(&wasm) {
443 let payload = payload.unwrap();
444 match payload {
445 crate::Payload::CodeSectionEntry(body) => {
446 let mut arity = vec![];
447 let mut func_validator = validator
448 .code_section_entry(&body)
449 .unwrap()
450 .into_validator(FuncValidatorAllocations::default());
451 let ops = body.get_operators_reader().unwrap();
452 for op in ops.into_iter() {
453 let op = op.unwrap();
454 arity.push(
455 op.operator_arity(&func_validator)
456 .expect("valid operators should have arity"),
457 );
458 func_validator.op(usize::MAX, &op).expect("should be valid");
459 }
460 actual.push(arity);
461 }
462 p => {
463 validator.payload(&p).unwrap();
464 }
465 }
466 }
467
468 assert_eq!(actual, expected);
469 }
470
471 #[test]
472 fn arity_smoke_test() {
473 let wasm = r#"
474 (module
475 (type $pair (struct (field i32) (field i32)))
476
477 (func $add (param i32 i32) (result i32)
478 local.get 0
479 local.get 1
480 i32.add
481 )
482
483 (func $f (param i32 i32) (result (ref null $pair))
484 local.get 0
485 local.get 1
486 call $add
487 if (result (ref null $pair))
488 local.get 0
489 local.get 1
490 struct.new $pair
491 else
492 unreachable
493 i32.add
494 unreachable
495 end
496 )
497 )
498 "#;
499
500 assert_arity(
501 wasm,
502 vec![
503 // $add
504 vec![
505 // local.get 0
506 (0, 1),
507 // local.get 1
508 (0, 1),
509 // i32.add
510 (2, 1),
511 // end
512 (1, 1),
513 ],
514 // $f
515 vec![
516 // local.get 0
517 (0, 1),
518 // local.get 1
519 (0, 1),
520 // call $add
521 (2, 1),
522 // if
523 (1, 0),
524 // local.get 0
525 (0, 1),
526 // local.get 1
527 (0, 1),
528 // struct.new $pair
529 (2, 1),
530 // else
531 (1, 0),
532 // unreachable,
533 (0, 0),
534 // i32.add
535 (2, 1),
536 // unreachable
537 (0, 0),
538 // end
539 (1, 1),
540 // implicit end
541 (1, 1),
542 ],
543 ],
544 );
545 }
546
547 #[test]
548 fn arity_if_no_else_same_params_and_results() {
549 let wasm = r#"
550 (module
551 (func (export "f") (param i64 i32) (result i64)
552 (local.get 0)
553 (local.get 1)
554 ;; If with no else. Same number of params and results.
555 if (param i64) (result i64)
556 drop
557 i64.const -1
558 end
559 )
560 )
561 "#;
562
563 assert_arity(
564 wasm,
565 vec![vec![
566 // local.get 0
567 (0, 1),
568 // local.get 1
569 (0, 1),
570 // if
571 (2, 1),
572 // drop
573 (1, 0),
574 // i64.const -1
575 (0, 1),
576 // end
577 (1, 1),
578 // implicit end
579 (1, 1),
580 ]],
581 );
582 }
583
584 #[test]
585 fn arity_br_table() {
586 let wasm = r#"
587 (module
588 (func (export "f") (result i32 i32)
589 i32.const 0
590 i32.const 1
591 i32.const 2
592 br_table 0 0
593 )
594 )
595 "#;
596
597 assert_arity(
598 wasm,
599 vec![vec![
600 // i32.const 0
601 (0, 1),
602 // i32.const 1
603 (0, 1),
604 // i32.const 2
605 (0, 1),
606 // br_table
607 (3, 0),
608 // implicit end
609 (2, 2),
610 ]],
611 );
612 }
613}