google / flatbuffers

FlatBuffers: Memory Efficient Serialization Library
https://flatbuffers.dev/
Apache License 2.0
23.16k stars 3.23k forks source link

[Rust] inconsistent nested_flatbuffers validations #8291

Open Nekrolm opened 5 months ago

Nekrolm commented 5 months ago

For byte fields marked as "nested_flatbuffer" flatc generates corresponding accessors

e.g. for schema

table Nested {
    value: string;
}

table Schema {
    nested: [ubyte] (nested_flatbuffer: "Nested");
}
root_type Schema;

the following safe accessor is generated

pub fn nested_nested_flatbuffer(&'a self) -> Option<Nested<'a>> {
    self.nested().map(|data| {
      use flatbuffers::Follow;
      // Safety:
      // Created from a valid Table for this object
      // Which contains a valid flatbuffer in this slot
      unsafe { <flatbuffers::ForwardsUOffset<Nested<'a>>>::follow(data.bytes(), 0) }
    })
  }

But assumption that the flatbuffer is valid there could be violated. E.g. it is possible to create invalid utf-8 &str even if only safe API is used and it looks like the all necessary validations were made:

mod schema_generated;

use schema_generated as fb;

fn nested() -> Vec<u8> {
    let mut builder = flatbuffers::FlatBufferBuilder::new();
    let s = builder.create_string("world");
    let nested = fb::Nested::create(&mut builder, &fb::NestedArgs {
        value: Some(s)
    });
    builder.finish(nested, None);
    let mut data = builder.finished_data().to_vec();
    // some corruption happens
    data.last_chunk_mut::<6>().map(|c| c[1] = 0xff);
    data
}

fn main() {

    let mut builder = flatbuffers::FlatBufferBuilder::new();
    let bytes = builder.create_vector(&nested());
    let schema = fb::Schema::create(&mut builder, &fb::SchemaArgs {
        nested: Some(bytes)
    });
    builder.finish(schema, None);
    let data = builder.finished_data();

    let schema = fb::root_as_schema(data).expect("ok root");

    let nested = schema.nested().map(|n| flatbuffers::root::<fb::Nested>(n.bytes())).transpose();

    // validation failed -- ok, as expected
    let err = nested.expect_err("utf-8 error");
    println!("got error {err:?}");

    let nested = schema.nested_nested_flatbuffer().expect("ok, it's there");
    let value = nested.value();
    // and now it panics!
    println!("hello: {value:?}")
}

output with rustc-1.77 in release:

got error Utf8Error { error: Utf8Error { valid_up_to: 3, error_len: Some(1) }, range: 24..29, error_trace: ErrorTrace([TableField { field_name: "value", position: 16 }]) }
thread 'main' panicked at library/core/src/fmt/mod.rs:2350:34:
byte index 7 is out of bounds of `wor�d`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
// flatbuffers crate:
name = "flatbuffers"
version = "24.3.25"

flatbuffers/flatc --version
flatc version 24.3.25