Add advisory for unbounded-spsc: Sender::send transmute UAF under tx/rx race#2884
Add advisory for unbounded-spsc: Sender::send transmute UAF under tx/rx race#2884berkant-koc wants to merge 2 commits into
Conversation
…rx race The DISCONNECTED arm of Sender::send transmutes *mut Producer<T> as a value-level Consumer<T>, yielding OOB read inside the Sender frame and a fake-Arc drop on a non-allocated address. Reachable via TOCTOU race between Receiver::drop and Sender::send.
|
Do you have approval from the maintainer to publish this? |
|
Same — closing per your point on #2883. The private channel here bounced ( |
Maintainer @spearman published v0.3.0 on 2026-05-16 with the fix plus regression test race_disconnect_does_not_corrupt_sender_or_abort (commit 2714da2), accepted GHSA-6m57-8r3p-pqx6, and closed upstream issue rustsec#4. All prior versions (0.2.0, 0.1.x) are yanked on crates.io. - patched = [">= 0.3.0"] - aliases = ["GHSA-6m57-8r3p-pqx6"] - url = spearman/unbounded-spsc#4
|
Reopening: maintainer @spearman published v0.3.0 on crates.io (2026-05-16) with the fix plus a regression test ( Advisory updated:
Ready for review. |
djc
left a comment
There was a problem hiding this comment.
I don't see the maintainer approving submission of a RustSec advisory anywhere.
| ## Trigger | ||
|
|
||
| ```rust | ||
| use std::thread; | ||
| use unbounded_spsc::channel; | ||
|
|
||
| for _ in 0..500 { | ||
| let (tx, rx) = channel::<Box<u64>>(); | ||
| let h = thread::spawn(move || { | ||
| for _ in 0..10_000 { let _ = tx.send(Box::new(0xDEAD_BEEF)); } | ||
| }); | ||
| drop(rx); | ||
| let _ = h.join(); | ||
| } | ||
| // Release: SIGSEGV within a few iterations. | ||
| // ASan: stack-buffer-overflow inside fake-Consumer::try_pop. | ||
| ``` | ||
|
|
||
| ## Smoking-gun upstream evidence | ||
|
|
||
| `src/lib.rs:975` in the project's own test suite carries: | ||
|
|
||
| ``` | ||
| // TODO: failures | ||
| // - failed with assertion on line 394 in send fn | ||
| // assert!(second.is_none()) | ||
| ``` | ||
|
|
||
| That assertion lives in the same transmute block. The author has observed the symptom — `try_pop()` returning `Some` where logically there should be `None` — as a flaky test rather than recognising it as UB. |
There was a problem hiding this comment.
Let's drop the "Trigger" and "Smoking-gun upstream evidence" sections.
|
|
||
| # `Sender::send` pointer-as-value transmute causes OOB read and fake-Arc drop under tx/rx race | ||
|
|
||
| Maintainer acknowledged on 2026-05-15; v0.3.0 ships the fix plus a regression test (`race_disconnect_does_not_corrupt_sender_or_abort`), and 0.2.0 + all 0.1.x versions have been yanked on crates.io. |
There was a problem hiding this comment.
Let's drop this metadata from the advisory contents.
This has not been published. |
|
You are right, my "accepted" wording was wrong. I conflated maintainer acknowledgement with publication.
Verified state via GitHub API just now:
- GHSA-6m57-8r3p-pqx6: `state=draft`
- `published_at`: null
- `cve_id`: null
So the advisory is acknowledged by @spearman in the draft but not yet published, which means the GHSA URL is 404 for non-collaborators and cannot serve as a usable `aliases` entry here.
I will ping @spearman on the GHSA thread to request publication so the alias mapping can resolve.
Two procedural paths:
1. Close this PR now, reopen once the GHSA flips to `published` with a real `published_at` and CVE id.
2. Hold open with a `blocked-on-upstream-publish` note and revisit when state changes.
Please advise on the preferred path.
|
|
Status update on the upstream advisory:
GHSA-6m57-8r3p-pqx6 was flipped to `state=published` at 15:05 UTC today by spearman. Severity is `medium`, the `html_url` now resolves anonymously, and `cve_id` is `None` pending GitHub-CNA assignment (no ETA from their side).
The PR is technically unblocked. Three procedural paths from here, deferring to your preference:
1. I update `advisory.toml` in-place on this branch with the resolved `aliases = ["GHSA-6m57-8r3p-pqx6"]` plus the `url` field, and push a follow-up commit.
2. Close this PR and I reopen a fresh one once GitHub-CNA assigns the CVE-ID.
3. Merge as-is with the GHSA-only alias, and I submit a small follow-up PR to add the CVE-ID once it lands.
Your call on which path; I am set up to execute any of the three.
|
Resubmission of the unbounded-spsc half of #2882, split per @djc's review and trimmed.
Sender::send(single-file crate,src/lib.rs:379-405in 0.2.0 / commit23a9ce7) transmutes*mut Producer<T>(a pointer, 8 bytes on 64-bit) into the bytes of a value-levelConsumer<T>. The resultingConsumer::buffer.ptris the address of a field on theSender, not the realArcInner<Buffer<T>>.consumer.try_pop()then readsBuffer<T>offsets that lie inside theSender<T>frame (OOB), andDropfor the fake Arc callsdeallocon a non-allocated address.Branch is only reachable single-threaded if the receiver-drop /
connected = falseguard is bypassed; the trigger is a TOCTOU race betweenSender::send'sconnected.load()andReceiver::drop'scompare_exchange(_, DISCONNECTED, ...). The author's own test suite carries a TODO atsrc/lib.rs:975referencing the same assert that fires in the buggy path — the symptom was observed but misclassified.The companion
oneringbufadvisory is filed separately as a sibling PR.Closes prior #2882 (superseded by this + the oneringbuf PR).