Skip to content

Instantly share code, notes, and snippets.

@ChayimFriedman2
Last active August 5, 2024 14:40
Show Gist options
  • Save ChayimFriedman2/c41e2012834debde79a7059576e79996 to your computer and use it in GitHub Desktop.
Save ChayimFriedman2/c41e2012834debde79a7059576e79996 to your computer and use it in GitHub Desktop.
Detailed analysis of crater results for https://github.com/rust-lang/rust/pull/128351

Detailed analysis of crater results for rust-lang/rust#128351

Summary: all crates that the lint triggered on contained actual UB (both in Stacked Borrows and in Tree Borrows). Not all instances are UB (some are only reads), though, but this clearly show the pattern is dangerous. However, there are some popular crates that do that.

Most are conversions from &NonAtomic to &Atomic.

Lint fired: mutable_transmutes

Usage status: no dependents, archived.

Status: does & to &mut transmutes, which are definitely UB (but are hidden behind Pin, and so weren't caught before).

It also violates the uniqueness of &mut, since those &mut are aliased with other & references created from the original &.

The & originated from &mut, and also the second &mut is &mut UnsafeCell. One of them might be why the author thought this is OK, but we know &mut UnsafeCell is not special in any way.

A more interesting question would be whether transmuting to &UnsafeCell instead (which is denied by the other lint) will be fine. The answer is that it can work, but it isn't needed: the crate never actually mutates values which weren't originally behind UnsafeCell, so just transmuting to & will work (but will require changes to public API).

Lint fired: unsafe_cell_reference_casting

Usage status: not published on crates.io, demo code.

This crates is created to demonstrate UB in Rust (and in fact the test that fails to compile fails Miri under Stacked Borrows, like all &->&UnsafeCell casts), so the fact the lints fire here means we are catching more UB at compile time, as intended. No real use-case here.

Lint fired: unsafe_cell_reference_casting

Usage status: as far as I can tell not published on crates.io. Last commit 4 years ago.

Status: the crate has macro that can transmute between arbitrary references, and it's exported, which seems like a major soundness hole. However, the macro is named as_atomic and it's usage is constrained to transmuting to atomics, so maybe the export was a mistake. The lint is fired on almost every invocation of the macro, except one that is cfg'd out.

What matters for us, however, is how it's used. Out of the seven times this macro is used, it is followed 4 times by a load(). But one time it is followed by a store, and another two times by compare_exchange_weak(). This is clearly UB.

The store and the loads are possible to fix since they are working on a &&mut [usize], so it is possible to read the &mut [usize] without going through the shared reference. But the compare_exchange_weak() is not fixable without changing the type. Furthermore, it comes from a &u8 function argument (where we pass LLVM noalias), so it may be possible to trigger an actual miscompilation here.

Lint fired: unsafe_cell_reference_casting

Usage status: 57,474 downloads, two dependents, last commit 3 years ago.

Running the crate's tests under Miri quickly revealed another violation of Stacked Borrows. Running it with Tree Borrows ignores other violations and focuses on this one: the UnsafeCell gotten from the shared reference is indeed being written into. We have UB here. The lint has saved us once again.

This crate also had much more UB; I fixed a bunch of those a sent a PR (jonhoo/arccstr#7) - now merged and released.

Lint fired: unsafe_cell_reference_casting

Usage status: Last commit 4 years ago. No dependents.

Status: lint fired once in a method (but for some reason this was reported twice?). A conversion from &*mut T to &AtomicPtr<T>. In the callers, the AtomicPtr is being loaded, stored, and compare-and-swapped. So, clearly UB.

It looks possible to make this AtomicPtr to begin with - an easy fix.

Lint fired: unsafe_cell_reference_casting

Usage status: this is a popular project. 2,939,140 downloads, 131 dependents.

Crater reports on an old version of it, but the problems seem to stay in the new version. I don't know why crater isn't reporting it.

The lint was firing in two places of conversions to atomics. In both it is correct. In one modification immediately follows, and the other is a public API returning a modifiable atomic (wrapper).

I have not tried to fiddle with that, but as it seems, in the case of the public function it is easily possible to make it sound by using addr_of!() instead of references as the object is FFI. I believe this is also possible in the second case, but I'm not sure.

Lint fired: unsafe_cell_reference_casting

Usage status: v_htmlescape is a popular crate - 2,425,606 downloads, 32 dependents. The rest are less, but their situation in the same.

Status: the lint was correct. This was UB (a store to a transmuted atomic). But this was already fixed on master (probably due to another lint, creating a shared reference to mutable static is discouraged), to use addr_of!(), which is not UB and does not lint. The fix is not published yet.

Lint fired: unsafe_cell_reference_casting

Usage status: 106,549 downloads, 10 dependents.

Status: the same as v_escape (same author). Was UB, but replaced with addr_of!(). The replacement is not yet released.

Lint fired: unsafe_cell_transmutes

Usage status: no dependents.

Status: definitely UB. One case exposes the transmuted atomic to the public. The other two also exposes it, but also store to it in the project's internal code. Two cases can just be made into atomics in the first place, it isn't clear to me why they weren't. The other seems to be non-salvageable (it is a general API for the conversion &u64->&AtomicU64Wrapper).

Lint fired: unsafe_cell_transmutes

Usage status: one dependent from the same project with no dependents.

Usage: UB. Stores into the transmuted atomic.

This code also relies on the internal layout of Arc, but nevermind.

This seems possible to save (but keep the dependency on the layout of Arc) by storing AtomicPtr instead of Arc, and converting it to Arc when needed instead of the other way around (or alternatively, store UnsafeCell<Arc>).

Usage status: no dependents. Last commit 3 years ago.

Status: could not compile locally even with nightly. Crater does not show the error. Skipped.

Usage status: not published to crates.io as far as I can tell.

Status: a &u64 transmuted into reference to #[repr(C)] struct CompCount { val: i32, comp_count: Cell<u32> }. The Cell is then mutated. So we have UB here. But it seems possible to just use a &CompCount in the first place (another piece of code in the same file does that).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment