Dealloc a pointer(*mut u8)

Hi,

I have created a ptr using alloc function

let layout = Layout::from_size_align_unchecked(1024 * 8 , align);
println!("Size {}, Align {}", layout.size(), layout.align());
let ptr = alloc(layout);

Now, I have converted this ptr to Vector using from_raw_parts().
After that I am passing this vector using ffi after forgetting at vector using forget(vec).
After that I receive ptr, len and capacity from ffi and than I wanted to dealloc that memory.

dealloc(vv.as_mut_ptr(), layout);

Is using delloc method for this use case is right?
How can I verify that the memory is actually dellocated? Is there any other way to do this deallocation of memory.

STOP! STOP DOING THIS!

Vectors automatically allocate and deallocate memory, you don’t need these unsafe tricks.

Just use Vec::new or Vec::with_capacity.

let _my_vec = Vec::<u8>::new();

Vectors use global_allocator.

Actually, I need to allocate memory aligned to 512(it can be something else too(power of 2)). Thats why I am doing this.
Doing,

let _my_vec = Vec::<u8>::new();

Doesn't guarantee that the memory will be aligned.

Is there a reason you can't leave it as a Vec?

Do not put the pointer into a Vec if the layout does not match what a Vec requires! Either just store the raw pointer directly, or wrap it in a custom struct.

To verify whether the deallocation actually happened, you can use valgrind.

6 Likes

It's good enough if you never giveaway the ownership or expose borrowing (&mut). But I still recommend you wrap the Vec<u8> in ManuallyDrop to ensure no automatic dropping is happening.

Edit: Actually just pass the pointer, do not construct Vec<u8>, construct &[u8] instead.

Its important for my business logic to convert it into Vec. Is there no safe way to do that? @zirconium-n
@alice
Why won't forgetting it forget(vv); , and than calling dealloc() after I use is complete will not work?

A Vec allocates data with needed layout, you mustn’t change it. You can implement your own vector tho.

3 Likes

Could you elaborate why does it require to be a Vec<u8>? Are you calling third party API requires Vec<u8>? &[u8] should be suffice for most if not all circumstance.

Why won't forgetting it forget(vv); , and than calling dealloc() after I use is complete will not work?

See my original comment: In practice it will work if you never giveaway &mut Vec<u8> or Vec<u8>, i.e. only using &Vec<u8>. But it's no different from &[u8] then.

...and don't grow or shrink it yourself, of course. And if vector isn't going to be grown or shrunk, &mut [u8] will probably cover every possible case, except when the API asks for more then it needs.

2 Likes

It is mostly for backward compatiabilty. Why won't it work if I giveaway Vec. And forget it. And later dealloc it later using pointer?

It works as long as you never modify or drop the Vec. People (including me) are just advising against doing it, since it's not a sound abstraction and easy to volatile the constraint.

So... If you have to do it, go ahead, and write a descriptive comment about it, then carefully code review any further modification related to these code.

1 Like

The reason I do not recommend it is that it is fragile. If you ever call a method that causes it to reallocate, or hit some unexpected code path that causes its destructor to run (e.g. are you resistant to panics?), then you get undefined behavior.

4 Likes

Do you actually need to use Vec here?

As others have said, the Vec::from_raw_parts() constructor adds a bunch of safety invariants in order to avoid UB. However, if we're just trying to allocate some memory so it can be passed across the FFI boundary, then why not just pass the pointer across as-is?

use std::alloc::Layout;

extern "C" {
    /// A FFI call that will initialize the buffer with something useful.
    fn call_to_c(buffer: *mut u8, len: usize);
}

fn main() {
    unsafe {
        // Allocate a chunk of memory
        let layout = Layout::from_size_align(1024 * 8, 512).unwrap();
        let ptr: *mut u8 = std::alloc::alloc(layout);
        assert!(!ptr.is_null());

        // Now, we pass the buffer across the FFI boundary so it can be initialized
        call_to_c(ptr, layout.size());

        // Safety: the C code initialized our buffer, so now it's safe to use
        let buffer: &[u8] = std::slice::from_raw_parts(ptr, layout.size());
        println!("{:?}", &buffer[..5]);

        // Make sure the buffer is freed at the end
        std::alloc::dealloc(ptr, layout);
    }
}

(playground)

1 Like

There's CVec that allows you to have a Vec-like container with arbitrary allocation managed by you:

But you absolutely can't do that with Vec. No way. Vec pretty much by definition is not allowed to behave the way you want it to, and fudging it is unsafe and unsound. Even if it may happen to work now, it's a time bomb that may corrupt your program in a different Rust version.

If you have an existing API that takes a Vec, then it's not compatible, and that API doesn't support higher memory alignment. You have to change that API (e.g. make it take impl AsRef<[u8]>).

A Vec that can't be resized or dropped is just a slice. You can use std::slice::from_raw_parts() safely with higher alignment.

6 Likes