Unsafe related question

Could someone please point out the fallacy here?

fn get_base_ptr(len : usize) -> *mut u8
{
    let mut buf: Vec<u8> = Vec::with_capacity(len);
    let ptr : *mut u8 = buf.as_mut_ptr();
    std::mem::forget(ptr);
    return ptr;
}

fn sum(buf_ptr : *mut u8) -> ()
{
    unsafe {
        let data = Vec::from_raw_parts(buf_ptr, 16, 16);
        let sum: u8 = data.iter().sum();
        println!("Sum is {}", sum);
    }
}

fn main()
{
    let input = vec![1 as u8, 2, 3, 4, 5];

    for  i in 1 .. 2 {
        let buf_ptr = get_base_ptr(input.len());
        unsafe {
            std::ptr::copy(input.as_ptr(), buf_ptr, input.len());
            sum(buf_ptr);
        }
    }
}

It runs and then spits out the following:

mem(20659,0x10cce1dc0) malloc: *** error for object 0x7f952d405c50: pointer being freed was not allocated
mem(20659,0x10cce1dc0) malloc: *** set a breakpoint in malloc_error_break to debug
Abort trap: 6

What am I missing?

First issue: in get_base_ptr, you're forgetting ptr, not buf, so buf gets de-allocated at the end of the function and the pointer you return is dangling. This causes undefined behaviour when you try to use that pointer later on, because the thing it was pointing at no longer exists.

Second issue: in sum, you're saying buf_ptr points at a Vec with capacity 16 and length 16, when it actually points at a Vec with capacity 5 and (since you've initialized it via the std::ptr::copy) length 5. This causes undefined behaviour when you try to sum data, because you're reading uninitialized memory.

Fixing these two issues gets Miri (a Rust interpreter that can detect some cases of undefined behaviour) to stop complaining, but I'm not confident enough in my unsafe knowledge to know if there's not other UB lurking here.

1 Like

Please format your code:

Edit: like this

fn get_base_ptr(len: usize) -> *mut u8 {
    let mut buf: Vec<u8> = Vec::with_capacity(len);
    let ptr: *mut u8 = buf.as_mut_ptr();
    std::mem::forget(ptr);
    return ptr;
}

fn sum(buf_ptr: *mut u8) -> () {
    unsafe {
        let data = Vec::from_raw_parts(buf_ptr, 16, 16);
        let sum: u8 = data.iter().sum();
        println!("Sum is {}", sum);
    }
}

fn main() {
    let input = vec![1 as u8, 2, 3, 4, 5];

    for i in 1..2 {
        let buf_ptr = get_base_ptr(input.len());
        unsafe {
            std::ptr::copy(input.as_ptr(), buf_ptr, input.len());
            sum(buf_ptr);
        }
    }
}
3 Likes

To add to what @17cupsofcoffee said, here's how I'd rewrite this code with no unsafe:

fn main() {
    let input = vec![1u8, 2, 3, 4, 5];
    // `Vec::leak` "forgets" the vector and returns a `&[T]` of its contents
    let slice: &'static [u8] = input.leak();
    dbg!(slice.iter().sum());
}

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.