Peter Prographo
Peter Prographo

Reputation: 1331

C to Rust and back "pointer being freed was not allocated" on Rust side

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

Answers (1)

Shepmaster
Shepmaster

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

Related Questions