blob: 43739147783639f98746338b2b8f0840da4ba87a [file] [log] [blame]
/*
* 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(
format!("{}_type", field.name()),
field.name().to_string(),
)?;
}
verified[union_pos] = true;
Ok(table_verifier)
}