Is this FFI logic sound?

I'm creating an FFI module which will allow arrays of floats (sent across the FFI boundary as an ExternalArray) to be used to calculate a result, which will then be sent back. The result is a Vec<Vec<f64>>. I've written all the From impls to make the conversions ergonomic, but I wanted to check whether I've accidentally done something that's accidentally UB.

  1. I don't take ownership of the incoming ExternalArray as this happens in a to_vec() call elsewhere – the slice isn't modified by my code.
  2. I don't understand why InternalArray has to be Clone, for instance.
#[repr(C)]
pub struct ExternalArray {
    pub data: *const c_void,
    pub len: size_t,
}

impl From<ExternalArray> for &[f64] {
    fn from(arr: ExternalArray) -> Self {
        unsafe { slice::from_raw_parts(arr.data as *mut f64, arr.len) }
    }
}



#[repr(C)]
pub struct WrapperArray {
    pub data: *const c_void,
    pub len: size_t,
}


#[repr(C)]
#[derive(Clone)]
pub struct InternalArray {
    pub data: *const c_void,
    pub len: size_t,
}

// prepare individual WrapperArray members to be leaked across the FFI boundary
impl From<Vec<f64>> for InternalArray {
    fn from(v: Vec<f64>) -> Self {
        let boxed = v.into_boxed_slice();
        let blen = boxed.len();
        let rawp = Box::into_raw(boxed);
        InternalArray {
            data: rawp as *const c_void,
            len: blen as size_t,
        }
    }
}

// create leakable data structure
impl From<Vec<Vec<f64>>> for WrapperArray {
    fn from(arr: Vec<Vec<f64>>) -> Self {
        let iarrs: Vec<InternalArray> = arr.into_iter().map(|member| member.into()).collect();
        let boxed = iarrs.into_boxed_slice();
        let blen = boxed.len();
        let rawp = Box::into_raw(boxed);
        WrapperArray {
            data: rawp as *const c_void,
            len: blen as size_t,
        }
    }
}

// Reconstitute individual WrapperArray members so they can be dropped
impl From<InternalArray> for Vec<f64> {
    fn from(arr: InternalArray) -> Self {
        // we originated this data, so pointer-to-slice -> box -> vec
        unsafe {
            let p = ptr::slice_from_raw_parts_mut(arr.data as *mut f64, arr.len);
            Box::from_raw(p).to_vec()
        }
    }
}

// Reconstitute a WrapperArray that has been returned across the FFI boundary so it can be dropped
impl From<WrapperArray> for Vec<Vec<f64>> {
    fn from(arr: WrapperArray) -> Self {
        let arrays = unsafe {
            let p = ptr::slice_from_raw_parts_mut(arr.data as *mut InternalArray, arr.len);
            Box::from_raw(p).to_vec()
        };
        arrays.into_iter().map(|arr| arr.into()).collect()
    }
}

I think you probably wanted to use into_vec() instead, as to_vec() construct a new Vec by making clones of the old slice.

1 Like

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.