一些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);
}
答案 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);
}
在您的代码中,h
是std::boxed::Box<std::ffi::c_void>
。