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

Ric*_*ich 5 ffi rust

一些C代码调用open下面的Rust 调用返回一个指针.后来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);
}
Run Code Online (Sandbox Code Playgroud)

Fra*_*gné 8

在close,你最终创建一个Box<c_void>而不是a,Box<Handle>因为你没有在调用之前将其转换*mut c_void回来.*mut HandleBox::from_raw

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

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


mca*_*ton 5

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

这有效:

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

在您的代码中,h是一个std::boxed::Box<std::ffi::c_void>.