Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

Deserializing structs over an FFI boundary

Lander Brandt edited this page Jul 16, 2019 · 5 revisions

Unfortunately, since the layout of objects used with Lain are not the same as your C structs would be, deserializing objects over an FFI boundary is not supported when using non-primitive data types. In essence, you cannot take this:

enum Foo {
    Bar = 1,
    Baz = 2,
}

#[derive(NewFuzzed, Mutatable)]
struct MyStruct {
    typ: UnsafeEnum<Foo, u8>,
    #[bitfield(backing_type = "u8", bits = 3)]
    x: u8,
    #[bitfield(backing_type = "u8", bits = 5)]
    y: u8,
    data: [u8; 0x10],
}

And re-construct the object from an opaque byte blob sent from a C application. In this specific case there are two issues blocking you from doing an std::mem::transmute::<[u8; N], MyStruct>(opaque_blob_ptr):

  1. UnsafeEnum internally is a tagged union and contains metadata that wouldn't be present on the C side of things.
  2. The bitfields are represented as whole types and are not actually packed in memory for the Rust representation.

Alternatives

The recommended approach for handling reading back data from an application sending binary data would be to either write a custom binary deserializer or use rust-bindgen to generate a separate set of #[repr(C)] objects used only for reading data.

If you are okay with slightly worse mutation and debugging of your structs, there's nothing preventing you from using basic data types and packing the bitfields yourself:

#[repr(C)]
enum Foo {
    Bar = 1,
    Baz = 2,
}

#[derive(NewFuzzed, Mutatable)]
#[repr(C)
struct MyStruct {
    typ: Foo,
    bitfield: u8,
    data: [u8; 0x10],
}

This should be legal to std::mem::transmute.

Clone this wiki locally