【问题标题】:Why does a Box pointer passed to C and back to Rust segfault?为什么 Box 指针传递给 C 并返回到 Rust 段错误?
【发布时间】:2019-02-08 10:25:10
【问题描述】:

一些 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);
}

【问题讨论】:

  • 请使用rustfmt 确保您的代码符合样式指南
  • 请注意,这与extern "C" 无关。尽管如此,它还是有段错误play.rust-lang.org/…
  • 如何让Box不掉线?虽然在示例中我的句柄是空的,但在实际代码中我想返回一个句柄,稍后将其传回给其他函数,然后在关闭时释放。
  • 你能举个例子来说明如何调用它吗? leak 似乎返回了内部 T,但这不能转换为最终的 void*
  • @E4_net_or_something_like_that Box::into_raw“忘记了”Box,所以Box不会open返回时被删除。

标签: rust ffi


【解决方案1】:

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

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

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

【讨论】:

  • 啊!我仍在搜索 为什么 它会出现段错误以添加到我的答案中。我怀疑有类似的事情,但还不确定。我比你早了几分钟,但你的回答要好得多!
【解决方案2】:

问题是您在将指针转换回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>

【讨论】:

    猜你喜欢
    • 2013-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多