ReferencePropagation introduces UB into code that is accepted by Stacked Borrows #132898
Open
Description
This program is accepted by Stacked Borrows:
fn main() {
struct Foo(u64);
impl Foo {
fn add(&mut self, n: u64) -> u64 {
self.0 + n
}
}
let mut f = Foo(0);
let alias = &mut f.0 as *mut u64;
let res = f.add(unsafe {
*alias = 42;
0
});
assert_eq!(res, 42);
}
That is a Stacked Borrows limitation; it is caused by the fact that 2-phase borrows cannot be modeled properly with just a Stack.
However, it also shows that defining an aliasing model that rejects this code is non-trivial, and we should be very careful with optimizations on such code until we have a clear plan for how to model this.
And yet, it turns out that running this code with mir-opt-level=2
introduces UB:
error: Undefined Behavior: trying to retag from <1484> for Unique permission at alloc702[0x0], but that tag does not exist in the borrow stack for this location
--> 2phase.rs:4:16
|
4 | fn add(&mut self, n: u64) -> u64 {
| ^^^^^^^^^
| |
| trying to retag from <1484> for Unique permission at alloc702[0x0], but that tag does not exist in the borrow stack for this location
| this error occurs as part of function-entry retag at alloc702[0x0..0x8]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <1484> was created by a SharedReadWrite retag at offsets [0x0..0x8]
--> 2phase.rs:11:15
|
11 | let res = f.add(unsafe {
| ^
help: <1484> was later invalidated at offsets [0x0..0x8] by a write access
--> 2phase.rs:14:9
|
14 | *alias = 42;
| ^^^^^^^^^^^
= note: BACKTRACE (of the first span):
= note: inside `main::Foo::add` at 2phase.rs:4:16: 4:25
note: inside `main`
--> 2phase.rs:11:15
|
11 | let res = f.add(unsafe {
| _______________^
12 | | // This is the access at fault, but it's not immediately apparent because
13 | | // the reference that got invalidated is not under a Protector.
14 | | *alias = 42;
15 | | 0
16 | | });
| |______^
This is quite surprising, I thought we were very conservative in terms of doing optimizations that rely on the aliasing model. I have not yet figured out where exactly this comes from.
Cc @rust-lang/opsem @rust-lang/wg-mir-opt @cjgillot