| /* | |
| * Copyright 2018 Google Inc. All rights reserved. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| use crate::reflection_generated::reflection::{BaseType, Field, Object, Schema}; | |
| use crate::{FlatbufferError, FlatbufferResult}; | |
| use flatbuffers::{ | |
| ForwardsUOffset, InvalidFlatbuffer, TableVerifier, UOffsetT, Vector, Verifiable, Verifier, | |
| VerifierOptions, SIZE_UOFFSET, SIZE_VOFFSET, | |
| }; | |
| use std::collections::HashMap; | |
| /// Verifies a buffer against its schema with custom verification options. | |
| pub fn verify_with_options( | |
| buffer: &[u8], | |
| schema: &Schema, | |
| opts: &VerifierOptions, | |
| buf_loc_to_obj_idx: &mut HashMap<usize, i32>, | |
| ) -> FlatbufferResult<()> { | |
| let mut verifier = Verifier::new(opts, buffer); | |
| if let Some(table_object) = schema.root_table() { | |
| if let core::result::Result::Ok(table_pos) = verifier.get_uoffset(0) { | |
| // Inserts -1 as object index for root table | |
| buf_loc_to_obj_idx.insert(table_pos.try_into()?, -1); | |
| let mut verified = vec![false; buffer.len()]; | |
| return verify_table( | |
| &mut verifier, | |
| &table_object, | |
| table_pos.try_into()?, | |
| schema, | |
| &mut verified, | |
| buf_loc_to_obj_idx, | |
| ); | |
| } | |
| } | |
| Err(FlatbufferError::InvalidSchema) | |
| } | |
| fn verify_table( | |
| verifier: &mut Verifier, | |
| table_object: &Object, | |
| table_pos: usize, | |
| schema: &Schema, | |
| verified: &mut [bool], | |
| buf_loc_to_obj_idx: &mut HashMap<usize, i32>, | |
| ) -> FlatbufferResult<()> { | |
| if table_pos < verified.len() && verified[table_pos] { | |
| return Ok(()); | |
| } | |
| let mut table_verifier = verifier.visit_table(table_pos)?; | |
| for field in &table_object.fields() { | |
| let field_name = field.name().to_owned(); | |
| table_verifier = match field.type_().base_type() { | |
| BaseType::UType | BaseType::UByte => { | |
| table_verifier.visit_field::<u8>(field_name, field.offset(), field.required())? | |
| } | |
| BaseType::Bool => { | |
| table_verifier.visit_field::<bool>(field_name, field.offset(), field.required())? | |
| } | |
| BaseType::Byte => { | |
| table_verifier.visit_field::<i8>(field_name, field.offset(), field.required())? | |
| } | |
| BaseType::Short => { | |
| table_verifier.visit_field::<i16>(field_name, field.offset(), field.required())? | |
| } | |
| BaseType::UShort => { | |
| table_verifier.visit_field::<u16>(field_name, field.offset(), field.required())? | |
| } | |
| BaseType::Int => { | |
| table_verifier.visit_field::<i32>(field_name, field.offset(), field.required())? | |
| } | |
| BaseType::UInt => { | |
| table_verifier.visit_field::<u32>(field_name, field.offset(), field.required())? | |
| } | |
| BaseType::Long => { | |
| table_verifier.visit_field::<i64>(field_name, field.offset(), field.required())? | |
| } | |
| BaseType::ULong => { | |
| table_verifier.visit_field::<u64>(field_name, field.offset(), field.required())? | |
| } | |
| BaseType::Float => { | |
| table_verifier.visit_field::<f32>(field_name, field.offset(), field.required())? | |
| } | |
| BaseType::Double => { | |
| table_verifier.visit_field::<f64>(field_name, field.offset(), field.required())? | |
| } | |
| BaseType::String => table_verifier.visit_field::<ForwardsUOffset<&str>>( | |
| field_name, | |
| field.offset(), | |
| field.required(), | |
| )?, | |
| BaseType::Vector => { | |
| verify_vector(table_verifier, &field, schema, verified, buf_loc_to_obj_idx)? | |
| } | |
| BaseType::Obj => { | |
| if let Some(field_pos) = table_verifier.deref(field.offset())? { | |
| let object_index = field.type_().index(); | |
| let child_obj = schema.objects().get(object_index.try_into()?); | |
| if child_obj.is_struct() { | |
| buf_loc_to_obj_idx.insert(field_pos, object_index); | |
| verify_struct( | |
| table_verifier.verifier(), | |
| &child_obj, | |
| field_pos, | |
| schema, | |
| buf_loc_to_obj_idx, | |
| )? | |
| } else { | |
| let field_value = table_verifier.verifier().get_uoffset(field_pos)?; | |
| let table_pos = field_pos.saturating_add(field_value.try_into()?); | |
| buf_loc_to_obj_idx.insert(table_pos, object_index); | |
| verify_table( | |
| table_verifier.verifier(), | |
| &child_obj, | |
| table_pos, | |
| schema, | |
| verified, | |
| buf_loc_to_obj_idx, | |
| )?; | |
| } | |
| } else if field.required() { | |
| return InvalidFlatbuffer::new_missing_required(field.name().to_string())?; | |
| } | |
| table_verifier | |
| } | |
| BaseType::Union => { | |
| if let Some(field_pos) = table_verifier.deref(field.offset())? { | |
| let field_value = table_verifier.verifier().get_uoffset(field_pos)?; | |
| verify_union( | |
| table_verifier, | |
| &field, | |
| field_pos.saturating_add(field_value.try_into()?), | |
| schema, | |
| verified, | |
| buf_loc_to_obj_idx, | |
| )? | |
| } else if field.required() { | |
| return InvalidFlatbuffer::new_missing_required(field.name().to_string())?; | |
| } else { | |
| table_verifier | |
| } | |
| } | |
| _ => { | |
| return Err(FlatbufferError::TypeNotSupported( | |
| field.type_().base_type().variant_name().unwrap_or_default().to_string(), | |
| )); | |
| } | |
| }; | |
| } | |
| table_verifier.finish(); | |
| verified[table_pos] = true; | |
| Ok(()) | |
| } | |
| fn verify_struct( | |
| verifier: &mut Verifier, | |
| struct_object: &Object, | |
| struct_pos: usize, | |
| schema: &Schema, | |
| buf_loc_to_obj_idx: &mut HashMap<usize, i32>, | |
| ) -> FlatbufferResult<()> { | |
| verifier.range_in_buffer(struct_pos, struct_object.bytesize().try_into()?)?; | |
| for field in &struct_object.fields() { | |
| if field.type_().base_type() == BaseType::Obj { | |
| let obj_idx = field.type_().index(); | |
| let child_obj = schema.objects().get(obj_idx.try_into()?); | |
| if child_obj.is_struct() { | |
| let field_pos = struct_pos.saturating_add(field.offset().into()); | |
| buf_loc_to_obj_idx.insert(field_pos, obj_idx); | |
| verify_struct(verifier, &child_obj, field_pos, schema, buf_loc_to_obj_idx)?; | |
| } | |
| } | |
| } | |
| Ok(()) | |
| } | |
| fn verify_vector<'a, 'b, 'c>( | |
| mut table_verifier: TableVerifier<'a, 'b, 'c>, | |
| field: &Field, | |
| schema: &Schema, | |
| verified: &mut [bool], | |
| buf_loc_to_obj_idx: &mut HashMap<usize, i32>, | |
| ) -> FlatbufferResult<TableVerifier<'a, 'b, 'c>> { | |
| let field_name = field.name().to_owned(); | |
| match field.type_().element() { | |
| BaseType::UType | BaseType::UByte => table_verifier | |
| .visit_field::<ForwardsUOffset<Vector<u8>>>( | |
| field_name, | |
| field.offset(), | |
| field.required(), | |
| ) | |
| .map_err(FlatbufferError::VerificationError), | |
| BaseType::Bool => table_verifier | |
| .visit_field::<ForwardsUOffset<Vector<bool>>>( | |
| field_name, | |
| field.offset(), | |
| field.required(), | |
| ) | |
| .map_err(FlatbufferError::VerificationError), | |
| BaseType::Byte => table_verifier | |
| .visit_field::<ForwardsUOffset<Vector<i8>>>( | |
| field_name, | |
| field.offset(), | |
| field.required(), | |
| ) | |
| .map_err(FlatbufferError::VerificationError), | |
| BaseType::Short => table_verifier | |
| .visit_field::<ForwardsUOffset<Vector<i16>>>( | |
| field_name, | |
| field.offset(), | |
| field.required(), | |
| ) | |
| .map_err(FlatbufferError::VerificationError), | |
| BaseType::UShort => table_verifier | |
| .visit_field::<ForwardsUOffset<Vector<u16>>>( | |
| field_name, | |
| field.offset(), | |
| field.required(), | |
| ) | |
| .map_err(FlatbufferError::VerificationError), | |
| BaseType::Int => table_verifier | |
| .visit_field::<ForwardsUOffset<Vector<i32>>>( | |
| field_name, | |
| field.offset(), | |
| field.required(), | |
| ) | |
| .map_err(FlatbufferError::VerificationError), | |
| BaseType::UInt => table_verifier | |
| .visit_field::<ForwardsUOffset<Vector<u32>>>( | |
| field_name, | |
| field.offset(), | |
| field.required(), | |
| ) | |
| .map_err(FlatbufferError::VerificationError), | |
| BaseType::Long => table_verifier | |
| .visit_field::<ForwardsUOffset<Vector<i64>>>( | |
| field_name, | |
| field.offset(), | |
| field.required(), | |
| ) | |
| .map_err(FlatbufferError::VerificationError), | |
| BaseType::ULong => table_verifier | |
| .visit_field::<ForwardsUOffset<Vector<u64>>>( | |
| field_name, | |
| field.offset(), | |
| field.required(), | |
| ) | |
| .map_err(FlatbufferError::VerificationError), | |
| BaseType::Float => table_verifier | |
| .visit_field::<ForwardsUOffset<Vector<f32>>>( | |
| field_name, | |
| field.offset(), | |
| field.required(), | |
| ) | |
| .map_err(FlatbufferError::VerificationError), | |
| BaseType::Double => table_verifier | |
| .visit_field::<ForwardsUOffset<Vector<f64>>>( | |
| field_name, | |
| field.offset(), | |
| field.required(), | |
| ) | |
| .map_err(FlatbufferError::VerificationError), | |
| BaseType::String => table_verifier | |
| .visit_field::<ForwardsUOffset<Vector<ForwardsUOffset<&str>>>>( | |
| field_name, | |
| field.offset(), | |
| field.required(), | |
| ) | |
| .map_err(FlatbufferError::VerificationError), | |
| BaseType::Obj => { | |
| if let Some(field_pos) = table_verifier.deref(field.offset())? { | |
| let verifier = table_verifier.verifier(); | |
| let vector_offset = verifier.get_uoffset(field_pos)?; | |
| let vector_pos = field_pos.saturating_add(vector_offset.try_into()?); | |
| let vector_len = verifier.get_uoffset(vector_pos)?; | |
| let vector_start = vector_pos.saturating_add(SIZE_UOFFSET); | |
| let child_obj_idx = field.type_().index(); | |
| let child_obj = schema.objects().get(child_obj_idx.try_into()?); | |
| if child_obj.is_struct() { | |
| let vector_size = vector_len.saturating_mul(child_obj.bytesize().try_into()?); | |
| verifier.range_in_buffer(vector_start, vector_size.try_into()?)?; | |
| let vector_range = core::ops::Range { | |
| start: vector_start, | |
| end: vector_start.saturating_add(vector_size.try_into()?), | |
| }; | |
| for struct_pos in vector_range.step_by(child_obj.bytesize().try_into()?) { | |
| buf_loc_to_obj_idx.insert(struct_pos, child_obj_idx); | |
| verify_struct( | |
| verifier, | |
| &child_obj, | |
| struct_pos, | |
| schema, | |
| buf_loc_to_obj_idx, | |
| )?; | |
| } | |
| } else { | |
| verifier.is_aligned::<UOffsetT>(vector_start)?; | |
| let vector_size = vector_len.saturating_mul(SIZE_UOFFSET.try_into()?); | |
| verifier.range_in_buffer(vector_start, vector_size.try_into()?)?; | |
| let vector_range = core::ops::Range { | |
| start: vector_start, | |
| end: vector_start.saturating_add(vector_size.try_into()?), | |
| }; | |
| for element_pos in vector_range.step_by(SIZE_UOFFSET) { | |
| let table_pos = element_pos | |
| .saturating_add(verifier.get_uoffset(element_pos)?.try_into()?); | |
| buf_loc_to_obj_idx.insert(table_pos, child_obj_idx); | |
| verify_table( | |
| verifier, | |
| &child_obj, | |
| table_pos, | |
| schema, | |
| verified, | |
| buf_loc_to_obj_idx, | |
| )?; | |
| } | |
| } | |
| } else if field.required() { | |
| return InvalidFlatbuffer::new_missing_required(field.name().to_string())?; | |
| } | |
| Ok(table_verifier) | |
| } | |
| _ => { | |
| return Err(FlatbufferError::TypeNotSupported( | |
| field.type_().base_type().variant_name().unwrap_or_default().to_string(), | |
| )) | |
| } | |
| } | |
| } | |
| fn verify_union<'a, 'b, 'c>( | |
| mut table_verifier: TableVerifier<'a, 'b, 'c>, | |
| field: &Field, | |
| union_pos: usize, | |
| schema: &Schema, | |
| verified: &mut [bool], | |
| buf_loc_to_obj_idx: &mut HashMap<usize, i32>, | |
| ) -> FlatbufferResult<TableVerifier<'a, 'b, 'c>> { | |
| let union_enum = schema.enums().get(field.type_().index().try_into()?); | |
| if union_enum.values().is_empty() { | |
| return Err(FlatbufferError::InvalidUnionEnum); | |
| } | |
| let enum_offset = field.offset() - u16::try_from(SIZE_VOFFSET)?; | |
| if let Some(enum_pos) = table_verifier.deref(enum_offset)? { | |
| let enum_value = table_verifier.verifier().get_u8(enum_pos)?; | |
| let enum_type = union_enum | |
| .values() | |
| .get(enum_value.into()) | |
| .union_type() | |
| .ok_or(FlatbufferError::InvalidUnionEnum)?; | |
| match enum_type.base_type() { | |
| BaseType::String => <&str>::run_verifier(table_verifier.verifier(), union_pos)?, | |
| BaseType::Obj => { | |
| let child_obj = schema.objects().get(enum_type.index().try_into()?); | |
| buf_loc_to_obj_idx.insert(union_pos, enum_type.index()); | |
| if child_obj.is_struct() { | |
| verify_struct( | |
| table_verifier.verifier(), | |
| &child_obj, | |
| union_pos, | |
| schema, | |
| buf_loc_to_obj_idx, | |
| )? | |
| } else { | |
| verify_table( | |
| table_verifier.verifier(), | |
| &child_obj, | |
| union_pos, | |
| schema, | |
| verified, | |
| buf_loc_to_obj_idx, | |
| )?; | |
| } | |
| } | |
| _ => { | |
| return Err(FlatbufferError::TypeNotSupported( | |
| enum_type.base_type().variant_name().unwrap_or_default().to_string(), | |
| )) | |
| } | |
| } | |
| } else { | |
| return InvalidFlatbuffer::new_inconsistent_union( | |
| field.name().to_string(), | |
| format!("{}_type", field.name()), | |
| )?; | |
| } | |
| verified[union_pos] = true; | |
| Ok(table_verifier) | |
| } |