Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fuzz: remove potential undefined behavior in chaos harness #80

Merged
merged 1 commit into from
Nov 7, 2023

Conversation

00xc
Copy link
Contributor

@00xc 00xc commented Nov 6, 2023

The chaos harness has a potential UB bug reported by Miri due to mutable pointer aliasing. The heap object has a mutable reference to HEAP_MEM, which gets invalidated when calculating remaining_space, as it does so through a mut pointer. Thus, using heap after using the pointer is technically undefined behavior under Rust's aliasing rules.

Heap::new(HEAP_MEM.as_mut_ptr(), size)

let remaining_space = HEAP_MEM
.as_mut_ptr()
.add(MAX_HEAP_SIZE)
.offset_from(heap.top());

heap.extend(additional as usize);

Fix this by taking a const pointer.

Note that it is very unlikely this caused any actual issues under the current state of the compiler.

This can be tested by running the following reproducer (a simplified version of the chaos harness) under Miri (cargo +nightly miri run).

Reproducer
use linked_list_allocator::Heap;
use std::alloc::Layout;
use std::ptr::NonNull;

#[derive(Debug)]
enum Action {
    // allocate a chunk with the size specified
    Alloc { size: u16, align_bit: u8 },
    // free the pointer at the index specified
    Free { index: u8 },
    // extend the heap by amount specified
    Extend { additional: u16 },
}
use Action::*;

const MAX_HEAP_SIZE: usize = 5000;
static mut HEAP_MEM: [u8; MAX_HEAP_SIZE] = [0; MAX_HEAP_SIZE];

fn main() {
    let actions = vec![
        Alloc {
            size: 25,
            align_bit: 1,
        },
        Extend { additional: 255 },
    ];
    let size = 100;
    fuzz(size, actions);
}

fn fuzz(size: u16, actions: Vec<Action>) {
    // init heap
    let mut heap = unsafe {
        let size = size as usize;
        if size > MAX_HEAP_SIZE || size < 3 * core::mem::size_of::<usize>() {
            return;
        }

        Heap::new(HEAP_MEM.as_mut_ptr(), size)
    };
    let mut ptrs: Vec<(NonNull<u8>, Layout)> = Vec::new();

    // process operations
    for action in actions {
        match action {
            Alloc { size, align_bit } => {
                let layout = {
                    let align = 1_usize.rotate_left(align_bit as u32);
                    if align == 1 << 63 {
                        return;
                    }
                    Layout::from_size_align(size as usize, align).unwrap()
                };

                if let Ok(ptr) = heap.allocate_first_fit(layout) {
                    ptrs.push((ptr, layout));
                } else {
                    return;
                }
            }
            Free { index } => {
                if index as usize >= ptrs.len() {
                    return;
                }

                let (ptr, layout) = ptrs.swap_remove(index as usize);
                unsafe {
                    heap.deallocate(ptr, layout);
                }
            }
            Extend { additional } =>
            // safety: new heap size never exceeds MAX_HEAP_SIZE
            unsafe {
                let remaining_space = HEAP_MEM
                    .as_mut_ptr()
                    .add(MAX_HEAP_SIZE)
                    .offset_from(heap.top());
                assert!(remaining_space >= 0);

                if additional as isize > remaining_space {
                    return;
                }

                heap.extend(additional as usize);
            },
        }
    }

    // free the remaining allocations
    for (ptr, layout) in ptrs {
        unsafe {
            heap.deallocate(ptr, layout);
        }
    }

    // make sure we can allocate the full heap (no fragmentation)
    let full = Layout::from_size_align(heap.size(), 1).unwrap();
    assert!(heap.allocate_first_fit(full).is_ok());
}

@Freax13
Copy link
Member

Freax13 commented Nov 6, 2023

The heap object has a mutable reference to HEAP_MEM, which gets invalidated when calculating remaining_space, as it does so through a mut pointer. Thus, using heap after using the pointer is technically undefined behavior under Rust's aliasing rules.

Wouldn't get the mutable reference in heap also get invalidated by the creation of a shared reference? Note that you can also create a pointer to the HEAP_MEM global without creating a reference by using the addr_of! macro.

@00xc
Copy link
Contributor Author

00xc commented Nov 6, 2023

Wouldn't get the mutable reference in heap also get invalidated by the creation of a shared reference?

Technically we are not creating any reference, right? Miri does not seem to complain either.

Note that you can also create a pointer to the HEAP_MEM global without creating a reference by using the addr_of! macro.

Yeah maybe this is cleaner, thanks!

The chaos harness has a potential UB bug reported by Miri due to
mutable pointer aliasing. The `heap` object has a mutable reference
to `HEAP_MEM`, which gets invalidated when calculating
`remaining_space`, as it does so through a mut pointer. Thus, using
`heap` after using the pointer is technically undefined behavior
under Rust's aliasing rules.

Fix this by creating a const pointer via the `addr_of!()` macro.

Note that it is very unlikely this caused any actual issues under the
current state of the compiler.

Signed-off-by: Carlos López <[email protected]>
@Freax13
Copy link
Member

Freax13 commented Nov 6, 2023

Wouldn't get the mutable reference in heap also get invalidated by the creation of a shared reference?

Technically we are not creating any reference, right? Miri does not seem to complain either.

The call to slice::as_ptr creates a reference to the slice. I'm not sure why Miri doesn't complain.

Copy link
Member

@phil-opp phil-opp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot!

@phil-opp phil-opp merged commit 3ccc544 into rust-osdev:main Nov 7, 2023
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants