我目前正在研究一个FFI库的安全绑定,这是我遇到的问题的最小实现。
fn test_weird_thing() {
use std::sync::Mutex;
struct WeirdThing<'a> {
vec: Mutex<Vec<i32>>,
callback_runner: &'a CallbackRunner,
}
// Hypothetical FFI Struct///
struct CallbackRunner {
callback: extern "C" fn(*const Mutex<Vec<i32>>),
data: *const Mutex<Vec<i32>>,
}
impl CallbackRunner {
extern "C" fn default_callback(_vec: *const Mutex<Vec<i32>>) {}
fn new() -> Self {
CallbackRunner {
callback: Self::default_callback,
data: std::ptr::null(),
}
}
fn set_callback(
&mut self, callback: extern "C" fn(*const Mutex<Vec<i32>>),
data: *const Mutex<Vec<i32>>
) {
self.data = data;
self.callback = callback;
}
fn run(&self) {
(self.callback)(self.data);
}
}
/////////////////////////////
impl<'a> WeirdThing<'a> {
fn new(runner: &'a mut CallbackRunner) -> Self {
let vec = Mutex::new(Vec::new());
runner.set_callback(Self::callback, &vec as *const Mutex<Vec<i32>>);
WeirdThing { vec, callback_runner: runner }
}
extern "C" fn callback(vec: *const Mutex<Vec<i32>>) {
let vec = unsafe { &*vec };
let mut vec = vec.lock().unwrap();
vec.push(1);
}
fn do_thing(&self) -> Vec<i32> {
self.callback_runner.run();
self.vec.lock().unwrap().clone()
}
}
let mut runner = CallbackRunner::new();
let thing = WeirdThing::new(&mut runner);
debug!("Thing: {:?}", thing.do_thing());
}
我认为当runner.run()
被调用时thing.vec
应该是有效的,因为它是在thing
仍然存在的时候被调用的,但是代码出现了异常。
我该如何解释这种行为?
1条答案
按热度按时间nszi6y051#
thing
在你调用回调函数的时候是有效的,问题是无论是将vec
添加到结构体还是从函数返回都会移动它,换句话说,它可能位于callback_runner.data
指向的内存位置之外的另一个内存位置。由于你正在解引用一个悬空指针,你的代码包含UB,幸运的是你得到了一个错误,而不是一个无声的失败。
要解决这个问题,您必须确保在创建指向
vec
的指针后,它不会在内存中移动,例如,将它与(固定的)Box
放在堆上。我已经画出了in this answer how to create a pointer to something you create within the function,你应该能够把它应用到你的问题中。