Re: Linux kernel in-tree Rust support

From: Geoffrey Thomas
Date: Sun Aug 23 2020 - 17:54:57 EST


On Mon, 24 Aug 2020, Adrian Bunk wrote:

In librsvg, breakages with more recent Rust versions in the past year
required updates of two vendored crates:

Interesting!

https://gitlab.gnome.org/GNOME/librsvg/-/commit/de26c4d8b192ed0224e6d38f54e429838608b902

Looks like this was, for a while, a warning and not an error:

https://github.com/servo/rust-cssparser/commit/3c98d22c5de3b696bf1fde2b6c90069812312aa6

= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future

https://gitlab.gnome.org/GNOME/librsvg/-/commit/696e4a6be2aeb00ea27945f94da066757431684d

Same here, and it looks like the same warning/error, too:

https://github.com/dimforge/nalgebra/issues/561

= warning: this error has been downgraded to a warning for backwards compatibility with previous releases
= warning: this represents potential undefined behavior in your code and this warning will become a hard error in the future

Following some links in that second issue, I see that this seems to be a summary of what's going on:

https://github.com/rust-lang/rust/issues/59159

in particular this paragraph: "at the time that NLL [non-lexical lifetimes] was stabilized, the compiler's acceptance of this borrowing pattern was categorized by the NLL team as a 'known bug'. The NLL team assumed that, as a bug fix, the compiler would be allowed to start rejecting the pattern in the future."

That is, both of these cases were code that should never have been accepted by the compiler because it is unsound, but the initial implementation of NLL was not clever enough to detect it. They later turned it into a warning, and later turned that into an error.

There is a link to this policy document which explains their process for breaking existing code in the case where it's necessary to fix a compiler bug or similar:

https://github.com/rust-lang/rfcs/blob/master/text/1589-rustc-bug-fix-procedure.md

There is also a link to this comment about why they decided to take this approach:

https://github.com/rust-lang/rust/pull/58739#issuecomment-476387184

which includes the followup task, "We do a retrospective and look to ways to alter our processes to better prevent this in the future." That is, it seems to me that the Rust team sees it as a mistake that they ended up in this situation.

Josh, do you know if that retrospective has happened? (I see some mumbling about NLL -> Polonius, can we be confident something similar won't happen with that? :) )

For updating Rust in Debian stable for the next Firefox ESR update it
would actually be useful if these violations of the "hard stability
guarantee" in Rust get fixed, so that the old librsvg 2.44.10 builds
again with the latest Rust.

It also makes me wonder how such regressions slip into Rust releases.

I do conceptually agree with this, even though it is not technically a "regression" (because it was really the old compiler that was buggy, not the new one). If I understand it right, neither of these lines of code were valid with the pre-NLL implementation either. They were only accepted by the initial NLL implementation. While the purpose of NLL was to enable writing (valid/sound) code that wouldn't have been accepted by the previous, simpler implementation of lifetimes, it seems like it should have been possible to opt out of it while there were "known bugs" precisely to prevent these sort of effective-regressions. (And I suspect that it was in fact possible to do so, but perhaps not documented clearly / perhaps there wasn't an awareness that this was a thing that users who deeply value stability would want to do.)

And yeah, I can see the value of a --accept-previously-accepted-buggy-code flag from the distros' point of view (even if I can also see why Rust upstream would not be super excited about it :) ). After all, if the choice is between the distro not taking _any_ bug fixes in rustc and the distro taking all but one, the latter seems like a better option.

The other takeaway, I think, is that you should regularly compile with warnings turned into hard errors. The policy document above (Rust RFC 1589) says that all such changes need to be a warning and not a hard error for at least one compiler reversion. That happened in this case, and both fixes were applied in the vendored crates while they were still warnings. For any kernel Rust use, I'd strongly advocate for running CI on every stable branch after each compiler release, preferably after each compiler beta release, and shaking out any warnings - even if they are not warnings the compiler will turn into errors on its own, they may still point to logic bugs in the code.

(The Rust folks have some automation named "crater" for running these sorts of tests across the Rust ecosystem, which the document mentions. I'd expect that anything shipped in major distros, including librsvg + its dependencies as well as any Rust in the kernel, should be included in crater.)

--
Geoffrey Thomas
https://ldpreload.com
geofft@xxxxxxxxxxxxx