-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Unsafe checking skips pointer dereferences in unused places #80059
Comments
Probably P-low as it's sound as-is, and has been this way for over 3 years. |
Why is this sound? Because the dereference gets optimized away? |
I think so. If you look at the MIR (in debug mode, so this should only have 'guaranteed' optimizations): // WARNING: This output format is intended for human consumers only
// and is subject to change without notice. Knock yourself out.
fn foo(_1: *const bool) -> () {
debug ptr => _1; // in scope 0 at src/lib.rs:1:8: 1:11
let mut _0: (); // return place in scope 0 at src/lib.rs:1:26: 1:26
scope 1 {
}
bb0: {
_0 = const (); // scope 0 at src/lib.rs:1:26: 3:2
return; // scope 0 at src/lib.rs:3:2: 3:2
}
} You'll see that the dereference never happens. I think this is because the wildcard pattern ( |
It's because fn foo(ptr: *const bool) {
*ptr;
} the error is still there even though LLVM also optimizes away that dereference. |
Assigning |
A few more examples: pub struct S {
x: (),
y: u32,
}
pub fn f(s: *const S) {
let S { x: (), .. } = *s;
}
pub fn g(a: *const u8) {
let 0u8..=255u8 = *a;
} It also affects behaviour of borrow checker, allowing following, which was disallowed back in 1.21: pub fn f() {
let _ = {
let a = 0;
&a
};
} |
Is this nominated for |
I'd like to tinker around and see what I can find :D |
Interestingly, this doesn't compile: fn foo(ptr: *const bool) {
let _ = { *ptr };
} Seems like the MIR builder immediately optimizes ignored results from EDIT: in the code above, the deref get optimized away later in the MIR opts, so it appears with |
The critical difference there, I think, is that the |
I think scottmcm is saying that adding curly braces makes a new value, rather than a "projection" on a |
Cc #79735 |
Yeah, curly braces force the presence of a place-to-value coercion, at which point a deref definitely happens. |
Another interesting variation that I do not think is covered by the comments above: fn foo(ptr: *const bool) {
let _: bool = *ptr;
} this also does not compile, due to unsafety-checking:
Update: Also, the span is wrong. 😆 |
What... is going on there? Why does adding a type annotation change anything?!? The only thing I can imagine is that this forces some kind of MIR to be emitted. But that sounds very wrong -- one can debate whether a |
I think the answer is going to be "because this is 'just a bug' that arose from the ad-hoc manner that some things are implemented in the compiler." If you look at this code in the compiler:
it is a spot where we have two special-cases, one for
I haven't yet 100% confirmed this hypothesis, but my current thinking is that these odd differences in behavior are arising due to differences in those three code paths. |
Yeah, something like that... e.g. I could imagine that the type ascription case emits a |
I think this is right. This is the dumped MIR: // MIR for `foo` 0 mir_map
| User Type Annotations
| 0: Canonical { max_universe: U0, variables: [], value: Ty(bool) } at foo.rs:2:12: 2:16
| 1: Canonical { max_universe: U0, variables: [], value: Ty(bool) } at foo.rs:2:12: 2:16
|
fn foo(_1: *const bool) -> () {
debug ptr => _1; // in scope 0 at foo.rs:1:8: 1:11
let mut _0: (); // return place in scope 0 at foo.rs:1:26: 1:26
scope 1 {
}
bb0: {
AscribeUserType((*_1), +, UserTypeProjection { base: UserType(1), projs: [] }); // scope 0 at foo.rs:2:12: 2:16
_0 = const (); // scope 0 at foo.rs:1:26: 3:2
return; // scope 0 at foo.rs:3:2: 3:2
}
} |
(Working on an experimental PR to not treat |
Previously, if the MIR had an AscribeUserType statement that ascribed a type to the pointee of a raw pointer, it would be treated as a dereference of the raw pointer for purposes of unsafe-checking. For example, the following code would be treated as containing a raw-pointer dereference: fn foo(ptr: *const bool) { let _: bool = *ptr; } Producing this error: error[E0133]: dereference of raw pointer is unsafe and requires unsafe function or block --> issue-80059.rs:2:12 | 2 | let _: bool = *ptr; | ^^^^ dereference of raw pointer Note that the error points to the type ascription as having a dereference! That's because the generated AscribeUserType MIR statement is treated as containing a dereference of `_1`: AscribeUserType((*_1), +, UserTypeProjection { base: UserType(1), projs: [] }); Now the unsafe-checker ignores uses inside `AscribeUserType` statements, which means this code now compiles successfully. ----- Note that this code: fn foo(ptr: *const bool) { let _: bool = *ptr; } does *not* produce an error (it compiles fine) because of the magical behavior of the `_` (wildcard) pattern (see rust-lang#80059).
This also compiles fn main(){
let uninit: &bool;
let _: bool = *uninit;
} Is this caused by the same bug? |
Yes, in general any checks that are done on the MIR will not happen for assignments to |
While the matching behaviors here may have the same root cause, I do not immediately classify that example as a bug. The idea is that
Having said that, I am not saying I would veto changing our behavior in this scenario. I guess it depends on whether one views the unsafety-checking and the initialization-checking as being on similar footing or not. |
It wasn't obvious to me that the initialization checking wasn't also a bug. But @pnkfelix said something in the meeting today that was persuasive to me: it's fundamentally flow-sensitive, not scope-based, so while this might feel strange, it fits. |
I feel like it would be rather strange to ignore |
We discussed this in a previous lang team meeting and concluded that we did not find that strange =) Unsafe checking is not flow sensitive (e.g., it applies to dead code), but borrow checking is. |
IMO we should fix this by having a special MIR statement for "evaluate this place and then discard the result (without place-to-value conversion)". Alternatively this can be expressed by using This would also help with Miri's rust-lang/miri#2360. |
Seen in #79735
I tried this code:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=44e42a069bc7601365f5f21a102178d9
Which, oddly, happily compiled despite the syntactic pointer dereference.
Back in 1.21, however, that gave the expected error:
https://rust.godbolt.org/z/rP93nf
The conversation in the 2020-12-15 lang team meeting implied that this was an unexpected regression, so opening and nomination for further discussion.
The text was updated successfully, but these errors were encountered: