I'm trying to free memory allocated to a CStringand passed to Python using ctypes. However, Python is crashing with a malloc error:
python(30068,0x7fff73f79000) malloc: *** error for object 0x103be2490: pointer being freed was not allocated
Here are the Rust functions I'm using to pass the pointer to ctypes:
#[repr(C)]
pub struct Array {
pub data: *const c_void,
pub len: libc::size_t,
}
// Build &mut[[f64; 2]] from an Array, so it can be dropped
impl<'a> From<Array> for &'a mut [[f64; 2]] {
fn from(arr: Array) -> Self {
unsafe { slice::from_raw_parts_mut(arr.data as *mut [f64; 2], arr.len) }
}
}
// Build an Array from a Vec, so it can be leaked across the FFI boundary
impl<T> From<Vec<T>> for Array {
fn from(vec: Vec<T>) -> Self {
let array = Array {
data: vec.as_ptr() as *const libc::c_void,
len: vec.len() as libc::size_t,
};
mem::forget(vec);
array
}
}
// Build a Vec from an Array, so it can be dropped
impl From<Array> for Vec<[f64; 2]> {
fn from(arr: Array) -> Self {
unsafe { Vec::from_raw_parts(arr.data as *mut [f64; 2], arr.len, arr.len) }
}
}
// Decode an Array into a Polyline
impl From<Array> for String {
fn from(incoming: Array) -> String {
let result: String = match encode_coordinates(&incoming.into(), 5) {
Ok(res) => res,
// we don't need to adapt the error
Err(res) => res
};
result
}
}
#[no_mangle]
pub extern "C" fn encode_coordinates_ffi(coords: Array) -> *mut c_char {
let s: String = coords.into();
CString::new(s).unwrap().into_raw()
}
And the one I'm using to free the pointer when it's returned by Python
pub extern "C" fn drop_cstring(p: *mut c_char) {
unsafe { CString::from_raw(p) };
}
And the Python function I'm using to convert the pointer to a str:
def char_array_to_string(res, _func, _args):
""" restype is c_void_p to prevent automatic conversion to str
which loses pointer access
"""
converted = cast(res, c_char_p)
result = converted.value
drop_cstring(converted)
return result
And the Python function I'm using to generate the Array struct to pass into Rust:
class _FFIArray(Structure):
"""
Convert sequence of float lists to a C-compatible void array
example: [[1.0, 2.0], [3.0, 4.0]]
"""
_fields_ = [("data", c_void_p),
("len", c_size_t)]
@classmethod
def from_param(cls, seq):
""" Allow implicit conversions """
return seq if isinstance(seq, cls) else cls(seq)
def __init__(self, seq, data_type = c_double):
arr = ((c_double * 2) * len(seq))()
for i, member in enumerate(seq):
arr[i][0] = member[0]
arr[i][1] = member[1]
self.data = cast(arr, c_void_p)
self.len = len(seq)
argtype and restype definitions:
encode_coordinates = lib.encode_coordinates_ffi
encode_coordinates.argtypes = (_FFIArray,)
encode_coordinates.restype = c_void_p
encode_coordinates.errcheck = char_array_to_string
drop_cstring = lib.drop_cstring
drop_cstring.argtypes = (c_char_p,)
drop_cstring.restype = None
I'm inclined to think it's not the Rust functions, because a dylib crash would cause a segfault (and the FFI tests pass on the Rust side). I can also continue with other operations in Python after calling the FFI functions – the malloc error occurs when the process exits.
coords.intoimplemented? I'm trying to get you to create a minimal reproducible example so that anyone else can reproduce the error, debug it and tell you the answer. Very few people on SO are magic code mindreaders... Maybe you don't even need theArrayaspect at all; have you tried removing that and seeing if the crash continues? Maybe it's only theArray; what about removing theCString? - Shepmasterfrom_raw_partsstates that "ptr needs to have been previously allocated via String/Vec<T>" which is not the case here. - J.J. Hakala