Skip to content

AtomicCell is unsound on types containing niches #833

@dtolnay

Description

@dtolnay

I believe it's incorrect to implement AtomicCell<T> in terms of UnsafeCell<T>, and it needs to be UnsafeCell<MaybeUninit<T>> instead, to prevent code outside the cell from observing partially initialized state.

Here is an example of safe code that reproduces UB:

use crossbeam_utils::atomic::AtomicCell;
use std::num::NonZeroU128;
use std::thread;

enum Enum {
    NeverConstructed,
    Cell(AtomicCell<NonZeroU128>),
}

static STATIC: Enum = Enum::Cell(AtomicCell::new(match NonZeroU128::new(1) {
    Some(nonzero) => nonzero,
    None => unreachable!(),
}));

fn main() {
    thread::spawn(|| {
        let cell = match &STATIC {
            Enum::NeverConstructed => unreachable!(),
            Enum::Cell(cell) => cell,
        };
        let x = NonZeroU128::new(0xFFFFFFFF_FFFFFFFF_00000000_00000000).unwrap();
        let y = NonZeroU128::new(0x00000000_00000000_FFFFFFFF_FFFFFFFF).unwrap();
        loop {
            cell.store(x);
            cell.store(y);
        }
    });

    loop {
        if let Enum::NeverConstructed = STATIC {
            unreachable!(":(");
        }
    }
}
$ cargo run

warning: variant is never constructed: `NeverConstructed`
 --> src/main.rs:6:5
  |
6 |     NeverConstructed,
  |     ^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: `repro` (bin "repro") generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 0.27s
     Running `target/debug/repro`

thread 'main' panicked at 'internal error: entered unreachable code: :(', src/main.rs:31:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions