Hi everyone,
I am teaching myself about the Rust FFI and have created a small example library to better understand how to allocate and free memory associated with trait objects. The allocation and freeing of the memory occurs through a C-API. The problem is that running the test program that uses this library results in a segmentation fault.
I can isolate the problem to three lines of code, but I do not know why they result in a segmentation fault. I have also found that the problematic lines of code actually work when they are part of the binary program's code and not part of the library. Could anyone please explain these observations to me?
Thanks!
-kmd
Steps to reproduce
- Create a new library crate with the command
cargo new --lib ffi-test
- Change into the
ffi-test
directory and replace the contents of the filesrc/lib.rs
with the following:
lib.rs
use std::boxed::Box;
use std::os::raw::c_void;
struct Foo { }
pub trait Bar {
fn hello(&self) {
println!("Hello world!");
}
}
impl Bar for Foo { }
#[no_mangle]
pub extern "C" fn new() -> *mut c_void {
let foo = Foo { };
let bar = Box::new(foo) as Box<dyn Bar>;
let bar = Box::new(bar);
Box::into_raw(bar) as *mut c_void
}
#[no_mangle]
pub extern "C" fn free(bar: *mut c_void) {
if bar.is_null() {
return;
}
let bar = bar as *mut Box<dyn Bar>;
unsafe{
Box::from_raw(bar);
}
}
- Create the file
src/bin/main.rs
with the following contents:
main.rs
use::ffi_test::{free, new};
fn main() {
let c_pointer = new();
free(c_pointer);
}
- Run the program with
cargo run
Results
When I run the program the message Segmentation fault (core dumped)
is printed to my terminal window.
If I comment out the following lines inside the free(bar: *mut c_void)
function, then no segmentation fault occurs:
//unsafe{
// Box::from_raw(bar);
//}
Furthermore, if I keep these lines commented out and use unsafe{ Box::from_raw(...) }
from inside main.rs
, then the program actually works. For example, if I change main.rs
to look like this:
use::ffi_test::{Bar, free, new};
fn main() {
let c_pointer = new();
let pointer = c_pointer as *mut Box<dyn Bar>;
unsafe {
let bar = Box::from_raw(pointer);
bar.hello();
}
}
Then I see `Hello world!" printed to the screen.
Additional info
Note that inside the new()
method I am creating a Box
to the trait object Box<dyn Bar>
and then converting it to a raw pointer. It is the pointer to the trait object that is passed through the API.
$ rustc --version
rustc 1.32.0 (9fda7c223 2019-01-16)
$ uname -a
Linux xxx 4.15.0-48-generic #51~16.04.1-Ubuntu SMP Fri Apr 5 12:01:12 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux