1 | use super::operators::{Frame, OperatorValidator, OperatorValidatorAllocations}; |
2 | use crate::{BinaryReader, Result, ValType, VisitOperator}; |
3 | use 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)] |
13 | pub 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 | |
25 | impl<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. |
60 | pub 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)] |
73 | pub struct FuncValidatorAllocations(OperatorValidatorAllocations); |
74 | |
75 | impl<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)] |
283 | mod 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 | |