| 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 | |