Rust advisories / cell-project
Unsound MediumRUSTSEC-2020-0164 · cell-project
RUSTSEC-2020-0164: `cell-project` used incorrect variance when projecting through `&Cell<T>`
Short answercell-project has an API that can cause undefined behaviour from safe code: `cell-project` used incorrect variance when projecting through `&Cell<T>`. Fixed in 0.1.4.
| Crate | cell-project |
|---|---|
| Type | Unsound (memory-corruption) |
| Affected versions | Before 0.1.4. |
| Fixed in | 0.1.4 |
| Severity | Medium (GitHub rating) |
| Published | 2020-08-27 |
| Last changed | 2026-07-17 |
| Also known as | GHSA-p75v-367r-2v23 |
| Affected functions | cell_project::cell_project, cell_project::nightly_cell_project |
What to do
- See which version you have:
cargo tree -i cell-project(it also shows which of your dependencies pulls it in). - Update within your version requirement:
cargo update -p cell-project. If the fix is a newer major or minor than your requirement allows, raise it in Cargo.toml (for examplecell-project = "0.1.4") or update the dependency that pulls it in. - Check again with
cargo audit, or paste your Cargo.lock into our Cargo.toml checker.
Details from the advisory
Overview
The issue lies in the implementation of the cell_project macro which used field as *const _ instead of field as *mut _. The problem being that *const T is covariant in T while *mut T is invariant in T. Keep in mind that &Cell<T> is invariant in T, so casting to *const T relaxed the variance, and lead to unsoundness, as shown in the example below.
use std::cell::Cell;
use cell_project::cell_project as cp;
struct Foo<'a> {
x: Option<&'a Cell<Foo<'a>>>,
}
impl<'a> Drop for Foo<'a> {
fn drop(&mut self) {
// `ourselves` is an &Cell<Self>.
// NB: `Drop` is unsound.
if let Some(ourselves) = self.x.as_ref() {
// replace `self` (but this doesn't actually replace `self`)
let is_x_none = ourselves.replace(Foo {
x: None,
}).x.as_ref().is_none();
// if we just moved out of `self`, and we had a `Some` originally,
// how come this is a `None`?
if is_x_none {
println!("how did we get a None?");
}
}
}
}
fn main() {
let foo = Cell::new(Foo {
x: None,
});
let x = cp!(Foo<'_>, foo.x);
x.set(Some(&foo));
}
MIRI error
$ cargo +nightly miri run
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `<snip>`
error: Undefined Behavior: not granting access to tag <untagged> because incompatible item is protected: [Unique for <2472> (call 796)]
--> $RUST_STD_PATH/src/rust/library/core/src/cell.rs:404:31
|
404 | mem::replace(unsafe { &mut *self.value.get() }, val)
| ^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag <untagged> because incompatible item is protected: [Unique for <2472> (call 796)]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
= note: inside `std::cell::Cell::<Foo>::replace` at $RUST_STD_PATH/src/rust/library/core/src/cell.rs:404:31
note: inside `<Foo as std::ops::Drop>::drop` at src/main.rs:14:29
--> src/main.rs:14:29
|
14 | let is_x_none = ourselves.replace(Foo {
| _____________________________^
15 | | x: None,
16 | | }).x.as_ref().is_none();
| |______________^
= note: inside `std::ptr::drop_in_place::<Foo> - shim(Some(Foo))` at $RUST_STD_PATH/src/rust/library/core/src/ptr/mod.rs:486:1
= note: inside `std::ptr::drop_in_place::<std::cell::UnsafeCell<Foo>> - shim(Some(std::cell::UnsafeCell<Foo>))` at $RUST_STD_PATH/src/rust/library/core/src/ptr/mod.rs:486:1
= note: inside `std::ptr::drop_in_place::<std::cell::Cell<Foo>> - shim(Some(std::cell::Cell<Foo>))` at $RUST_STD_PATH/src/rust/library/core/src/ptr/mod.rs:486:1
note: inside `main` at src/main.rs:32:1
--> src/main.rs:32:1
|
32 | }
| ^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to previous error
Affected Versions
All versions of the cell-project crate before 0.1.4 are affected.
Mitigation
This was fixed in [Issues/4], and released as version 0.1.4. So just updating to the latest version will include the fix (which may result in a compile error on unsound usage).
Acknowledgements
This was discovered and fixed by @SoniEx2 in cell-project: [Issues/3] and [Issues/4]
References
- Package: crates.io/crates/cell-project
- Advisory: rustsec.org/advisories/RUSTSEC-2020-0164.html
- Report: github.com/RustyYato/cell-project/issues/3
- Report: github.com/RustyYato/cell-project/issues/3
- Report: github.com/RustyYato/cell-project/issues/4
From the RustSec Advisory Database (CC0-1.0) via the OSV export, downloaded Oct 24, 2026. Severity score computed from the published CVSS vector.
Up next
Are you affected?Check your whole dependency treePaste Cargo.lock: every advisory, for your exact versions.Read it next →Also worth your time
Did this answer your question?