Skip to content

Commit ec1db97

Browse files
committed
Auto merge of rust-lang#3114 - RalfJung:fn-call-tests, r=RalfJung
ensure RET assignments do not get propagated on unwinding Mostly this adds a test for rust-lang/unsafe-code-guidelines#468, and then also some other related tests I thought of while writing that test.
2 parents a04b7a3 + e6637cb commit ec1db97

6 files changed

+135
-2
lines changed

src/tools/miri/tests/fail/function_calls/return_pointer_aliasing2.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
//@compile-flags: -Zmiri-tree-borrows
1+
// This does need an aliasing model.
2+
//@revisions: stack tree
3+
//@[tree]compile-flags: -Zmiri-tree-borrows
24
#![feature(raw_ref_op)]
35
#![feature(core_intrinsics)]
46
#![feature(custom_mir)]
@@ -25,6 +27,7 @@ pub fn main() {
2527
fn myfun(ptr: *mut i32) -> i32 {
2628
// This overwrites the return place, which shouldn't be possible through another pointer.
2729
unsafe { ptr.write(0) };
28-
//~^ ERROR: /write access .* forbidden/
30+
//~[stack]^ ERROR: tag does not exist in the borrow stack
31+
//~[tree]| ERROR: /write access .* forbidden/
2932
13
3033
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
2+
--> $DIR/return_pointer_aliasing2.rs:LL:CC
3+
|
4+
LL | unsafe { ptr.write(0) };
5+
| ^^^^^^^^^^^^
6+
| |
7+
| attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
8+
| this error occurs as part of an access at ALLOC[0x0..0x4]
9+
|
10+
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
11+
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
12+
help: <TAG> was created by a SharedReadWrite retag at offsets [0x0..0x4]
13+
--> $DIR/return_pointer_aliasing2.rs:LL:CC
14+
|
15+
LL | / mir! {
16+
LL | | {
17+
LL | | let _x = 0;
18+
LL | | let ptr = &raw mut _x;
19+
... |
20+
LL | | }
21+
LL | | }
22+
| |_____^
23+
help: <TAG> was later invalidated at offsets [0x0..0x4] by a Unique in-place function argument/return passing protection
24+
--> $DIR/return_pointer_aliasing2.rs:LL:CC
25+
|
26+
LL | unsafe { ptr.write(0) };
27+
| ^^^^^^^^^^^^^^^^^^^^^^^
28+
= note: BACKTRACE (of the first span):
29+
= note: inside `myfun` at $DIR/return_pointer_aliasing2.rs:LL:CC
30+
note: inside `main`
31+
--> $DIR/return_pointer_aliasing2.rs:LL:CC
32+
|
33+
LL | Call(_x = myfun(ptr), after_call)
34+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
35+
= note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
36+
37+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
38+
39+
error: aborting due to previous error
40+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Doesn't need an aliasing model.
2+
//@compile-flags: -Zmiri-disable-stacked-borrows
3+
#![feature(raw_ref_op)]
4+
#![feature(core_intrinsics)]
5+
#![feature(custom_mir)]
6+
7+
use std::intrinsics::mir::*;
8+
use std::panic;
9+
10+
#[repr(C)]
11+
struct S(i32, [u8; 128]);
12+
13+
#[custom_mir(dialect = "runtime", phase = "optimized")]
14+
fn docall(out: &mut S) {
15+
mir! {
16+
{
17+
Call(*out = callee(), after_call)
18+
}
19+
20+
after_call = {
21+
Return()
22+
}
23+
}
24+
}
25+
26+
fn startpanic() -> () {
27+
panic!()
28+
}
29+
30+
#[custom_mir(dialect = "runtime", phase = "optimized")]
31+
fn callee() -> S {
32+
mir! {
33+
type RET = S;
34+
let _unit: ();
35+
{
36+
// We test whether changes done to RET before unwinding
37+
// become visible to the outside. In codegen we can see them
38+
// but Miri should detect this as UB!
39+
RET.0 = 42;
40+
Call(_unit = startpanic(), after_call)
41+
}
42+
43+
after_call = {
44+
Return()
45+
}
46+
}
47+
}
48+
49+
fn main() {
50+
let mut x = S(0, [0; 128]);
51+
panic::catch_unwind(panic::AssertUnwindSafe(|| docall(&mut x))).unwrap_err();
52+
// The return place got de-initialized before the call and assigning to RET
53+
// does not propagate if we do not reach the `Return`.
54+
dbg!(x.0); //~ERROR: uninitialized
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
thread 'main' panicked at $DIR/return_pointer_on_unwind.rs:LL:CC:
2+
explicit panic
3+
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
4+
error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
5+
--> $DIR/return_pointer_on_unwind.rs:LL:CC
6+
|
7+
LL | dbg!(x.0);
8+
| ^^^^^^^^^ using uninitialized data, but this operation requires initialized memory
9+
|
10+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
11+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
12+
= note: BACKTRACE:
13+
= note: inside `main` at RUSTLIB/std/src/macros.rs:LL:CC
14+
= note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
15+
16+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
17+
18+
error: aborting due to previous error
19+

src/tools/miri/tests/pass/calls.rs

+16
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,26 @@ fn const_fn_call() -> i64 {
3434
x
3535
}
3636

37+
fn call_return_into_passed_reference() {
38+
pub fn func<T>(v: &mut T, f: fn(&T) -> T) {
39+
// MIR building will introduce a temporary, so this becomes
40+
// `let temp = f(v); *v = temp;`.
41+
// If this got optimized to `*v = f(v)` on the MIR level we'd have UB
42+
// since the return place may not be observed while the function runs!
43+
*v = f(v);
44+
}
45+
46+
let mut x = 0;
47+
func(&mut x, |v| v + 1);
48+
assert_eq!(x, 1);
49+
}
50+
3751
fn main() {
3852
assert_eq!(call(), 2);
3953
assert_eq!(factorial_recursive(), 3628800);
4054
assert_eq!(call_generic(), (42, true));
4155
assert_eq!(cross_crate_fn_call(), 1);
4256
assert_eq!(const_fn_call(), 11);
57+
58+
call_return_into_passed_reference();
4359
}

0 commit comments

Comments
 (0)