悬空指针安全吗?
Is it safe to have a dangling pointer?
我有以下 Rust 代码:
extern crate libc; // 0.2.66
use libc::{free, malloc};
fn main() {
unsafe {
let mut p = malloc(16);
let mut q = p;
free(p);
p = std::ptr::null_mut();
q = std::ptr::null_mut();
}
}
在 free
之后更改 p
和 q
是否是未定义的行为,就像在此代码中一样?在我的真实代码中,q
会长时间保持悬垂指针,直到清理代码可以 运行 在其上,但在那段时间它不会被使用。
Rust 的原始指针本身永远不会导致未定义的行为。由于此代码仅调用 malloc
并将值传回 free
,如果那些 C 函数将调用序列定义为 UB,则它只能是 UB,而它们没有。
悬挂指针不是问题 — 解引用悬挂(或无效)指针是个问题。这不会发生在这里。
这与 Rust 的引用不同,Rust 的引用总是保证引用一个有效的值,并且永远不会悬空。这个例子是未定义的行为:
let mut p = malloc(16);
free(p);
&*p;
error: Miri evaluation error: dangling pointer was dereferenced
--> src/main.rs:9:9
|
9 | &*p;
| ^^^ Miri evaluation error: dangling pointer was dereferenced
|
= note: inside call to `main` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67:34
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:73
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/sys_common/backtrace.rs:129:5
= note: inside call to `std::sys_common::backtrace::__rust_begin_short_backtrace::<[closure@DefId(1:6016 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:13
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:296:40
= note: inside call to `std::panicking::r#try::do_call::<[closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:272:13
= note: inside call to `std::panicking::r#try::<i32, [closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe]>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:394:14
= note: inside call to `std::panic::catch_unwind::<[closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:51:25
= note: inside call to `std::rt::lang_start_internal` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67:5
= note: inside call to `std::rt::lang_start::<()>`
是这样的:
let mut p = malloc(16);
free(p);
p = std::ptr::null_mut();
&*p;
error: Miri evaluation error: invalid use of NULL pointer
--> src/main.rs:10:9
|
10 | &*p;
| ^^^ Miri evaluation error: invalid use of NULL pointer
|
= note: inside call to `main` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67:34
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:73
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/sys_common/backtrace.rs:129:5
= note: inside call to `std::sys_common::backtrace::__rust_begin_short_backtrace::<[closure@DefId(1:6016 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:13
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:296:40
= note: inside call to `std::panicking::r#try::do_call::<[closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:272:13
= note: inside call to `std::panicking::r#try::<i32, [closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe]>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:394:14
= note: inside call to `std::panic::catch_unwind::<[closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:51:25
= note: inside call to `std::rt::lang_start_internal` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67:5
= note: inside call to `std::rt::lang_start::<()>`
我有以下 Rust 代码:
extern crate libc; // 0.2.66
use libc::{free, malloc};
fn main() {
unsafe {
let mut p = malloc(16);
let mut q = p;
free(p);
p = std::ptr::null_mut();
q = std::ptr::null_mut();
}
}
在 free
之后更改 p
和 q
是否是未定义的行为,就像在此代码中一样?在我的真实代码中,q
会长时间保持悬垂指针,直到清理代码可以 运行 在其上,但在那段时间它不会被使用。
Rust 的原始指针本身永远不会导致未定义的行为。由于此代码仅调用 malloc
并将值传回 free
,如果那些 C 函数将调用序列定义为 UB,则它只能是 UB,而它们没有。
悬挂指针不是问题 — 解引用悬挂(或无效)指针是个问题。这不会发生在这里。
这与 Rust 的引用不同,Rust 的引用总是保证引用一个有效的值,并且永远不会悬空。这个例子是未定义的行为:
let mut p = malloc(16);
free(p);
&*p;
error: Miri evaluation error: dangling pointer was dereferenced
--> src/main.rs:9:9
|
9 | &*p;
| ^^^ Miri evaluation error: dangling pointer was dereferenced
|
= note: inside call to `main` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67:34
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:73
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/sys_common/backtrace.rs:129:5
= note: inside call to `std::sys_common::backtrace::__rust_begin_short_backtrace::<[closure@DefId(1:6016 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:13
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:296:40
= note: inside call to `std::panicking::r#try::do_call::<[closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:272:13
= note: inside call to `std::panicking::r#try::<i32, [closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe]>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:394:14
= note: inside call to `std::panic::catch_unwind::<[closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:51:25
= note: inside call to `std::rt::lang_start_internal` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67:5
= note: inside call to `std::rt::lang_start::<()>`
是这样的:
let mut p = malloc(16);
free(p);
p = std::ptr::null_mut();
&*p;
error: Miri evaluation error: invalid use of NULL pointer
--> src/main.rs:10:9
|
10 | &*p;
| ^^^ Miri evaluation error: invalid use of NULL pointer
|
= note: inside call to `main` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67:34
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:73
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/sys_common/backtrace.rs:129:5
= note: inside call to `std::sys_common::backtrace::__rust_begin_short_backtrace::<[closure@DefId(1:6016 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]::{{closure}}[0]) 0:&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:52:13
= note: inside call to closure at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:296:40
= note: inside call to `std::panicking::r#try::do_call::<[closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panicking.rs:272:13
= note: inside call to `std::panicking::r#try::<i32, [closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe]>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/panic.rs:394:14
= note: inside call to `std::panic::catch_unwind::<[closure@DefId(1:6015 ~ std[49a3]::rt[0]::lang_start_internal[0]::{{closure}}[0]) 0:&&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe], i32>` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:51:25
= note: inside call to `std::rt::lang_start_internal` at /root/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libstd/rt.rs:67:5
= note: inside call to `std::rt::lang_start::<()>`