You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust FFI边界处的panic何时会被认定为未定义行为?

Rust FFI边界的Panic与未定义行为疑问

问题背景

《Rust Nomicon》中有如下示例:

#[no_mangle]
extern "C" fn assert_nonzero(input: u32) {
    assert!(input != 0)
}

若调用assert_nonzero时传入参数0,无论是否以panic=abort编译,运行时都会保证(安全地)终止进程。


而《Rust Reference》则明确指出:

被认定为未定义行为的情况:[...] 使用错误的调用ABI调用函数,或从具有错误unwind ABI的函数中展开。


这两种表述看似矛盾:assert_nonzero采用C ABI,但触发Rust panic时会展开栈。那它到底是保证安全终止进程,还是属于未定义行为(UB)?二者无法并存。

有观点明确指出这属于未定义行为,那么是《Rust Nomicon》已过时,还是存在理解误区?

更核心的问题是:Rust FFI边界处的panic究竟何时会被认定为未定义行为?

解答

1. 《Rust Nomicon》的表述问题

Nomicon中的这段内容存在表述不严谨或过时的问题。当前Rust官方规范(Reference)明确将“从具有错误unwind ABI的函数中展开”列为未定义行为——extern "C"函数的unwind ABI与Rust原生unwind机制不兼容,当这类函数内部触发panic并尝试展开时,就会触发UB。

所谓“安全终止”的表述并不准确:若编译时使用panic=abort,进程会直接终止,不会触发栈展开,这种情况是安全的;但如果使用默认的panic=unwind,栈展开会跨FFI边界,此时进程行为不可预测,无法保证“安全终止”。

2. FFI边界panic触发UB的判定条件

满足以下任一条件时,FFI边界的panic会被认定为未定义行为:

  • 被非Rust代码调用的Rust函数(使用非Rust ABI,如extern "C")内部触发panic,且编译时启用panic=unwind:栈展开会跨越FFI边界,外部代码的调用栈无法处理Rust的unwind机制。
  • Rust代码调用外部函数时,外部函数抛出与Rust unwind ABI不兼容的异常,且Rust代码未正确捕获处理:跨ABI的异常/ panic展开会触发UB。

以下情况则是安全的:

  • 编译Rust代码时使用panic=abort:panic发生时直接终止进程,不会触发栈展开,不会跨FFI边界产生问题。
  • Rust函数被外部调用时,内部确保不会触发panic,或通过catch_unwind捕获panic,避免栈展开到FFI边界。

内容的提问来源于stack exchange,提问作者ChrisB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 15:33:22