-
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
Add mem_uninitialized lint to FCW lint on uses #100342
Conversation
r? @wesleywiser (rust-highfive has picked a reviewer for you, use r? to override) |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
90c3c9f
to
b427392
Compare
This comment has been minimized.
This comment has been minimized.
Hey! It looks like you've submitted a new PR for the library teams! If this PR contains changes to any Examples of
|
This comment has been minimized.
This comment has been minimized.
Did some tests compiling some representative rust programs, and it's worth noting that this would cause likely a fair chunk of nontrivial programs to warn (For example, Also the message might need adjusting, it currently says I still feel a warning is really the best choice to push crates to update their dependencies / remove their use of mem::uninit, but it's also probably worth keeping in mind just how many people will see it. |
and now that i think of it this is probably more @rustbot label -T-compiler T-lang i think. maybe not API? Not sure! |
Nominating for Lang Team discussion: should we lint on |
This PR, as implemented, would be all uses, even definitely or possibly non-UB ones, since at the call-site, we might not know the type, and nearly all uses of mem::uninit are UB anyways. The best we could likely do and still be useful is to skip known non-UB calls, of which I doubt there really are any. And yeah, I don't know if this is T-Lang or T-Libs or both, but #98862 (comment) was nominated for T-libs and consensus seemed to be that a FCW was a good idea. |
Can we get a better understanding of the actual path most users will have to fixing this? Is it cargo update, or do they need to actually go and edit their Cargo.toml (moving to an entirely different crate or across semver versions)? And, if the majority of users do need to actually make a semver-incompatible change here, is that something we can help with (e.g., by pushing some key crates to release point releases of older versions)? I am pretty wary of false-positive warnings for uses of uninitialized that aren't currently UB, particularly if we're telling a very large amount of people to take some non-trivial action (e.g., more than cargo update). I think that sets a bad precedent and may discourage people from trusting such notices in the future. For example, looking at itoa (since you mentioned it), I believe we're warning as a result of this call. Given we've merged #99182, this is just slow but not actually language UB anymore. It's debatable whether it's library UB -- but it seems like, in general, not something we need a FCW to warn users to move off of in their dependencies: doing so would cause more noise than benefit, I think. It seems likely that we can detect at least this kind of case (concrete types, no generics), so I would expect it to be possible to avoid linting here. Maybe the invalid_value lint could be reused here (not familiar with its exact semantics). If that buys us avoiding warnings for ~10-20% of Rust users, that's still very significant, and seems not unlikely. |
In the case of itoa specifically, this was brought up upstream in dtolnay/itoa#36 with the response
But yeah, the fix was made on a breaking change release of the library, (0.4.8 -> 1.0.0), so
Of the 1.7 million monthly downloads of itoa 0.4.8, that accounts for around 1.6 million of them.
As for more UB cases, a fair few cases of UB were found in #99389 (even where we needed to allow uninit Yeah, we could use a modified form of the invalid_value lint here. Invalid_value lints about known to be UB calls, but it also lints for mem::zeroed and MaybeUninit::uninit().assume_init() (and even unsafe fn uninit<T>() -> T { mem::uninitialized() } We could do a very targeted fix of "if it's known to be a |
After looking at more failures (by using the https://github.com/mTvare6/hello-world.rs repo as a repo that has a lot of dependencies, so the check runs on a lot of somewhat representative code), ignoring just I'll try and write a more targeted check, as a "invalid_values but only caring about mem::uninitialized and falling back to linting, instead of ignoring". For reference, the current set of hit crates on that repo is |
Ignoring just known to be ints/floats/raw pointers (so a generic T will still lint) gets us |
This comment has been minimized.
This comment has been minimized.
compiler/rustc_lint/src/builtin.rs
Outdated
/// ```rust,compile_fail | ||
/// #![deny(mem_uninitialized)] | ||
/// fn main() { | ||
/// let x: i32 = unsafe { std::mem::uninitialized() }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is blocked on #98919.
compiler/rustc_lint/src/builtin.rs
Outdated
|
||
/// Return `None` only if we are sure this type does | ||
/// allow being left uninitialized. | ||
fn ty_find_init_error<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<InitError> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this so clever?
I think the idea is to just warn on all uses of this function. Basically this is a very strong form of deprecation. We might even do this via a new attribute rather than a specific lint -- #[extremely_deprecated]
or so, which makes the existing deprecation lint into an FCW lint.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, looks like this was specifically requested by @Mark-Simulacrum. However my understanding from previous discussions was that we didn't want to differentiate here -- we want to slowly work towards removing that function entirely. If we had a time machine we'd erase it from history; lacking that we will remove it as much as we possibly can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm not sure if we agreed on exactly when it should lint. #100342 (comment) seemed to say it's better off ignoring cases like [u8; 64]
or similar.
"Basically just a lint looking for use of the mem_uninitialized diagnostic item" is what this was originally implemented as, and I can revert it back to that if we agree that's fine.
But if we wanted to reduce noise, then we could ignore things that are not UB (assuming the 0x01 filling or a hypothetical freeze
).
Personally I think we've had MaybeUninit for long enough and that a full-on FCW for any mention of mem::uninitialized
is warranted, but it would be noisy for some time while the ecosystem upgrades.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at it again, the guidance we got from the lang team is ambiguous, so I asked for clarification in that thread.
@rustbot label -I-lang-nominated -T-lang +T-libs-api +I-libs-api-nominated Given that this issue is spawned from #98862, which is tagged as libs-api, we are relabeling this to libs-api. Our sense is that the decision of what is UB remains T-lang purview, but the decision of the stdlib surface (e.g., whether to deprecate, remove things, etc) is T-libs-api. |
Errors now look like: (hyper, only the first one since there's 2 instances of the same exact error here) The package `hyper v0.13.10` currently triggers the following future incompatibility lints:
> warning: the type `[httparse::Header; 100]` does not permit being left uninitialized
> --> /home/jess/.cargo/registry/src/github.com-1ecc6299db9ec823/hyper-0.13.10/src/proto/h1/role.rs:120:77
> |
> 120 | let mut headers: [httparse::Header<'_>; MAX_HEADERS] = unsafe { mem::uninitialized() };
> | ^^^^^^^^^^^^^^^^^^^^
> | |
> | this code causes undefined behavior when executed
> | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
> |
> = note: `#[allow(mem_uninitialized)]` on by default
> = note: for more information, see FIXME: fill this in
> note: references must be non-null (in this struct field)
> --> /home/jess/.cargo/registry/src/github.com-1ecc6299db9ec823/httparse-1.7.1/src/lib.rs:660:5
> |
> 660 | pub name: &'a str,
> | ^^^^^^^^^^^^^^^^^
> Old Old crossbeam looks like The package `crossbeam v0.2.12` currently triggers the following future incompatibility lints:
> warning: the type `T` is generic, and might not permit being left uninitialized
> --> /home/jess/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-0.2.12/src/sync/ms_queue.rs:66:45
> |
> 66 | payload: Payload::Data(unsafe { mem::uninitialized() }),
> | ^^^^^^^^^^^^^^^^^^^^
> | |
> | this code causes undefined behavior when executed
> | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
> |
> = note: `#[allow(mem_uninitialized)]` on by default
> = note: for more information, see FIXME: fill this in
> = note: type might not be allowed to be left uninitialized
>
> warning: the type `[std::cell::UnsafeCell<(T, std::sync::atomic::AtomicBool)>; 32]` is generic, and might not permit being left uninitialized
> --> /home/jess/.cargo/registry/src/github.com-1ecc6299db9ec823/crossbeam-0.2.12/src/sync/seg_queue.rs:40:28
> |
> 40 | data: unsafe { mem::uninitialized() },
> | ^^^^^^^^^^^^^^^^^^^^
> | |
> | this code causes undefined behavior when executed
> | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
> |
> = note: `#[allow(mem_uninitialized)]` on by default
> = note: for more information, see FIXME: fill this in
> note: type might not be allowed to be left uninitialized (in this struct field)
> --> /home/jess/src/rust/library/core/src/cell.rs:1869:5
> |
> 1869 | value: T,
> | ^^^^^^^^
> |
So when I have something like what hyper does in my own crate, I now get two warnings -- one from |
Yeah. It's unfortunate. I could not emit an |
This is now implemented. Output from doing fn main() {
let _x: char = unsafe { std::mem::uninitialized() };
} looks like # cargo +source check
Checking abcdef v0.1.0 (/tmp/scratchaBCBdAv6h)
warning: use of deprecated function `std::mem::uninitialized`: use `mem::MaybeUninit` instead
--> src/main.rs:2:39
|
2 | let _x: char = unsafe { std::mem::uninitialized() };
| ^^^^^^^^^^^^^
|
= note: `#[warn(deprecated)]` on by default
warning: the type `char` does not permit being left uninitialized
--> src/main.rs:2:29
|
2 | let _x: char = unsafe { std::mem::uninitialized() };
| ^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| this code causes undefined behavior when executed
| help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
= note: `#[warn(mem_uninitialized)]` on by default
= note: for more information, see FIXME: fill this in
= note: characters must be a valid Unicode codepoint
warning: `abcdef` (bin "abcdef") generated 2 warnings
Finished dev [unoptimized + debuginfo] target(s) in 0.07s
warning: the following packages contain code that will be rejected by a future version of Rust: abcdef v0.1.0 (/tmp/scratchaBCBdAv6h)
note: to see what the problems were, use the option `--future-incompat-report`, or run `cargo report future-incompatibilities --id 8` Might be a good idea to merge the checks for invalid_value and mem_uninitialized and pass in another argument indicating which form of the checks we're doing, since right now it is duplicated code. |
☔ The latest upstream changes (presumably #98919) made this pull request unmergeable. Please resolve the merge conflicts. |
The compiler changes look good to me but since this is r? rust-lang/libs |
I'm in favor of a FCW warning here, but will punt to someone on libs-api. |
c308c04
to
96a2e65
Compare
Created #101570 as a tracking issue for the FCW, and put that in the code. |
☔ The latest upstream changes (presumably #102377) made this pull request unmergeable. Please resolve the merge conflicts. |
any updates here? @joshtriplett @5225225 |
I don't think it's a good idea to add an FCW for |
Makes sense. I'll adjust the code to instead lint for that. |
Cleaning up old PRs that I won't get to, don't have the time. Happy to help anyone pick this up again, though. |
This PR adds a lint that will detect uses of
std::mem::uninitialized
(using themem_uninitialized
diagnostic item) that are not definitely of "basic" types like int/float/raw pointer, and emit a FCW lint for it.For generic cases, we can't say it's definitely UB, just that it might be.
Background: #98862 (comment)