如何修复 VaList 的 "may not be safely transferred across an unwind boundary"?

How do I fix "may not be safely transferred across an unwind boundary" for VaList?

我正在尝试 override/wrap 使用 Rust 代码的 Libc vprintf(format, va_list) 函数。为此,我需要将 VaList 参数传递给还需要捕获展开错误的不安全代码:

#![feature(c_variadic)]
extern crate libc;

use libc::{c_char, c_int};

pub unsafe extern "C" fn vprintf(format: *const c_char, args: std::ffi::VaList) -> c_int {
    if true {
        ::std::panic::catch_unwind(|| hook_fn(format, args)).ok()
    } else {
        None
    }
    .unwrap_or_else(|| hook_fn(format, args))
}

pub unsafe fn hook_fn(format: *const c_char, args: std::ffi::VaList) -> c_int {
    0
}

fn main() {
    println!("Hello, world!");
}

我的代码无法编译:

error[E0277]: the type `&mut std::ffi::VaListImpl<'_>` may not be safely transferred across an unwind boundary
   --> src/main.rs:8:9
    |
8   |         ::std::panic::catch_unwind(|| hook_fn(format, args)).ok()
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ ------------------------ within this `[closure@src/main.rs:8:36: 8:60 format:&*const i8, args:std::ffi::VaList<'_, '_>]`
    |         |
    |         `&mut std::ffi::VaListImpl<'_>` may not be safely transferred across an unwind boundary
    |
    = help: within `[closure@src/main.rs:8:36: 8:60 format:&*const i8, args:std::ffi::VaList<'_, '_>]`, the trait `std::panic::UnwindSafe` is not implemented for `&mut std::ffi::VaListImpl<'_>`
    = note: `std::panic::UnwindSafe` is implemented for `&std::ffi::VaListImpl<'_>`, but not for `&mut std::ffi::VaListImpl<'_>`
    = note: required because it appears within the type `std::ffi::VaList<'_, '_>`
    = note: required because it appears within the type `[closure@src/main.rs:8:36: 8:60 format:&*const i8, args:std::ffi::VaList<'_, '_>]`

某些类型,尤其是 FFI 类型,如果在恐慌后使用,可能会导致未定义的行为。这种安全性通过类型是否实现 UnwindSafe 来跟踪,而 VaList 不实现。

这在错误消息的第一“帮助”行中有解释:

= help: within `[closure@src/main.rs:8:36: 8:61 format:&*const i8, args:std::ffi::VaList<'_, '_>]`, 
the trait `std::panic::UnwindSafe` is not implemented for `&mut std::ffi::VaListImpl<'_>`

第一个“注意”也给了你一个可能的解决方案:

note: `std::panic::UnwindSafe` is implemented for `&std::ffi::VaListImpl<'_>`, but not for `&mut std::ffi::VaListImpl<'_>`

它告诉您 可以安全地跨展开边界共享对 VaListImpl 的不可变引用。因此,您可以通过引用传递值来修复您的代码:

pub unsafe extern "C" fn vprintf(format: *const c_char, args: std::ffi::VaList) -> c_int {
    if true {
        ::std::panic::catch_unwind(|| hook_fn(format, &args)).ok()
    } else {
        None
    }
    .unwrap_or_else(|| hook_fn(format, &args))
}

pub unsafe fn hook_fn(format: *const c_char, args: &std::ffi::VaList) -> c_int {
    0
}

这有助于解决问题。 不确定这是否是解决此问题的正确方法。 Peter Halls 上面的建议可能仍然是正确的。但它似乎并没有为我修复错误。

#![feature(c_variadic)]
extern crate libc;

use libc::{c_char, c_int};

pub unsafe extern "C" fn vprintf(format: *const c_char, args: std::ffi::VaList) -> c_int {
    let ap : VaListImpl = args.clone();
    if true {
        ::std::panic::catch_unwind(|| hook_fn(format, ap.clone())).ok()
    } else {
        None
    }
    .unwrap_or_else(|| hook_fn(format, args))
}

pub unsafe fn hook_fn(format: *const c_char, args: std::ffi::VaList) -> c_int {
    0
}

fn main() {
    println!("Hello, world!");
}