oreboot icon indicating copy to clipboard operation
oreboot copied to clipboard

Serialize and deserialize structs safely

Open rjoleary opened this issue 3 years ago • 4 comments

There are a few places where we unnecessarily use unsafe blocks to serialize and deserialize structs:

In particular see https://github.com/oreboot/oreboot/blob/main/src/lib/uefi/src/lib.rs. For example:

let fveh: efi::EFI_FIRMWARE_VOLUME_EXT_HEADER =
                unsafe { core::ptr::read(fveh_bytes.as_ptr() as *const _) };

Instead, we should be using some Rust macros to achieve this safely. There are a number of crates built for this task:

  • https://serde.rs/ is obviously the most popular. Some of its flexibility gets in the way. We just need to be able to serialize/deserialize a struct in the same format as a packed C struct.
  • https://github.com/xoac/endian_codec
  • https://crates.io/crates/packed_struct
  • ... (I have no strong opinions for a particular crate at the moment)

rjoleary avatar May 21 '21 19:05 rjoleary

I saw a blog post recently about this: https://adventures.michaelfbryan.com/posts/deserializing-binary-data-files/

rjoleary avatar Jun 26 '21 05:06 rjoleary

About the crates that can be used:

ssmarshal is surprisingly good for that (I'd give it 10 of 10 points)--but technically one would be using the crate for something it was not designed for (it's designed as a message format for a microkernel). It does work very well for general C struct serialization--and it's very easy to think about serialization that way (it's just using serde and serde derive).

zerocopy is the right way to do it; but boy is it annoying. I'm using zerocopy quite a lot nowadays regardless.

But really, if you don't care about endianness and the architectures are the same anyway, then just using repr(C) and no crates is fine.

packed_struct is also OK.

daym avatar Jun 26 '21 12:06 daym

With the core library alone, is there a non-unsafe way to convert a &[u8] to a repr(C) struct?

rjoleary avatar Jun 30 '21 22:06 rjoleary

The safe way I know how to do it is parse field-by-field like nom (supports no_std). You can also structure it to parse lazily with accessors (a la capnproto) if you want to avoid having original and deserialized versions in memory. These are not nearly as easy as transmutation.

TIL that there's a safe transmute working group.

fotonick avatar Jul 01 '21 23:07 fotonick