orxfun / orxfun/orx-concurrent-queue

Unsound API in ConcurrentQueue::pull/pull_with_idx allows double-free via arithmetic overflow

Open
#14 0 comments 0 reactions 0 assignees View on GitHub

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:

  1. fetch_add(usize::MAX) wraps popped from 1 to 0
  2. end_idx = 1 + usize::MAX = 0 (wrapping)
  3. popped is now 0 — behind its original value
  4. On queue drop, elements 0..written are dropped again, including element 0 which was already consumed by pull(1) → double-free

Contributor guide

No contributing guide indexed for this repository

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

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.