rust-lang / rust-lang/rust

`Arc::strong_count` memory ordering is a potential footgun

Open
#117,485 21 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-atomic C-discussion T-libs
Dominant language
Rust
Stars
119k
Forks
16.2k
PR merge metrics
PR metrics pending

Description

In https://github.com/rust-lang/rust/pull/115546 the memory ordering of {Arc,Weak}::{strong,weak}_count got changed to Relaxed, which can cause some hard to debug race bugs.

I tried this code:

use std::cell::UnsafeCell;
use std::sync::Arc;
use std::thread;

struct SendMe<T>(T);
unsafe impl<T> Send for SendMe<T> {}

fn main() {
    let a = Arc::new(UnsafeCell::new(0i32));
    let b = SendMe(a.clone());
    thread::spawn(move || {
        let b = b;
        for _ in 0..100 {
            unsafe { *b.0.get() += 1; }
        }
    });
    while Arc::strong_count(&a) != 1 {}
    // core::sync::atomic::fence(core::sync::atomic::Ordering::Acquire);
    unsafe { *a.get() += 1; }
}

I expected to see this happen: I expected the check strong_count == 0 to be strong enough to avoid a data race in this program. My intuition was that this check implies that no other thread can access the data concurrently.

Instead, this happened: With the fence commented out, the above program has undefined behavior. Miri outputs:

error: Undefined Behavior: Data race detected between (1) Write on thread `<unnamed>` and (2) Read on thread `main` at alloc826+0x10. (2) just happened here
  --> src/main.rs:19:14
   |
19 |     unsafe { *a.get() += 1; }
   |              ^^^^^^^^^^^^^ Data race detected between (1) Write on thread `<unnamed>` and (2) Read on thread `main` at alloc826+0x10. (2) just happened here
   |
help: and (1) occurred earlier here
  --> src/main.rs:14:22
   |
14 |             unsafe { *b.0.get() += 1; }
   |                      ^^^^^^^^^^^^^^^
   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
   = note: BACKTRACE (of the first span):
   = note: inside `main` at src/main.rs:19:14: 19:27

I think we should either put the Acquire ordering back or add a warning to the docs that such code requires a manual fence.

Meta

1.74 = current beta

cc @SUPERCILEX author of https://github.com/rust-lang/rust/pull/115546

@rustbot label T-libs A-atomic

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Review Rust PR #115546 and reproduce the supplied Arc::strong_count example with Miri, including the commented Acquire fence. Determine whether the API's memory ordering or its documentation should change, and validate the chosen resolution against the reported data-race behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
operating-systems
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.