1use super::operators::{Frame, OperatorValidator, OperatorValidatorAllocations};
2use crate::{BinaryReader, Result, ValType, VisitOperator};
3use crate::{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
66/// External handle to the internal allocations used during function validation.
67///
68/// This is created with either the `Default` implementation or with
69/// [`FuncValidator::into_allocations`]. It is then passed as an argument to
70/// [`FuncToValidate::into_validator`] to provide a means of reusing allocations
71/// between each function.
72#[derive(Default)]
73pub struct FuncValidatorAllocations(OperatorValidatorAllocations);
74
75impl<T: WasmModuleResources> FuncValidator<T> {
76 /// Convenience function to validate an entire function's body.
77 ///
78 /// You may not end up using this in final implementations because you'll
79 /// often want to interleave validation with parsing.
80 pub fn validate(&mut self, body: &FunctionBody<'_>) -> Result<()> {
81 let mut reader = body.get_binary_reader();
82 self.read_locals(&mut reader)?;
83 #[cfg(feature = "features")]
84 {
85 reader.set_features(self.validator.features);
86 }
87 while !reader.eof() {
88 // In a debug build, verify that the validator's pops and pushes to and from
89 // the operand stack match the operator's arity.
90 #[cfg(debug_assertions)]
91 let (pop_push_snapshot, arity) = (
92 self.validator.pop_push_count,
93 reader
94 .clone()
95 .read_operator()?
96 .operator_arity(&self.visitor(reader.original_position())),
97 );
98
99 reader.visit_operator(&mut self.visitor(reader.original_position()))??;
100
101 #[cfg(debug_assertions)]
102 {
103 let (params, results) = arity.ok_or(format_err!(
104 reader.original_position(),
105 "could not calculate operator arity"
106 ))?;
107
108 let pop_count = self.validator.pop_push_count.0 - pop_push_snapshot.0;
109 let push_count = self.validator.pop_push_count.1 - pop_push_snapshot.1;
110
111 if pop_count != params || push_count != results {
112 panic!("arity mismatch in validation. Expecting {} operands popped, {} pushed, but got {} popped, {} pushed.",
113 params, results, pop_count, push_count);
114 }
115 }
116 }
117 self.finish(reader.original_position())
118 }
119
120 /// Reads the local definitions from the given `BinaryReader`, often sourced
121 /// from a `FunctionBody`.
122 ///
123 /// This function will automatically advance the `BinaryReader` forward,
124 /// leaving reading operators up to the caller afterwards.
125 pub fn read_locals(&mut self, reader: &mut BinaryReader<'_>) -> Result<()> {
126 for _ in 0..reader.read_var_u32()? {
127 let offset = reader.original_position();
128 let cnt = reader.read()?;
129 let ty = reader.read()?;
130 self.define_locals(offset, cnt, ty)?;
131 }
132 Ok(())
133 }
134
135 /// Defines locals into this validator.
136 ///
137 /// This should be used if the application is already reading local
138 /// definitions and there's no need to re-parse the function again.
139 pub fn define_locals(&mut self, offset: usize, count: u32, ty: ValType) -> Result<()> {
140 self.validator
141 .define_locals(offset, count, ty, &self.resources)
142 }
143
144 /// Validates the next operator in a function.
145 ///
146 /// This functions is expected to be called once-per-operator in a
147 /// WebAssembly function. Each operator's offset in the original binary and
148 /// the operator itself are passed to this function to provide more useful
149 /// error messages.
150 pub fn op(&mut self, offset: usize, operator: &Operator<'_>) -> Result<()> {
151 self.visitor(offset).visit_operator(operator)
152 }
153
154 /// Get the operator visitor for the next operator in the function.
155 ///
156 /// The returned visitor is intended to visit just one instruction at the `offset`.
157 ///
158 /// # Example
159 ///
160 /// ```
161 /// # use wasmparser::{WasmModuleResources, FuncValidator, FunctionBody, Result};
162 /// pub fn validate<R>(validator: &mut FuncValidator<R>, body: &FunctionBody<'_>) -> Result<()>
163 /// where R: WasmModuleResources
164 /// {
165 /// let mut operator_reader = body.get_binary_reader();
166 /// while !operator_reader.eof() {
167 /// let mut visitor = validator.visitor(operator_reader.original_position());
168 /// operator_reader.visit_operator(&mut visitor)??;
169 /// }
170 /// validator.finish(operator_reader.original_position())
171 /// }
172 /// ```
173 pub fn visitor<'this, 'a: 'this>(
174 &'this mut self,
175 offset: usize,
176 ) -> impl VisitOperator<'a, Output = Result<()>> + ModuleArity + 'this {
177 self.validator.with_resources(&self.resources, offset)
178 }
179
180 /// Same as [`FuncValidator::visit`] except that the returned type
181 /// implements the [`VisitSimdOperator`](crate::VisitSimdOperator) trait as
182 /// well.
183 #[cfg(feature = "simd")]
184 pub fn simd_visitor<'this, 'a: 'this>(
185 &'this mut self,
186 offset: usize,
187 ) -> impl crate::VisitSimdOperator<'a, Output = Result<()>> + ModuleArity + 'this {
188 self.validator.with_resources_simd(&self.resources, offset)
189 }
190
191 /// Function that must be called after the last opcode has been processed.
192 ///
193 /// This will validate that the function was properly terminated with the
194 /// `end` opcode. If this function is not called then the function will not
195 /// be properly validated.
196 ///
197 /// The `offset` provided to this function will be used as a position for an
198 /// error if validation fails.
199 pub fn finish(&mut self, offset: usize) -> Result<()> {
200 self.validator.finish(offset)
201 }
202
203 /// Returns the Wasm features enabled for this validator.
204 pub fn features(&self) -> &WasmFeatures {
205 &self.validator.features
206 }
207
208 /// Returns the underlying module resources that this validator is using.
209 pub fn resources(&self) -> &T {
210 &self.resources
211 }
212
213 /// The index of the function within the module's function index space that
214 /// is being validated.
215 pub fn index(&self) -> u32 {
216 self.index
217 }
218
219 /// Returns the number of defined local variables in the function.
220 pub fn len_locals(&self) -> u32 {
221 self.validator.locals.len_locals()
222 }
223
224 /// Returns the type of the local variable at the given `index` if any.
225 pub fn get_local_type(&self, index: u32) -> Option<ValType> {
226 self.validator.locals.get(index)
227 }
228
229 /// Get the current height of the operand stack.
230 ///
231 /// This returns the height of the whole operand stack for this function,
232 /// not just for the current control frame.
233 pub fn operand_stack_height(&self) -> u32 {
234 self.validator.operand_stack_height() as u32
235 }
236
237 /// Returns the optional value type of the value operand at the given
238 /// `depth` from the top of the operand stack.
239 ///
240 /// - Returns `None` if the `depth` is out of bounds.
241 /// - Returns `Some(None)` if there is a value with unknown type
242 /// at the given `depth`.
243 ///
244 /// # Note
245 ///
246 /// A `depth` of 0 will refer to the last operand on the stack.
247 pub fn get_operand_type(&self, depth: usize) -> Option<Option<ValType>> {
248 self.validator.peek_operand_at(depth)
249 }
250
251 /// Returns the number of frames on the control flow stack.
252 ///
253 /// This returns the height of the whole control stack for this function,
254 /// not just for the current control frame.
255 pub fn control_stack_height(&self) -> u32 {
256 self.validator.control_stack_height() as u32
257 }
258
259 /// Returns a shared reference to the control flow [`Frame`] of the
260 /// control flow stack at the given `depth` if any.
261 ///
262 /// Returns `None` if the `depth` is out of bounds.
263 ///
264 /// # Note
265 ///
266 /// A `depth` of 0 will refer to the last frame on the stack.
267 pub fn get_control_frame(&self, depth: usize) -> Option<&Frame> {
268 self.validator.get_frame(depth)
269 }
270
271 /// Consumes this validator and returns the underlying allocations that
272 /// were used during the validation process.
273 ///
274 /// The returned value here can be paired with
275 /// [`FuncToValidate::into_validator`] to reuse the allocations already
276 /// created by this validator.
277 pub fn into_allocations(self) -> FuncValidatorAllocations {
278 FuncValidatorAllocations(self.validator.into_allocations())
279 }
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285 use crate::types::CoreTypeId;
286 use crate::{HeapType, RefType};
287
288 struct EmptyResources(crate::SubType);
289
290 impl Default for EmptyResources {
291 fn default() -> Self {
292 EmptyResources(crate::SubType {
293 supertype_idx: None,
294 is_final: true,
295 composite_type: crate::CompositeType {
296 inner: crate::CompositeInnerType::Func(crate::FuncType::new([], [])),
297 shared: false,
298 },
299 })
300 }
301 }
302
303 impl WasmModuleResources for EmptyResources {
304 fn table_at(&self, _at: u32) -> Option<crate::TableType> {
305 todo!()
306 }
307 fn memory_at(&self, _at: u32) -> Option<crate::MemoryType> {
308 todo!()
309 }
310 fn tag_at(&self, _at: u32) -> Option<&crate::FuncType> {
311 todo!()
312 }
313 fn global_at(&self, _at: u32) -> Option<crate::GlobalType> {
314 todo!()
315 }
316 fn sub_type_at(&self, _type_idx: u32) -> Option<&crate::SubType> {
317 Some(&self.0)
318 }
319 fn sub_type_at_id(&self, _id: CoreTypeId) -> &crate::SubType {
320 todo!()
321 }
322 fn type_id_of_function(&self, _at: u32) -> Option<CoreTypeId> {
323 todo!()
324 }
325 fn type_index_of_function(&self, _at: u32) -> Option<u32> {
326 todo!()
327 }
328 fn check_heap_type(&self, _t: &mut HeapType, _offset: usize) -> Result<()> {
329 Ok(())
330 }
331 fn top_type(&self, _heap_type: &HeapType) -> HeapType {
332 todo!()
333 }
334 fn element_type_at(&self, _at: u32) -> Option<crate::RefType> {
335 todo!()
336 }
337 fn is_subtype(&self, _t1: ValType, _t2: ValType) -> bool {
338 todo!()
339 }
340 fn is_shared(&self, _ty: RefType) -> bool {
341 todo!()
342 }
343 fn element_count(&self) -> u32 {
344 todo!()
345 }
346 fn data_count(&self) -> Option<u32> {
347 todo!()
348 }
349 fn is_function_referenced(&self, _idx: u32) -> bool {
350 todo!()
351 }
352 }
353
354 #[test]
355 fn operand_stack_height() {
356 let mut v = FuncToValidate {
357 index: 0,
358 ty: 0,
359 resources: EmptyResources::default(),
360 features: Default::default(),
361 }
362 .into_validator(Default::default());
363
364 // Initially zero values on the stack.
365 assert_eq!(v.operand_stack_height(), 0);
366
367 // Pushing a constant value makes use have one value on the stack.
368 assert!(v.op(0, &Operator::I32Const { value: 0 }).is_ok());
369 assert_eq!(v.operand_stack_height(), 1);
370
371 // Entering a new control block does not affect the stack height.
372 assert!(v
373 .op(
374 1,
375 &Operator::Block {
376 blockty: crate::BlockType::Empty
377 }
378 )
379 .is_ok());
380 assert_eq!(v.operand_stack_height(), 1);
381
382 // Pushing another constant value makes use have two values on the stack.
383 assert!(v.op(2, &Operator::I32Const { value: 99 }).is_ok());
384 assert_eq!(v.operand_stack_height(), 2);
385 }
386}
387