Miri UB - Stacked Borrows with Threads

Hi,
I am building an AtomicCell primitive and encountered a Miri error about Stacked Borrows.

The error is generated for this test:

let fancy_cell = Arc::new(AtomicCell::new("Bonjour"));

(0..10).map( |_| { 
let cell = fancy_cell.clone();
thread::spawn(move || {
for _ in 0..10 {
cell.store("Bonjour")}})
})
.collect::<Vec<_>>()
.into_iter()
.for_each(|h|{h.join();});

It produces this error:

error: Undefined Behavior: not granting access to tag <107473> because that would remove [Unique for <107062>] which is weakly protected because it is an argument of call 49781

  • --> /redacted/lib/rustlib/src/rust/library/core/src/sync/atomic.rs:3273:33*
  •     |*
    

3273 | (SeqCst, SeqCst) => intrinsics::atomic_cxchg_seqcst_seqcst(dst, old, new),

  •     |                                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag <107473> because that would remove [Unique for <107062>] which is weakly protected because it is an argument of call 49781*
    

It also produces this information about the tags:

<107473> was created by a SharedReadWrite retag at offsets [0x10..0x11]

  • --> tests/con_test.rs:45:89*
  •   |*
    

45 | (0..10).map(||{ let cell = fancy_cell.clone(); thread::spawn(move||{for _ in 0..10{cell.store("Bonjour")}})}).collect::<Vec<>>().into_iter().for_each(|h|{h.join();});

  •   | ^^^^^^^^^^^^^^^^^^^^^*  
    

<107062> is this argument

  • --> tests/con_test.rs:45:89*
  •   |*
    

45 | (0..10).map(||{ let cell = fancy_cell.clone(); thread::spawn(move||{for _ in 0..10{cell.store("Bonjour")}})}).collect::<Vec<>>().into_iter().for_each(|h|{h.join();});

  •   | ^^^^^^^^^^^^^^^^^^^^^*
    

To me this reads like an aliasing issue? How could and would SharedReadWrite_Permissions be created/fix the error?

Strangely a single threaded test (calling the erroring function too) causes no errors:

let x = AtomicCell::new("Bonjour");
x.store("Bonjour");
x.store("Bonjour");

Works perfectly fine in Miris opinion.

Note:
Internally the pointers passed into the intrinsic function are loaded from AtomicPtrs which get their pointers from UnsafeCell::get();
The raw pointers pass function and thread boundaries.
About Stacked Borrows

Help is appreciated.

Thanks,
Elektron

The hints point at: instrinsic::atomic_cxchg_seqcst_seqcst(dst, old, new) and cell.store("Bonjour").
Formatting got me :slight_smile:

You'd have to share your code (the definition, not the usage!) for us to stand a chance of finding the error.

1 Like

Here is the source code:
https://github.com/IanTick/Molecules

Implementation is at src/primitives/AtomicCell.rs