Reputation: 1331
I'm trying to call some Rust code from C and get a result back, but I'm getting a "pointer being freed was not allocated" error on the Rust side.
I want to call the hex::encode
function. I pass in a pointer to some bytes, the length, and a pointer allocated in C with malloc. I want the result of the conversion passed back at this pointer.
The Rust function:
extern crate libc;
use hex;
use std::ffi::CString;
use std::os::raw::c_char;
use std::vec::Vec;
use std::{ffi, ptr, slice};
#[no_mangle]
pub extern "C" fn bytes_to_hex_string(bp: *mut u8, bp_size: usize, sp: *mut c_char) -> i8 {
println!("1");
let p_vec = unsafe { Vec::from_raw_parts(bp, bp_size, bp_size + 1) };
println!("2");
let str = hex::encode(p_vec);
println!("3");
let bytes = str.as_bytes();
println!("4");
let cs = CString::new(bytes).unwrap(); // will fail if bytes has "gap" (null) in sequence
println!("5");
unsafe {
libc::strcpy(sp, cs.as_ptr());
}
println!("6");
return 1;
}
Called from this C code:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int8_t bytes_to_hex( uint8_t *, unsigned int, char *);
int main() {
char *ptr = "Hello World!";
char *hex = (char *)malloc(1000);
printf("about to call....\n");
bytes_to_hex_string((uint8_t*)ptr,strlen(ptr),hex);
printf("about to print....\n");
printf("%s\n",hex);
printf("about to free....\n");
free(hex);
}
When I run the program:
$ ./a.out
about to call....
1
2
a.out(2941,0x7fffccae03c0) malloc: *** error for object 0x10afe2b80: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6
It looks like the Rust code inside the hex crate is freeing the ptr
pointer, is that right?
Is there a way round this somehow? I would prefer for the allocation and freeing to happen in C, but I am open to any suggestions to solve this!
Upvotes: 3
Views: 1223
Reputation: 432089
If you don't want Rust to deallocate your pointer, don't use types that own the allocation. Vec
and CString
both take ownership of the pointer and manage it. Presumably you were surprised to need to provide both a length and a capacity?
Use slice::from_raw_parts
instead:
extern crate libc;
extern crate hex;
use std::ffi::CString;
use std::os::raw::c_char;
use std::slice;
#[no_mangle]
pub unsafe extern "C" fn bytes_to_hex_string(bp: *mut u8, bp_size: usize, sp: *mut c_char) -> i8 {
let p_vec = slice::from_raw_parts(bp, bp_size);
let str = hex::encode(p_vec);
let cs = CString::new(str).unwrap();
libc::strcpy(sp, cs.as_ptr());
return 1;
}
I didn't test this, so I cannot say if your strcpy
is correct. Panicking in a FFI function is undefined behavior, however. It's also surprising that you declare both pointers as mutable... You also don't need to import Vec
as it's in the prelude.
See also:
Upvotes: 4