-
Notifications
You must be signed in to change notification settings - Fork 1.7k
New lint: potentially unwinding caller-supplied operators in unsafe
code
#9200
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
Would the lint need to consider |
Libraries typically can't choose their panicking mode (even if a crate had |
I would expect libraries to appear that only consider There is value on making developers aware of the issue, even if they are writing However, if the lint's message mentions e.g. "the operator may unwind" as suggested, it may be confusing for developers writing |
The lint could observe |
I understand the worry about having two dialects -- it is very valid. However, even if there were no abort-only library today:
To be clear, I am not trying to advocate for any side here regarding defaults or "what Rust should be". I am raising the
What do you mean by this? Do you mean something like "the compiler may still use unwinding under the hood to implement some feature"? Or do you mean something like "dependencies do not control the build profile" or "Rust/Cargo/crates.io do not provide a way to flag a library as |
Discussion about unwind soundness is a bit off topic: Removal of unwind from Current Rust semantics are that unwind may happen, but is not guaranteed to happen. Note that it's different from a guarantee that an unwind will not happen. Libraries that only support Libraries that only support
I mean definition what is sound in the language (i.e. whether Rust code is correct) is independent from policies of individual projects. "We only use There's one Rust language, without situational exceptions. Rust doesn't relax meaning but your original concern can be addressed either by checking the flag or adding |
That's a big stronger than I intended; I more meant that while I'm arguing against So in general I'm in favor of more allow-by-default But I also think saying operators unwinding is subtle/invisible the way drop unwinding is subtle/invisible is significantly overstating. Normal use of operators (e.g. The btw-operators-can-unwind lint thus shouldn't be limited to downstream types' operators; it should lint against any operator which is not known guaranteed to not unwind. (This is not an analysis of the function; this is a property of the signature having |
Not sure what you exactly mean by "checking the flag", but extra text wouldn't hurt (or a link if it is too long). As for the rest:
But it is still misleading when compiling under `-Cpanic=abort`. That is why I would say something like "may unwind when compiled under...".
Not sure why you bring all this up -- nobody has argued otherwise.
Correctness is all about the requirements. If some Rust code is only ever going to be used under a Rust's soundness: if we take the definition from https://doc.rust-lang.org/stable/reference/behavior-considered-undefined.html, then it is unclear how compilation options play with it. If one cannot cause UB with any safe code compiled with the required/documented/intended flags, should we consider it unsound? One could definitely read "any safe client" in the widest sense possible, and call it unsound. But such an interpretation may not be too useful for projects that intentionally disable unwinding and may want to design libraries where unwinding does not play a role. At any rate, the definition could be clarified. But in any case, even if the definition includes all compilation options, one can still create sound libraries by simply refusing to compile under the wrong setting: #[cfg(not(panic = "abort"))]
compile_error!("Designed for abort-only environments only. Please read the docs!");
But nobody is changing the language by refusing to compile, even going with your interpretation of soundness.
Sound Rust code, maybe, depending again on how soundness is defined (and even then, you can refuse to compile). But correct, no. As I mentioned above, I understand the worry about splitting the ecosystem, having libraries that cannot be used everywhere etc. But code is not incorrect just because your environment cannot use it; and if some projects or entities decide it is better for them to write |
soundness offtopicI'd like to add to my previous statements: you could rely on You couldn't do that in regular non-unsafe
This has been debated when actix-web had functions not marked as
I didn't mean in terms of stopping compilation, because when I've mentioned WASM, because it's a case where Rust's soundness rules require more than the platform typically does. Rust wants globals to be thread-safe always, for every project on every platform. However, almost every WASM project is strictly single-threaded. "But my program is always single-threaded" is a fine justification in C, but not a thing in Rust. If a library for WASM only was documented as "for single-threaded use only!" and had a safe API that could — purely in theory and never in practice — made to use |
off-topicThe terminology I have seen used the most is that a library is e.g. "sound under This is in practice the case for more library crates than you'd expect! A lot of crates will either refuse to compile or sometimes just be sound with The difficulty of being unsound with some compiler flags shouldn't be understated, though: if you just For a library to truly be properly sound, it needs to be sound under all build options present and future. If your API is not sound under some present or future option, then you need to gate it to show up only for options which you know are sound. All of this will hopefully be clearer and easier to talk about if/when the portability lint becomes a thing. Especially if this is a allow-by-default opt-in |
More "off-topic" @kornelski
I am well aware what In fact, what you suggest here (calling
That project allowed to introduce UB with safe code, regardless of target properties. Unless you are talking about something else, that discussion isn't relevant here.
I am aware of that. I sent the PR to add that to the Reference :)
I agree many C/C++ developers have differing views on UB, but not all. In fact, I have been working for a while on showing the value of having a similar soundness concept in the C standard in WG14 and also its UBSG (spare time permitting).
I think you haven't understood my argument, or I don't understand what you are trying to reply to here. My point was simple: even with the widest "any safe client" interpretation, you can still make sound, abort-only libraries by e.g. refusing to compile. That doesn't break any language rules as far as I am aware of.
If you are not able to trigger UB with any safe client, it would be a perfectly fine justification in Rust too.
Which rules would be broken by the suggested approaches, exactly? Let me try to summarize the discussion: in my reading, you apparently claimed abort-only code is (or should be considered) non-existing ("I haven't seen an abort-only library"), unsound ("I do not think that ignoring panics could be considered sound in Rust") and incorrect ("correct Rust code should be written as if unwinding could happen"). And my reply was that abort-only code almost definitely exists (if not today, tomorrow); it can be sound (even with the strictest interpretation of "any safe client", e.g. if you refuse to compile otherwise); and that code may be correct even if it is unsound ( If you agree that abort-only libraries are fine as long as e.g. they refuse to compile if unwinding is enabled, then there is no disagreement on my part (modulo concerns about an ecosystem split etc.). |
Even more off-topic @CAD97
Exactly. My only comment was that it could be a good idea for the Reference to add a bit of clarification on what constitutes "any safe client" and "any safe code", in particular around flags and target properties etc. For instance, for libraries in crates.io, "any safe client" should most likely be understood as anybody using the library, which includes potentially all flag combinations and targets. Thus one needs to refuse compilation (or similar) to be properly sound (and, even if that was not the definition, one should do it anyhow to prevent mistakes). But someone can also argue then that inside something like an internal kernel library, "any safe client" would be e.g. drivers, and those cannot cause UB even if that library does not include a To be clear, I am happy with the strict interpretation and calling that also "unsound". I just would like that the Reference establishes it. Maybe a footnote or something?
Indeed, this reason is exactly why I was careful to use the
Yeah, seems fair. I just wanted that I would still tweak it to "this operation may unwind under |
@ojeda additional message will bring noise either way. Maybe not too bad but maybe OTOH maybe it's OK, I've seen libraries that deliberately don't support 16 bit systems because they handle network messages larger than 2^16 bytes long. If handling unwinds is completely impractical (internal kenel libraries) forcing them to do it is just worse. |
@Kixunil I agree it makes them less useful, of course -- it is a fact that less users would be able to use them. I also acknowledged the ecosystem split concern. But that does not mean specialized libraries should be shunned (e.g. called "incorrect") just because they are not useful for everybody. Most libraries will anyway still support But if, somehow, the majority of libraries suddenly started to only provide support for |
@Kixunil I don't mean they would be just less useful. I mean they would be unsound. I mean that every library has to preserve soundness when compiled with panic=unwind. It can be broken useless junk, but it has to do something to be sound. Soundness is an objective property that always applies, not something that is context-dependent and can be opted out by the code author. The same way Rust requires all code to be thread-safe, even if the code doesn't support being run on a platform that has threads. It's valid to call
abort() or compile_error! , but it's not valid to do nothing and cause unsafety/UB in a non-supported configuration. |
@kornelski I believe @ojeda claims that |
There exist more than a few clippy lints that are only applicable most of the time, so not including this one or adding extra verbosity to the diagnostic on the basis of a possible particular set of circumstances that we don't know even exists in practice seems strange. |
No, I didn't suggest that. In fact, I was the one that suggested the |
There is no extra verbosity for the common case with what I suggested.
Being less confusing and more correct does not seem like a "strange" reason to me. |
@ojeda I mean for clippy to detect that pattern as opposed to flag being set. If that's what you had in mind, then fine, just your repeated |
So there is the lint discussion and the soundness discussion (which is why we were all hiding the latter as off-topic). For the former, yes, my suggestion was about the flag (more precisely, the panic strategy of the compilation session), i.e. if For the latter, my |
What it does
If a non-
unsafe
generic function containingunsafe
block uses an operator that is implemented by the caller (viacore::ops::*
) the lint warns that the operator may unwind and suggestscatch_unwind
or calling the method explicitly to signal the programmer thought about it.Related: #3915
See also: rust-lang/rfcs#3288 (comment)
Lint Name
suspicious-operators-in-unsafe
Category
suspicious
Advantage
Drawbacks
It's quite possible there will be a bunch of FPs. Maybe it'll have to be off by default.
Example
Could be written as:
or:
The text was updated successfully, but these errors were encountered: