为什么Box指针传递给C并返回到Rust segfault?

时间:2019-02-08 10:25:10

标签: rust ffi

一些C代码调用到Rust open调用下面,该调用返回一个指针。后来,C代码将完全相同的指针传递回close函数,该函数试图删除(释放)它。它在free(3)中出现段错误。为什么?

use std::os::raw::{c_int, c_void};

struct Handle;

extern "C" fn open(_readonly: c_int) -> *mut c_void {
    let h = Handle;
    let h = Box::new(h);
    return Box::into_raw(h) as *mut c_void;
}

extern "C" fn close(h: *mut c_void) {
    let h = unsafe { Box::from_raw(h) };
    // XXX This segfaults - why?
    drop(h);
}

2 个答案:

答案 0 :(得分:8)

close中,您最终创建了Box<c_void>而不是Box<Handle>,因为在调用{之前,您没有将*mut c_void投射回*mut Handle {1}}。

Box::from_raw

顺便说一句,fn close(h: *mut c_void) { let h = unsafe { Box::from_raw(h as *mut Handle) }; drop(h); } 实际上并未为零大小的类型(例如此处的Box)分配任何内存,而是使用了固定的非零指针值(在当前的实现是类型的对齐方式;零尺寸类型的对齐方式默认为1)。装箱的零大小类型的析构函数知道不要尝试在此虚拟内存地址处释放内存,但是Handle不是零大小类型(其大小为1),因此{{1}的析构函数}尝试释放地址c_void上的内存,这会导致段错误。

答案 1 :(得分:4)

问题是您在将指针转换回Handle时没有将指针转换回Box指针,并得到了错误类型的Box

这有效:

fn close(h: *mut c_void) {
    let h = unsafe { Box::from_raw(h as *mut Handle) };
    //                               ^^^^^^^^^^^^^^
    drop(h);
}

在您的代码中,hstd::boxed::Box<std::ffi::c_void>