Skip to content

Main example, init code has extensive undefined behavior #108

@jamesmunns

Description

@jamesmunns

The current docs have the following example:

 // Initialize the allocator BEFORE you use it { use core::mem::MaybeUninit; const HEAP_SIZE: usize = 1024; static mut HEAP_MEM: [MaybeUninit<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) } }

However, the init function looks like this:

 pub unsafe fn init(&self, start_addr: usize, size: usize) { critical_section::with(|cs| { let block: &[u8] = core::slice::from_raw_parts(start_addr as *const u8, size); self.heap .borrow(cs) .borrow_mut() .insert_free_block_ptr(block.into()); }); }

This is undefined behavior, you are creating a &[u8] from uninit memory. This could be replaced with something like:

static mut HEAP_MEM: [u8; HEAP_SIZE] = [0; HEAP_SIZE];

And the heap would still reside in .bss and wouldn't be UB.

Furthermore, I think the init code is also unsound, at least under stacked borrows, because you create an immutable slice, then hand it to Tlsf to write to it, which does not have the provenance necessary to do so. There's probably another method you can use to create the NonNull<[u8]> for insert_free_block_ptr which does not create an intervening slice, which might also bypass the first unsoundness here.

In general, we should fix this quickly, and yank the current versions affected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions