-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Surprising case of UB arising from stacked borrow rules #128803
Comments
You are just rediscovering rust-lang/unsafe-code-guidelines#133. |
I don't think there's anything in this issue that is not already captured in the comments on ucg#133. So I'm going to close this and if you think you have something to add that's not already expressed on that issue, it would be much better for you to comment there. |
Thanks for the report! Yes, this is counter-intuitive. Writing unsafe code that mixes references and raw pointers is tricky business, and the worst case is when you don't even realize you are using any references.
I don't think this is an instance of that -- I expect this will still be UB even with Tree Borrows. The OP would likely be just as surprised by this UB: fn main() {
let mut b = MyBox::new(1);
let p = std::ptr::addr_of_mut!(*b);
unsafe { p.write(13); }
*b = 2;
assert_eq!( unsafe { p.read() }, 2);
} If this corresponds to any UCG issue it is rust-lang/unsafe-code-guidelines#450. It's also closely related to the UB that arises from Maybe |
I agree with @RalfJung: this is closely related to rust-lang/unsafe-code-guidelines#450 and not related to rust-lang/unsafe-code-guidelines#133. Though that @saethlin got this wrong reinforces my view that it's going to become impossibly tricky to write sound unsafe code if any of this ever becomes more than a MIRI experiment. Whatever final form these rules take needs to be something that programmers can understand and reason about, not something that empirically covers most real-world code but is only fully understood by a handful of people and everybody else just cargo-cults around. The Anyway, the example that Ralf gives at the top of the ucg#450 thread is even nastier than mine, and any plausible resolution to that issue will probably address this one too. So I think this issue can remain closed from here and I'll follow up on ucg#450 if I have any insight on it. |
This ticket arose from some toy programs I wrote while investigating the internals of the
ouroboros
crate and trying to wrap my head around whatAliasableDeref
is needed for, but the issue turned out to be more basic. Just to be sure that what I was seeing had nothing really to do withcore::ptr::Unique
or even withNonNull
, I wrote a trivial box implementation that uses neither of these:Now, the following program produces UB in MIRI:
But with a subtle change in how
p
is constructed, it runs cleanly:The difference is that, even despite my use of
addr_of_mut!
, in the first version I'm temporarily creating a&mut i32
returned byderef_mut
and implicitly casting it to a*mut i32
, rather than obtaining a*mut i32
directly. This subtle distinction causes my pointer to be infected with stacked-borrow cooties, and become invalidated after assigning to*b
. (Edit for clarification: it's not surprising that the reference is being created despiteaddr_of_mut!
; I wasn't expecting that to do anything load-bearing, it's just how I habitually create pointers. What's surprising is that the transient existence of the reference matters at all, since it's valid).This is quite counterintuitive, especially considering that the following practically line-for-line equivalent code is perfectly good C++:
For Rust, even unsafe Rust, to contain a landmine like this seems to go very much against its philosophy, and I see it as indictment of the stacked borrow experiment, or at least the current iteration of its design.
cc @RalfJung
The text was updated successfully, but these errors were encountered: