Is it considered UB to construct a slice from data allocated by C?

I have a library which uses another C library underneath via FFI, and the library has a function which allocates some data (just a sequence of bytes) and returns a pointer to the data (along with the allocation's size). And I somehow need to construct a "Rust-native" Vec<u8> out of this data.

My intent is to use the Vec<T>: From<&[T]> implementation, but for that I have to construct a slice somehow. So, is the following considered UB, and are there better approaches?

extern "C" {
    fn get_data(ptr: *mut *mut u8, size: *mut usize);
}
fn main() {
    let ptr: *mut *mut u8 = std::ptr::null_mut();
    let size = 0;
    unsafe {
        get_data(&raw mut ptr, &raw mut size);
    }
    // data pointed by `ptr` is allocated using `malloc`, though we never drop it
    let slice: &[u8] = unsafe { std::mem::transmute((ptr, size)) };
    // Construct the vector by copying each element
    let v = Vec::from(slice);
}

Thanks in advance!

Edit: The data returned by get_data is now owned by the caller, and is later free'd

AFAIK, the layout of &[T] is not guaranteed so the transmute us UB.
However, you can simply use from_raw_parts in std::slice - Rust

Does the user of get_data get ownership and is supposed to drop the pointer?

Edit: If you do not need/want to free the memory you can write this minimal wrapper:

fn access_data<T>(f: impl FnOnce(&[u8]) -> T) -> Option<T> {
    let mut ptr: *mut u8 = std::ptr::null_mut();
    let mut size = 0;
    unsafe {
        get_data(&mut ptr, &mut size);
        if ptr.is_null() {
            return None;
        }
        let slice = core::slice::from_raw_parts(ptr, size);
        Some(f(slice))
    }
}

which you can call with access_data(Vec::from).unwrap() to get your Vec. But you can also use any other function that processes a slice directly.

3 Likes

Vec::from(slice) creates a new Vec and copies the data into it. If i understand you correctly you probably want Vec::from_raw_parts as that also deallocates the memory when you are done with it.

Vec deallocates using Rust's allocator, which cannot be used to free C allocations. This is UB, but it might happen to work correctly sometimes.

In general, the same language that allocated some memory must be the one to free it. OP, what does the C library's documentation say about freeing the data given from get_data?[1] Is it giving the data up entirely to the caller, or is it just temporarily borrowed from C?


  1. C's free function is easily accessible from Rust under libc::free. ↩ī¸Ž

6 Likes

Thanks, this looks like exactly what I need!

P.S: The user gets ownership of the returned data, but it's later free'd, so we need to copy it anyways.

Is it giving the data up entirely to the caller, or is it just temporarily borrowed from C?

It yields ownership to the Rust code, sorry for not mentioning this in the question.

you can use a custom Vec like (actually, more Box<[u8]> like) type which Deref to [u8], but you cannot use the FFI pointer to call Vec::from_raw_parts(), because Vec::from_raw_parts() must use a pointer allocated by rust's Global allocator.

however, you can create a slice from FFI the pointer directly, since the element type u8 is FFI safe. Vec::from_slice() is safe, but it will allocate memory from rust's global allocator and copy the data.

1 Like

It's definitely not UB to construct a slice out of data allocated by C. You can do so using std::slice::from_raw_parts, which takes a pointer and a length.

However, this is definitely not okay. There's no guarantee that the layouts match here. You need to use slice::from_raw_parts instead.

As long as you're okay with copying the elements over, then sure. You cannot create a vector without copying, though, because the vector would use the wrong destructor to free the data in its destructor / when it reallocates.

5 Likes