-
Notifications
You must be signed in to change notification settings - Fork 13.4k
FFI + release mode + passing pointer to temporary value to C function produces an invalid result #136676
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
As per https://doc.rust-lang.org/stable/reference/destructors.html#r-destructors.scope.match-arm, as soon as the match arm ends, the result of It seems like rust didn't detect this because the other match arm had Here's the same behavior reproduced in pure Rust code (This code prints garbage values in release mode): struct DataFFI {
pub a: f32,
pub b: f32,
}
fn make_data(x: f32) -> DataFFI {
DataFFI { a: x, b: x }
}
fn do_sum(dat: Option<f32>) {
sum_me(match dat {
Some(x) => &make_data(x),
None => std::ptr::null(),
})
}
fn main() {
do_sum(None);
do_sum(Some(4.0));
}
#[inline(never)]
fn sum_me(dat: *const DataFFI) {
unsafe {
if !dat.is_null() {
println!("{}", (*dat).a);
}
}
} Running this code in Miri causes an error of use-after-free, as expected. |
Shouldn't rustc by itself be more aggressive at detecting such leaking dangling references/pointers? Miri doesn't find anything if it's not being run and this bug exists in sdl3-rs for two years. In case like this rustc knows object is dropped, as it drops automatically and copies pointer outside of drop. |
Clippy issues that propose detecting similar stuff: rust-lang/rust-clippy#2045, rust-lang/rust-clippy#5965, rust-lang/rust-clippy#7311, rust-lang/rust-clippy#10959. Existing rustc lint that detects similar stuff, but surprisingly doesn't detect the case of |
Found a relatively easy way to detect this UB(at least the case as was in sdl3, it now fixed). It "justs" needs MIR as the code gets down to two consequent MIR instructions: getting a pointer to something, then dropping that something. It can be detected by grep: $ grep -nP "= &raw (const|mut) (_\d+);\n\s+StorageDead\(\2\)" sdl3-0.12.0-5b12244.mir
118453: _0 = &raw const _3;
118454| StorageDead(_3);
118792: _8 = &raw const _12;
118793| StorageDead(_12);
118830: _13 = &raw const _18;
118831| StorageDead(_18);
119069: _13 = &raw const _17;
119070| StorageDead(_17);
119107: _18 = &raw const _23;
119108| StorageDead(_23);
119139: _24 = &raw const _29;
119140| StorageDead(_29);
151644: _5 = &raw const _9;
151645| StorageDead(_9); (It may not detect all cases, probably will not if pointer is taken to not the latest expr, but to expr in the middle) does compiler have a stage where it runs through MIR to check that generated code makes sense? |
Playing around with SDL3 crate I've met behavior which is interestingly even caught in C++:
foo(&bar())
where bar returns struct in C gives error like "Taking the address of a temporary object of type 'T'". When rust calls C function, it allows similar code and bad things might happen in release mode:The code (optionally) creates C struct and calls C func passing a pointer to it.
build.rs is done like this
C function is
I expected to see this happen: explanation
And this happens in debug mode: when dat is passed it contains what we want. But in release mode
Instead, this happened:
(Also interestingly in this run we have
-1833370100000000
without41065216
)It relies on both using address from return value and match. If we use
or
sum_dat(&make_data())
it works.Meta
rustc --version --verbose
:Backtrace
The text was updated successfully, but these errors were encountered: