-
Notifications
You must be signed in to change notification settings - Fork 13.3k
mutable_transmutes lint should catch transmutes from a type without interior mutability to one with #111229
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
WG-prioritization assigning priority (Zulip discussion). @rustbot label -I-prioritize +P-high +T-compiler +A-LLVM +regression-from-stable-to-beta |
@rustbot claim |
FWIW, we've hit yet another case of such a transmute that a lint would have made obvious in the first place... |
While trying to find workarounds, I also figured out that the same footgun can be triggered with "manual" transmutes too... e.g.
or
I don't know if they exist in the wild, but a lint would sure be useful for those too. |
This lint should be quite a big priority IMO. Code like this is busted right now if
It's totally non-obvious that this code is broken, and totally non-obvious that LLVM will mis-compile your code unless |
AIUI this actually boils down to UnsafeCell. Any other interior mutability that counts for this problem will have an UnsafeCell hidden somewhere. |
BTW, |
…rior mutability. r=emilio aka one specific change in LLVM 16 introducing UB in Rust, take 3? 4? This time, we have multiple types like: #[xpcom(implement(nsISomething))] struct Foo { foo: RefCell<Something>, } impl Foo { fn from_interface(obj: &nsISomething) -> &Self { unsafe { ::std::mem::transmute(obj) } } } At first glance, this looks innocuous. But the problem is that nsISomething, as far as LLVM is informed by Rust, is readonly, but Foo, via the RefCell, has interious mutability. LLVM ends up assuming that any mutability that happens to that returned &Foo can't happen, and removes it. This is yet another case where rust-lang/rust#111229 would save our feet from this footgun LLVM 16 added and that the rust compiler doesn't help us prevent the least. Differential Revision: https://phabricator.services.mozilla.com/D183569
…rior mutability. r=emilio aka one specific change in LLVM 16 introducing UB in Rust, take 3? 4? This time, we have multiple types like: #[xpcom(implement(nsISomething))] struct Foo { foo: RefCell<Something>, } impl Foo { fn from_interface(obj: &nsISomething) -> &Self { unsafe { ::std::mem::transmute(obj) } } } At first glance, this looks innocuous. But the problem is that nsISomething, as far as LLVM is informed by Rust, is readonly, but Foo, via the RefCell, has interious mutability. LLVM ends up assuming that any mutability that happens to that returned &Foo can't happen, and removes it. This is yet another case where rust-lang/rust#111229 would save our feet from this footgun LLVM 16 added and that the rust compiler doesn't help us prevent the least. Differential Revision: https://phabricator.services.mozilla.com/D183569 UltraBlame original commit: e978ba270cf08576ca3f3155590465e102c31f2c
…rior mutability. r=emilio aka one specific change in LLVM 16 introducing UB in Rust, take 3? 4? This time, we have multiple types like: #[xpcom(implement(nsISomething))] struct Foo { foo: RefCell<Something>, } impl Foo { fn from_interface(obj: &nsISomething) -> &Self { unsafe { ::std::mem::transmute(obj) } } } At first glance, this looks innocuous. But the problem is that nsISomething, as far as LLVM is informed by Rust, is readonly, but Foo, via the RefCell, has interious mutability. LLVM ends up assuming that any mutability that happens to that returned &Foo can't happen, and removes it. This is yet another case where rust-lang/rust#111229 would save our feet from this footgun LLVM 16 added and that the rust compiler doesn't help us prevent the least. Differential Revision: https://phabricator.services.mozilla.com/D183569 UltraBlame original commit: e978ba270cf08576ca3f3155590465e102c31f2c
…rior mutability. r=emilio aka one specific change in LLVM 16 introducing UB in Rust, take 3? 4? This time, we have multiple types like: #[xpcom(implement(nsISomething))] struct Foo { foo: RefCell<Something>, } impl Foo { fn from_interface(obj: &nsISomething) -> &Self { unsafe { ::std::mem::transmute(obj) } } } At first glance, this looks innocuous. But the problem is that nsISomething, as far as LLVM is informed by Rust, is readonly, but Foo, via the RefCell, has interious mutability. LLVM ends up assuming that any mutability that happens to that returned &Foo can't happen, and removes it. This is yet another case where rust-lang/rust#111229 would save our feet from this footgun LLVM 16 added and that the rust compiler doesn't help us prevent the least. Differential Revision: https://phabricator.services.mozilla.com/D183569 UltraBlame original commit: e978ba270cf08576ca3f3155590465e102c31f2c
…rior mutability. r=emilio, a=dsmith aka one specific change in LLVM 16 introducing UB in Rust, take 3? 4? This time, we have multiple types like: #[xpcom(implement(nsISomething))] struct Foo { foo: RefCell<Something>, } impl Foo { fn from_interface(obj: &nsISomething) -> &Self { unsafe { ::std::mem::transmute(obj) } } } At first glance, this looks innocuous. But the problem is that nsISomething, as far as LLVM is informed by Rust, is readonly, but Foo, via the RefCell, has interious mutability. LLVM ends up assuming that any mutability that happens to that returned &Foo can't happen, and removes it. This is yet another case where rust-lang/rust#111229 would save our feet from this footgun LLVM 16 added and that the rust compiler doesn't help us prevent the least. Differential Revision: https://phabricator.services.mozilla.com/D183569
…rior mutability. r=emilio,a=dsmith aka one specific change in LLVM 16 introducing UB in Rust, take 3? 4? This time, we have multiple types like: #[xpcom(implement(nsISomething))] struct Foo { foo: RefCell<Something>, } impl Foo { fn from_interface(obj: &nsISomething) -> &Self { unsafe { ::std::mem::transmute(obj) } } } At first glance, this looks innocuous. But the problem is that nsISomething, as far as LLVM is informed by Rust, is readonly, but Foo, via the RefCell, has interious mutability. LLVM ends up assuming that any mutability that happens to that returned &Foo can't happen, and removes it. This is yet another case where rust-lang/rust#111229 would save our feet from this footgun LLVM 16 added and that the rust compiler doesn't help us prevent the least. Differential Revision: https://phabricator.services.mozilla.com/D183569
…rior mutability. r=emilio, a=dsmith aka one specific change in LLVM 16 introducing UB in Rust, take 3? 4? This time, we have multiple types like: #[xpcom(implement(nsISomething))] struct Foo { foo: RefCell<Something>, } impl Foo { fn from_interface(obj: &nsISomething) -> &Self { unsafe { ::std::mem::transmute(obj) } } } At first glance, this looks innocuous. But the problem is that nsISomething, as far as LLVM is informed by Rust, is readonly, but Foo, via the RefCell, has interious mutability. LLVM ends up assuming that any mutability that happens to that returned &Foo can't happen, and removes it. This is yet another case where rust-lang/rust#111229 would save our feet from this footgun LLVM 16 added and that the rust compiler doesn't help us prevent the least. Differential Revision: https://phabricator.services.mozilla.com/D183569 *** Bug 1831467 fix comment a=dsmith
@samueltardieu do you think it could be added as a clippy checker? |
…rior mutability. r=emilio, a=dsmith aka one specific change in LLVM 16 introducing UB in Rust, take 3? 4? This time, we have multiple types like: #[xpcom(implement(nsISomething))] struct Foo { foo: RefCell<Something>, } impl Foo { fn from_interface(obj: &nsISomething) -> &Self { unsafe { ::std::mem::transmute(obj) } } } At first glance, this looks innocuous. But the problem is that nsISomething, as far as LLVM is informed by Rust, is readonly, but Foo, via the RefCell, has interious mutability. LLVM ends up assuming that any mutability that happens to that returned &Foo can't happen, and removes it. This is yet another case where rust-lang/rust#111229 would save our feet from this footgun LLVM 16 added and that the rust compiler doesn't help us prevent the least. Differential Revision: https://phabricator.services.mozilla.com/D183569 *** Bug 1831467 fix comment a=dsmith
…rior mutability. r=emilio,a=dsmith aka one specific change in LLVM 16 introducing UB in Rust, take 3? 4? This time, we have multiple types like: #[xpcom(implement(nsISomething))] struct Foo { foo: RefCell<Something>, } impl Foo { fn from_interface(obj: &nsISomething) -> &Self { unsafe { ::std::mem::transmute(obj) } } } At first glance, this looks innocuous. But the problem is that nsISomething, as far as LLVM is informed by Rust, is readonly, but Foo, via the RefCell, has interious mutability. LLVM ends up assuming that any mutability that happens to that returned &Foo can't happen, and removes it. This is yet another case where rust-lang/rust#111229 would save our feet from this footgun LLVM 16 added and that the rust compiler doesn't help us prevent the least. Differential Revision: https://phabricator.services.mozilla.com/D183569
@ChayimFriedman2 any progress on this? |
@sagudev I'm working on it (slowly). It is not as simple, because to handle the example in the OP also requires looking for nested fields, which the current code does not do. |
Good luck. Clippy has |
Under Tree Borrows transmuting a |
@ChayimFriedman2 I think warn-by-default would be a better state than the current status quo. |
There apparently is a way to go around existing lints around transmutation of shared references into mutable pointers by passing them into functions. See example here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=fc92b7ea1550c45c3d978420b1036e91 Arguably, this sort of inconsistent warning behavior is even more dangerous then no warning at all... Hopefully you can address this at the same time. Thanks! |
@alexpyattaev It is extremely hard to lint on such cases. It will probably require inter-procedural analysis, or at least an extensive system of metadata about functions that rustc is unlikely to ever have.
Lints are on a best-effort basis. We lint when we can, and don't lint when we cannot. This is way better than not linting at all, especially considering that many of the incorrect usages will get caught. Also, since this is a new feature request, that should've been a new issue. |
Apologies, I simply misunderstood how the lint was intended/expected to work. By no means do I expect the compiler to hold my hand when I type unsafe and do strange stuff. My concern was motivated by one of my students finding this way to bypass the lint and thinking his code no longer had UB (since the error went away). As this lint was not intended to be a "reliable idiot stopper", probably it would be nice to have something like "stop and think, do not try to work around this lint" somewhere in there to prevent people from "finding workaround" for it. Or you can safely ignore this and let them learn the hard way to not upset the compiler =) |
@rustbot claim |
@dev-ardi I already have a working PR for this (#128351), don't know why I was unassigned. |
My apologizes, I have no idea why I unassigned when I even did a try job on the PR (#128351) |
…rior mutability. r=emilio, a=dsmith aka one specific change in LLVM 16 introducing UB in Rust, take 3? 4? This time, we have multiple types like: #[xpcom(implement(nsISomething))] struct Foo { foo: RefCell<Something>, } impl Foo { fn from_interface(obj: &nsISomething) -> &Self { unsafe { ::std::mem::transmute(obj) } } } At first glance, this looks innocuous. But the problem is that nsISomething, as far as LLVM is informed by Rust, is readonly, but Foo, via the RefCell, has interious mutability. LLVM ends up assuming that any mutability that happens to that returned &Foo can't happen, and removes it. This is yet another case where rust-lang/rust#111229 would save our feet from this footgun LLVM 16 added and that the rust compiler doesn't help us prevent the least. Differential Revision: https://phabricator.services.mozilla.com/D183569 *** Bug 1831467 fix comment a=dsmith
…rior mutability. r=emilio,a=dsmith aka one specific change in LLVM 16 introducing UB in Rust, take 3? 4? This time, we have multiple types like: #[xpcom(implement(nsISomething))] struct Foo { foo: RefCell<Something>, } impl Foo { fn from_interface(obj: &nsISomething) -> &Self { unsafe { ::std::mem::transmute(obj) } } } At first glance, this looks innocuous. But the problem is that nsISomething, as far as LLVM is informed by Rust, is readonly, but Foo, via the RefCell, has interious mutability. LLVM ends up assuming that any mutability that happens to that returned &Foo can't happen, and removes it. This is yet another case where rust-lang/rust#111229 would save our feet from this footgun LLVM 16 added and that the rust compiler doesn't help us prevent the least. Differential Revision: https://phabricator.services.mozilla.com/D183569
I'm filing this as a regression, although the summary is worded in a feature request-ish way.
So, the regression itself is this: code that transmutes from a type without interior mutability to one with interior mutability leads to really bad outcomes after the bump to LLVM 16, because of the changes in llvm/llvm-project@01859da. This is a pattern that happens in the wild, probably mostly around FFI. At least, that's how it happens in the Firefox codebase.
Code
Here is a reduced testcase using a similar pattern to what Firefox is using:
With rustc up to 1.69.0 in --release mode, this produces the following IR:
With rustc 1.70.0-beta.2 in --release mode, this produces the following IR:
Note how everything is gone because the input pointer is marked as noalias readonly, and thus the code is not expected to change what it points to, so it's all removed. This is the typical example of undefined behavior leading to the optimizer doing unexpected things. I'm not arguing that there isn't undefined behavior. The undefined behavior existed before. But with LLVM 16, now the undefined behavior is actively dangerous.
Now, to come back to the feature-request-y summary I wrote, the following code does not compile:
The produced error is:
I would argue that the code that is now compiled to nothing should also produce a similar error, and that error should be shipped in 1.70.0.
Cc: @pcwalton, @nikic
The text was updated successfully, but these errors were encountered: