Protecting callers from their own padding bytes

(See Holding an impl AsMut<[u8]> and multiple &mut [u8] into it at the same time for context. This is a followup.)

I wound up going with the following layout for my arena:

pub struct Arena<'buf> {
  lifetime_phantom: PhantomData<&'buf mut ()>,
  mem_ptr: NonNull<u8>,
  mem_len: usize,
  next_byte: usize,

My question: can this be adapted into a typed arena for a fixed type T with no restrictions on the type? The primary problem I see is that when 'buf ends (e.g., the arena is dumped into forget()), the slice that it was constructed from is now again accessible. This means that if T has padding bits, they are now observable as bytes through the &mut [u8].

I'm not sure that can be done with a safe API in current Rust? Is there a way MaybeUninit can save me? If so, it seems like the following would work:

impl<'buf, T> TypedArena<'buf, T> { // Same layout as Arena.
  pub fn new(buf: &'buf [MaybeUninit<u8>]) -> Self { ... }

let mem = unsafe { MauybeUninit::<[MaybeUninit; 1024]>::uninit().assume_init() };
let arena = Arena::new(&mut mem);
// At no point is a direct reference to the arena memory created here.

I think new does not need to be marked as unsafe. Thoughts?

Bonus points: the above example assumes that T is fixed. Can I make one arena allocate all sized types? The following would be Bad:

let mut arena = ...;
let _ = arena.alloc_typed<DefinitelyHasPadding>(...);
let bytes = arena.alloc(4);  // Oops I've witnessed padding bytes.

I suspect that zeroing every allocation (through ptr::write) before returning it is sufficient...?

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.