为什么 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);
}
在您的代码中,h
是 std::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
处的内存,这会导致段错误。
一些 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);
}
在您的代码中,h
是 std::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
处的内存,这会导致段错误。