为什么 Box 指针传递给 C 并返回给 Rust 段错误?

Why does a Box pointer passed to C and back to Rust segfault?

一些 C 代码调用 Rust open 调用,下面是 returns 一个指针。稍后,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);
}

问题是您在将指针转换回 Box 时没有将指针转换回 Handle 指针,并得到了错误类型的 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>

close 中,您最终创建了 Box<c_void> 而不是 Box<Handle>,因为您之前没有将 *mut c_void 转换回 *mut Handle调用 Box::from_raw.

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

顺便说一下,Box 实际上并没有为零大小的类型分配任何内存(例如这里的 Handle),而是使用固定的非零指针值(这, 在当前实现中,是类型的对齐方式;默认情况下,零大小类型的对齐方式为 1)。装箱的零大小类型的析构函数知道不要尝试在这个虚构的内存地址上释放内存,但是 c_void 不是零大小类型(它的大小为 1),因此 [=12= 的析构函数] 尝试释放地址 0x1 处的内存,这会导致段错误。