/* * 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, ) -> 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, ) -> 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::(field_name, field.offset(), field.required())? } BaseType::Bool => { table_verifier.visit_field::(field_name, field.offset(), field.required())? } BaseType::Byte => { table_verifier.visit_field::(field_name, field.offset(), field.required())? } BaseType::Short => { table_verifier.visit_field::(field_name, field.offset(), field.required())? } BaseType::UShort => { table_verifier.visit_field::(field_name, field.offset(), field.required())? } BaseType::Int => { table_verifier.visit_field::(field_name, field.offset(), field.required())? } BaseType::UInt => { table_verifier.visit_field::(field_name, field.offset(), field.required())? } BaseType::Long => { table_verifier.visit_field::(field_name, field.offset(), field.required())? } BaseType::ULong => { table_verifier.visit_field::(field_name, field.offset(), field.required())? } BaseType::Float => { table_verifier.visit_field::(field_name, field.offset(), field.required())? } BaseType::Double => { table_verifier.visit_field::(field_name, field.offset(), field.required())? } BaseType::String => table_verifier.visit_field::>( 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, ) -> 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, ) -> FlatbufferResult> { let field_name = field.name().to_owned(); match field.type_().element() { BaseType::UType | BaseType::UByte => table_verifier .visit_field::>>( field_name, field.offset(), field.required(), ) .map_err(FlatbufferError::VerificationError), BaseType::Bool => table_verifier .visit_field::>>( field_name, field.offset(), field.required(), ) .map_err(FlatbufferError::VerificationError), BaseType::Byte => table_verifier .visit_field::>>( field_name, field.offset(), field.required(), ) .map_err(FlatbufferError::VerificationError), BaseType::Short => table_verifier .visit_field::>>( field_name, field.offset(), field.required(), ) .map_err(FlatbufferError::VerificationError), BaseType::UShort => table_verifier .visit_field::>>( field_name, field.offset(), field.required(), ) .map_err(FlatbufferError::VerificationError), BaseType::Int => table_verifier .visit_field::>>( field_name, field.offset(), field.required(), ) .map_err(FlatbufferError::VerificationError), BaseType::UInt => table_verifier .visit_field::>>( field_name, field.offset(), field.required(), ) .map_err(FlatbufferError::VerificationError), BaseType::Long => table_verifier .visit_field::>>( field_name, field.offset(), field.required(), ) .map_err(FlatbufferError::VerificationError), BaseType::ULong => table_verifier .visit_field::>>( field_name, field.offset(), field.required(), ) .map_err(FlatbufferError::VerificationError), BaseType::Float => table_verifier .visit_field::>>( field_name, field.offset(), field.required(), ) .map_err(FlatbufferError::VerificationError), BaseType::Double => table_verifier .visit_field::>>( field_name, field.offset(), field.required(), ) .map_err(FlatbufferError::VerificationError), BaseType::String => table_verifier .visit_field::>>>( 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::(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, ) -> FlatbufferResult> { 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( format!("{}_type", field.name()), field.name().to_string(), )?; } verified[union_pos] = true; Ok(table_verifier) }