orxfun / orxfun/orx-concurrent-queue
Unsound API in ConcurrentQueue::pull/pull_with_idx allows double-free via arithmetic overflow
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 1
- Forks
- 1
- PR merge metrics
- No merged PRs in 30d
Description
Summary
ConcurrentQueue::pull and ConcurrentQueue::pull_with_idx use fetch_add(chunk_size) on the internal popped counter followed by an unchecked begin_idx + chunk_size. When chunk_size is large enough to cause wrapping overflow, the popped counter wraps backward, breaking the invariant that it only advances. On queue drop, already-consumed elements are dropped again, resulting in a double-free.
Confirmed on orx-concurrent-queue 2.0.0.
PoC
use orx_concurrent_queue::*;
fn main() {
let queue: ConcurrentQueue<String> = ConcurrentQueue::new();
for i in 0..20usize {
queue.push(i.to_string());
}
let iter_opt = queue.pull(1);
let iter_with_idx_opt = queue.pull_with_idx(usize::MAX);
if let Some(iter) = iter_opt {
drop(iter);
}
if let Some((_idx, iter)) = iter_with_idx_opt {
drop(iter);
}
}
ASAN output
==569332==ERROR: AddressSanitizer: attempting double-free on 0x7a21b1fe0010 in thread T0:
SUMMARY: AddressSanitizer: double-free in free
Affected code
src/queue.rs — both pull (line 451) and pull_with_idx (line 557):
let begin_idx = self.popped.fetch_add(chunk_size, Ordering::Relaxed);
let end_idx = begin_idx + chunk_size; // wrapping overflow
When chunk_size = usize::MAX and begin_idx = 1:
fetch_add(usize::MAX)wrapspoppedfrom 1 to 0end_idx = 1 + usize::MAX = 0(wrapping)poppedis now 0 — behind its original value- On queue drop, elements
0..writtenare dropped again, including element 0 which was already consumed bypull(1)→ double-free
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/queue.rs at pull around line 451 and pull_with_idx around line 557, then reproduce the issue with the provided usize::MAX PoC under AddressSanitizer. Trace the popped counter and index range across both methods. Done means oversized chunks cannot regress queue state or cause consumed elements to be dropped twice, with regression coverage for both APIs.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100