Skip to content

Fix data race in PinnedPagableArc type - #232

Closed
Ollie-Pearce wants to merge 1 commit into
facebook:mainfrom
Ollie-Pearce:fix-data-race
Closed

Fix data race in PinnedPagableArc type#232
Ollie-Pearce wants to merge 1 commit into
facebook:mainfrom
Ollie-Pearce:fix-data-race

Conversation

@Ollie-Pearce

Copy link
Copy Markdown
Contributor

When PinnedPagableArc is dropped the pin counter is decremented with a Relaxed ordering. This can result in a data race when two threads drop an owned instance of a type duplicated via Clone.

Reproduction:

Test Case:

#[test]
fn racy_test() {
    let backend = InMemoryPagableStorage::new();
    let foo = PinnedPagableArc::new(false, PagableStorageHandle::new(backend.handle()));
    let bar = foo.clone();
    std::thread::scope(|s| {
        s.spawn(move || drop(bar));
        let _read = *foo;
        drop(foo);
    });
}

Running with Miri:

MIRIFLAGS="-Zmiri-many-seeds=0..16" cargo +nightly miri test -p pagable --test repro

Miri output:

error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `racy_test`
and (2) retag write of type `pagable::pagable_arc::PagableArcInnerData<bool>` on thread `unnamed-3`
at alloc768762+0x20
   --> pagable/src/pagable_arc.rs:877:44
    |
877 |                 let data: &mut _ = unsafe { &mut *ptr.data.get() };
    |                                             ^^^^^^^^^^^^^^^^^^^^ (2) just happened here
help: and (1) occurred earlier here
   --> pagable/src/pagable_arc.rs:716:14

Fix:

Synchronise the PinnedPagableArc pin counter with Release/Acquire memory orderings and use a fence before data is unpinned.

This mirrors the synchronisation used by std::sync::Arc.
If you'd like to see another example I found a similar bug in the stabby crate

@meta-cla

meta-cla Bot commented Aug 23, 2026

Copy link
Copy Markdown

Hi @Ollie-Pearce!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 23, 2026
@meta-cla

meta-cla Bot commented Aug 23, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@meta-codesync

meta-codesync Bot commented Aug 24, 2026

Copy link
Copy Markdown

@ndmitchell has imported this pull request. If you are a Meta employee, you can view this in D117200708. (Because this pull request was imported automatically, there will not be any future comments.)

@8Keep 8Keep left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review automatically exported from Phabricator review in Meta.

@meta-codesync meta-codesync Bot closed this in ab8400a Aug 25, 2026
@meta-codesync meta-codesync Bot added the Merged label Aug 25, 2026
@meta-codesync

meta-codesync Bot commented Aug 25, 2026

Copy link
Copy Markdown

@ndmitchell merged this pull request in ab8400a.

meta-codesync Bot pushed a commit to facebook/buck2 that referenced this pull request Aug 25, 2026
Summary:
When `PinnedPagableArc` is dropped the pin counter is decremented with a `Relaxed` ordering. This can result in a data race when two threads drop an owned instance of a type duplicated via `Clone`.

## Reproduction:

### Test Case:
```rust
#[test]
fn racy_test() {
    let backend = InMemoryPagableStorage::new();
    let foo = PinnedPagableArc::new(false, PagableStorageHandle::new(backend.handle()));
    let bar = foo.clone();
    std::thread::scope(|s| {
        s.spawn(move || drop(bar));
        let _read = *foo;
        drop(foo);
    });
}
```

### Running with Miri:
`MIRIFLAGS="-Zmiri-many-seeds=0..16" cargo +nightly miri test -p pagable --test repro`

### Miri output:
```
error: Undefined Behavior: Data race detected between (1) non-atomic read on thread `racy_test`
and (2) retag write of type `pagable::pagable_arc::PagableArcInnerData<bool>` on thread `unnamed-3`
at alloc768762+0x20
   --> pagable/src/pagable_arc.rs:877:44
    |
877 |                 let data: &mut _ = unsafe { &mut *ptr.data.get() };
    |                                             ^^^^^^^^^^^^^^^^^^^^ (2) just happened here
help: and (1) occurred earlier here
   --> pagable/src/pagable_arc.rs:716:14
```

## Fix:

Synchronise the `PinnedPagableArc` pin counter with `Release`/`Acquire` memory orderings and use a fence before data is unpinned.

This mirrors the synchronisation used by [`std::sync::Arc`](https://doc.rust-lang.org/src/alloc/sync.rs.html#2827).
If you'd like to see another example [I found a similar bug in the `stabby` crate](ZettaScaleLabs/stabby#139)

X-link: facebook/starlark-rust#232

Reviewed By: 8Keep

Differential Revision: D117200708

Pulled By: ndmitchell

fbshipit-source-id: 0ca54c4d87d8a5314af6b58ef631da71907fe72b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants