Common Weakness Enumeration

CWE-415

Allowed

Double Free

Abstraction: Variant · Status: Draft

The product calls free() twice on the same memory address.

1013 vulnerabilities reference this CWE, most recent first.

GHSA-6G84-HFM2-X43X

Vulnerability from github – Published: 2026-05-06 12:30 – Updated: 2026-05-08 15:31
VLAI
Details

In the Linux kernel, the following vulnerability has been resolved:

RDMA/umem: Fix double dma_buf_unpin in failure path

In ib_umem_dmabuf_get_pinned_with_dma_device(), the call to ib_umem_dmabuf_map_pages() can fail. If this occurs, the dmabuf is immediately unpinned but the umem_dmabuf->pinned flag is still set. Then, when ib_umem_release() is called, it calls ib_umem_dmabuf_revoke() which will call dma_buf_unpin() again.

Fix this by removing the immediate unpin upon failure and just let the ib_umem_release/revoke path handle it. This also ensures the proper unmap-unpin unwind ordering if the dmabuf_map_pages call happened to fail due to dma_resv_wait_timeout (and therefore has a non-NULL umem_dmabuf->sgt).

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-43128"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-415"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-05-06T12:16:29Z",
    "severity": "HIGH"
  },
  "details": "In the Linux kernel, the following vulnerability has been resolved:\n\nRDMA/umem: Fix double dma_buf_unpin in failure path\n\nIn ib_umem_dmabuf_get_pinned_with_dma_device(), the call to\nib_umem_dmabuf_map_pages() can fail. If this occurs, the dmabuf\nis immediately unpinned but the umem_dmabuf-\u003epinned flag is still\nset. Then, when ib_umem_release() is called, it calls\nib_umem_dmabuf_revoke() which will call dma_buf_unpin() again.\n\nFix this by removing the immediate unpin upon failure and just let\nthe ib_umem_release/revoke path handle it. This also ensures the\nproper unmap-unpin unwind ordering if the dmabuf_map_pages call\nhappened to fail due to dma_resv_wait_timeout (and therefore has\na non-NULL umem_dmabuf-\u003esgt).",
  "id": "GHSA-6g84-hfm2-x43x",
  "modified": "2026-05-08T15:31:16Z",
  "published": "2026-05-06T12:30:29Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-43128"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/104016eb671e19709721c1b0048dd912dc2e96be"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/40126bcbefa79ea86672e05dae608596bab38319"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/70542b69abff34d24b11ae0bb200cc7a766d18df"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/b324327ff6f48d8065dca67eb3b91357e72726bd"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/ba3bf0f1bf1d5d0404678485e872980532fcc2c4"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/d3e32e2f3262f1b25d77c085ace38e2cc4ad75cf"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-6GR6-QRXR-M9RV

Vulnerability from github – Published: 2022-05-14 00:52 – Updated: 2022-05-14 00:52
VLAI
Details

Adobe Acrobat and Reader versions 2018.011.20063 and earlier, 2017.011.30102 and earlier, and 2015.006.30452 and earlier have a double free vulnerability. Successful exploitation could lead to arbitrary code execution.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2018-12841"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-415"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2018-10-12T18:29:00Z",
    "severity": "HIGH"
  },
  "details": "Adobe Acrobat and Reader versions 2018.011.20063 and earlier, 2017.011.30102 and earlier, and 2015.006.30452 and earlier have a double free vulnerability. Successful exploitation could lead to arbitrary code execution.",
  "id": "GHSA-6gr6-qrxr-m9rv",
  "modified": "2022-05-14T00:52:36Z",
  "published": "2022-05-14T00:52:36Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2018-12841"
    },
    {
      "type": "WEB",
      "url": "https://helpx.adobe.com/security/products/acrobat/apsb18-30.html"
    },
    {
      "type": "WEB",
      "url": "http://www.securityfocus.com/bid/105440"
    },
    {
      "type": "WEB",
      "url": "http://www.securitytracker.com/id/1041809"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-6HVH-5RRP-396C

Vulnerability from github – Published: 2026-04-14 18:30 – Updated: 2026-04-14 18:30
VLAI
Details

Double free in Windows Shell allows an authorized attacker to elevate privileges locally.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-26166"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-415"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-04-14T18:16:50Z",
    "severity": "HIGH"
  },
  "details": "Double free in Windows Shell allows an authorized attacker to elevate privileges locally.",
  "id": "GHSA-6hvh-5rrp-396c",
  "modified": "2026-04-14T18:30:37Z",
  "published": "2026-04-14T18:30:37Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-26166"
    },
    {
      "type": "WEB",
      "url": "https://msrc.microsoft.com/update-guide/vulnerability/CVE-2026-26166"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-6J8R-9FPW-HMXP

Vulnerability from github – Published: 2024-07-30 09:32 – Updated: 2025-11-04 00:31
VLAI
Details

In the Linux kernel, the following vulnerability has been resolved:

mlxsw: core_linecards: Fix double memory deallocation in case of invalid INI file

In case of invalid INI file mlxsw_linecard_types_init() deallocates memory but doesn't reset pointer to NULL and returns 0. In case of any error occurred after mlxsw_linecard_types_init() call, mlxsw_linecards_init() calls mlxsw_linecard_types_fini() which performs memory deallocation again.

Add pointer reset to NULL.

Found by Linux Verification Center (linuxtesting.org) with SVACE.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-42138"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-415"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-07-30T08:15:05Z",
    "severity": "HIGH"
  },
  "details": "In the Linux kernel, the following vulnerability has been resolved:\n\nmlxsw: core_linecards: Fix double memory deallocation in case of invalid INI file\n\nIn case of invalid INI file mlxsw_linecard_types_init() deallocates memory\nbut doesn\u0027t reset pointer to NULL and returns 0. In case of any error\noccurred after mlxsw_linecard_types_init() call, mlxsw_linecards_init()\ncalls mlxsw_linecard_types_fini() which performs memory deallocation again.\n\nAdd pointer reset to NULL.\n\nFound by Linux Verification Center (linuxtesting.org) with SVACE.",
  "id": "GHSA-6j8r-9fpw-hmxp",
  "modified": "2025-11-04T00:31:07Z",
  "published": "2024-07-30T09:32:02Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-42138"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/8ce34dccbe8fa7d2ef86f2d8e7db2a9b67cabfc3"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/9af7437669b72f804fc4269f487528dbbed142a2"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/ab557f5cd993a3201b09593633d04b891263d5c0"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/f8b55a465b0e8a500179808166fe9420f5c091a1"
    },
    {
      "type": "WEB",
      "url": "https://lists.debian.org/debian-lts-announce/2025/01/msg00001.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-6M57-8R3P-PQX6

Vulnerability from github – Published: 2026-05-29 19:05 – Updated: 2026-06-12 19:31
VLAI
Summary
unbounded-spsc: Sender::send pointer-as-value transmute causes OOB read and fake-Arc drop under TX/RX race
Details

Summary

Sender::send in src/lib.rs contains an unsafe block in the DISCONNECTED arm that transmutes a raw pointer (*mut Producer<T>) into the bytes of a value-level Consumer<T>. The author's intent, visible in the surrounding comment at lines 386-390, was a value transmute. The shipped code is one level of indirection off.

The resulting Consumer<T> has its internal Arc::ptr set to the address of the producer field on the Sender, not the real ArcInner<Buffer<T>>. Every subsequent consumer.try_pop() walks Buffer<T> fields at offsets that lie inside the Sender<T> struct (over send_new, inner) and adjacent memory, an out-of-bounds read. When the fake Consumer<T> is dropped at the end of the unsafe block, its Drop calls Arc::drop_in_place on a non-ArcInner address: it decrements bytes that the type system treats as strong_count: AtomicUsize but that are actually the real Arc::ptr value of the Sender, and at zero count it calls dealloc(Layout::for_value(...)) on an address the allocator never returned.

Reachable from 100% safe Rust through the canonical channel pattern: a tx.send(msg) that races with rx.drop(). This is consistent with the SIGSEGV that issue #3 reports in your own test suite.

Affected code (0.2.0, master at 23a9ce7)

```rust // src/lib.rs:384-401 DISCONNECTED => { self.inner.counter.store (DISCONNECTED, Ordering::SeqCst); // We want to guarantee if a message was not received that we get it // back; since spsc::{Producer,Consumer} have the same // internal representation (as a singleton struct containing Arc // >), we can safely transmute the producer in order to // pop the message back if it was orphaned. unsafe { let consumer : spsc::Consumer = std::mem::transmute (self.producer.get()); // <-- POINTER, not value let first = consumer.try_pop(); let second = consumer.try_pop(); assert!(second.is_none()); // <-- line 396; smoking-gun assert if let Some(t) = first { return Err (SendError (t)) } } }, self.producer is UnsafeCell> (line 29). UnsafeCell::::get(&self) returns mut X, a raw pointer, 8 bytes on 64-bit. The signature of transmute is transmute::(src: Src) -> Dst, so the call expands to transmute::<mut spsc::Producer, spsc::Consumer>(self.producer.get()). 8 bytes of pointer are reinterpreted as the bytes of a Consumer.

In bounded-spsc-queue-0.4.0, both Producer and Consumer are newtypes around Arc>, one pointer wide. The destination value therefore has Arc::ptr == &mut Producer as *const ArcInner>. To be a valid Arc>, that pointer must point to ArcInner { strong: AtomicUsize, weak: AtomicUsize, data: Buffer }, but it actually points to the start of Sender (the producer field). The first 8 bytes there hold the real Arc::ptr. The fake Arc reads those bytes as strong_count. The fake try_pop() then reads Buffer head/tail/data slots starting at offset 16 inside the Sender, that is, inside the send_new and inner fields.

The author's intent (per the comment at lines 386-390) was a value-level transmute:

let producer_val: spsc::Producer = std::ptr::read(self.producer.get()); let consumer : spsc::Consumer = std::mem::transmute(producer_val); which is layout-sound iff Producer and Consumer have identical layouts (they do, both are single-Arc newtypes). The shipped code is one indirection off.

Reachability The branch is not reachable single-threaded. Receiver::drop (line 332) stores connected = false before setting counter = DISCONNECTED; Sender::send (line 359) early-returns on connected == false. The trigger is a TOCTOU race:

Sender's self.inner.connected.load(SeqCst) reads true. Receiver-drop runs: stores connected = false and counter.compare_exchange(_, DISCONNECTED, SeqCst, SeqCst). Sender's self.inner.counter.fetch_add(1, SeqCst) (line 379) sees DISCONNECTED and enters the unsafe block. Under heavy contention this reproduces ~3/10 trials in release mode.

Proof of concept (race shape) // Cargo.toml: unbounded-spsc = "0.2" use std::thread; use unbounded_spsc::channel;

fn main() { for trial in 0..500 { let (tx, rx) = channel::>(); let started = std::sync::Arc::new( std::sync::atomic::AtomicBool::new(false)); let s = started.clone(); let h = thread::spawn(move || { s.store(true, std::sync::atomic::Ordering::SeqCst); for _ in 0..10_000 { let _ = tx.send(Box::new(0xDEAD_BEEF)); } }); while !started.load(std::sync::atomic::Ordering::SeqCst) { std::hint::spin_loop(); } drop(rx); let _ = h.join(); eprintln!("trial {trial} ok"); } } Observed:

Release-mode (no sanitizer): Segmentation fault (core dumped) reliably within a few trials. The non-segfaulting trials are masked by the separate send_new.send(new_consumer).unwrap() panic, see Secondary defect below. -Zsanitizer=address -Zbuild-std (nightly): ASan reports stack-buffer-overflow / stack-use-after-scope from the fake-Consumer's try_pop walking off the Sender frame. This matches the SIGSEGV reported in your own issue #3.

Smoking-gun upstream evidence src/lib.rs:975 in the project's test suite carries a TODO:

// TODO: failures // - failed with assertion on line 394 in send fn // assert!(second.is_none()) That is the assertion site of the transmute block (line 396 in 0.2.0 / master). You have observed try_pop() returning a non-None value where logically there should be none, which is exactly what reading random bytes from the Sender's send_new / inner fields produces, and the symptom has been marked as a flaky test rather than recognised as UB.

Impact Reachable from 100% safe Rust. Concrete UB primitives:

OOB read of bytes adjacent to the Sender struct via fake Consumer::try_pop(). The popped T is returned through Err(SendError(t)) to safe-code, an allocator-layout-controlled leak of process memory. OOB write via fake Arc::drop AtomicUsize::fetch_sub on bytes that are actually the real Arc::ptr value of the Sender. Allocator corruption via fake Arc::drop calling dealloc(Layout::for_value(...)) on a non-allocated address. The Sender struct holds the real Arc immediately after the producer field; the deallocator call therefore uses a layout the allocator never allocated, which on glibc is a confirmed double-free / arbitrary-bucket-poisoning primitive, and on hardened allocators (jemalloc-secure, mimalloc-secure) is an immediate abort. Secondary defect (same call path, bonus) Sender::send line 369:

self.send_new.send(new_consumer).unwrap(); When the Sender's message queue is full, a fresh bounded_spsc_queue::Channel is allocated and the new Consumer is shipped over an std::sync::mpsc side-channel to the Receiver. If the Receiver has already been dropped, receive_new is gone and this unwrap() panics. The panic surfaces in your own test suite, issue #2 (tests::port_gone_concurrent panicked at src/lib.rs:369) and the in-source TODO at lines 365-368 already note the question "Are we sure that this is safe to unwrap or should we handle the result explicitly ?".

The fix is to return Err(SendError(t)) instead of unwrapping, same shape as the channel-closed result the function already returns on the connected-false path. This is not a memory-safety defect, only a panic, but it lives on the same TX/RX-race code path and a single coordinated patch can address both. Filing it here so we cover the full call site in one cycle.

Suggested patch (primary defect) Replace the pointer-as-value transmute with a value-level read and a ManuallyDrop to suppress the alias's Producer::drop on subsequent exit:

unsafe { use core::mem::ManuallyDrop;

// Sound value-level transmute: Producer<T> and Consumer<T> are both
// newtypes around Arc<Buffer<T>>, so the value layouts match.
// ptr::read takes ownership of the Producer's bytes without running
// Producer's Drop.
let producer_val: spsc::Producer<T> = std::ptr::read(self.producer.get());
let consumer    : spsc::Consumer<T> = std::mem::transmute(producer_val);

let first  = consumer.try_pop();
let second = consumer.try_pop();
assert!(second.is_none());
if let Some(t) = first {
    return Err(SendError(t));
}

// consumer drops here; the same memory backs `producer`, so suppress
// the double Producer drop:
let _ = ManuallyDrop::new(consumer);

} Cleaner: restructure Sender to hold producer and consumer in a private enum Endpoint so no transmute is required, or use the bounded_spsc_queue::Producer::reclaim() escape hatch if available.

Suggested patch (secondary defect) if let Err(std::sync::mpsc::SendError(_)) = self.send_new.send(new_consumer) { // Receiver has been dropped: take the message back as the public // SendError, the same way the connected==false early-return does. return Err(SendError(t)); } Regression test (release-mode, race shape)

[test]

fn race_disconnect_does_not_corrupt_sender_or_abort() { for _ in 0..200 { let (tx, rx) = unbounded_spsc::channel::>(); let h = std::thread::spawn(move || { for _ in 0..10_000 { let _ = tx.send(Box::new(0xDEAD_BEEF)); } }); drop(rx); h.join().unwrap(); } } Reverse dependencies Two crates on crates.io depend on unbounded-spsc, both owned by you: apis (process-calculus framework) and gooey-rs (tile-UI library, unbounded-spsc gated behind opengl/fmod features). The OpenGL/FMOD callback-mailbox use is a natural rx-drop-during-tx-send scenario at scene-graph teardown. A single coordinated bump cycle is feasible.

Researcher Berkant Koc me@berkoc.com PGP: 0C588DFD76204987284213EA0AC529C41F8AA5D6

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "crates.io",
        "name": "unbounded-spsc"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "last_affected": "0.2.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-46690"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-125",
      "CWE-415",
      "CWE-704",
      "CWE-787"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-29T19:05:21Z",
    "nvd_published_at": "2026-06-12T16:16:29Z",
    "severity": "MODERATE"
  },
  "details": "## Summary\n\n`Sender::send` in `src/lib.rs` contains an `unsafe` block in the `DISCONNECTED` arm that transmutes a **raw pointer** (`*mut Producer\u003cT\u003e`) into the bytes of a **value-level** `Consumer\u003cT\u003e`. The author\u0027s intent, visible in the surrounding comment at lines 386-390, was a value transmute. The shipped code is one level of indirection off.\n\nThe resulting `Consumer\u003cT\u003e` has its internal `Arc::ptr` set to the address of the `producer` field on the `Sender`, not the real `ArcInner\u003cBuffer\u003cT\u003e\u003e`. Every subsequent `consumer.try_pop()` walks `Buffer\u003cT\u003e` fields at offsets that lie inside the `Sender\u003cT\u003e` struct (over `send_new`, `inner`) and adjacent memory, an out-of-bounds read. When the fake `Consumer\u003cT\u003e` is dropped at the end of the `unsafe` block, its `Drop` calls `Arc::drop_in_place` on a non-`ArcInner` address: it decrements bytes that the type system treats as `strong_count: AtomicUsize` but that are actually the real `Arc::ptr` value of the `Sender`, and at zero count it calls `dealloc(Layout::for_value(...))` on an address the allocator never returned.\n\nReachable from 100% safe Rust through the canonical channel pattern: a `tx.send(msg)` that races with `rx.drop()`. This is consistent with the SIGSEGV that issue #3 reports in your own test suite.\n\n## Affected code (0.2.0, master at `23a9ce7`)\n\n```rust\n// src/lib.rs:384-401\nDISCONNECTED =\u003e {\n    self.inner.counter.store (DISCONNECTED, Ordering::SeqCst);\n    // We want to guarantee if a message was not received that we get it\n    // back; since spsc::{Producer,Consumer} have the same\n    // internal representation (as a singleton struct containing Arc\n    // \u003cBuffer \u003cT\u003e\u003e), we can safely transmute the producer in order to\n    // pop the message back if it was orphaned.\n    unsafe {\n      let consumer : spsc::Consumer \u003cT\u003e\n        = std::mem::transmute (self.producer.get());     // \u003c-- POINTER, not value\n      let first    = consumer.try_pop();\n      let second   = consumer.try_pop();\n      assert!(second.is_none());                          // \u003c-- line 396; smoking-gun assert\n      if let Some(t) = first {\n        return Err (SendError (t))\n      }\n    }\n},\nself.producer is UnsafeCell\u003cspsc::Producer\u003cT\u003e\u003e (line 29). UnsafeCell::\u003cX\u003e::get(\u0026self) returns *mut X, a raw pointer, 8 bytes on 64-bit. The signature of transmute is transmute::\u003cSrc, Dst\u003e(src: Src) -\u003e Dst, so the call expands to transmute::\u003c*mut spsc::Producer\u003cT\u003e, spsc::Consumer\u003cT\u003e\u003e(self.producer.get()). 8 bytes of pointer are reinterpreted as the bytes of a Consumer\u003cT\u003e.\n\nIn bounded-spsc-queue-0.4.0, both Producer\u003cT\u003e and Consumer\u003cT\u003e are newtypes around Arc\u003cBuffer\u003cT\u003e\u003e, one pointer wide. The destination value therefore has Arc::ptr == \u0026mut Producer\u003cT\u003e as *const ArcInner\u003cBuffer\u003cT\u003e\u003e. To be a valid Arc\u003cBuffer\u003cT\u003e\u003e, that pointer must point to ArcInner { strong: AtomicUsize, weak: AtomicUsize, data: Buffer\u003cT\u003e }, but it actually points to the start of Sender\u003cT\u003e (the producer field). The first 8 bytes there hold the real Arc::ptr. The fake Arc reads those bytes as strong_count. The fake try_pop() then reads Buffer\u003cT\u003e head/tail/data slots starting at offset 16 inside the Sender\u003cT\u003e, that is, inside the send_new and inner fields.\n\nThe author\u0027s intent (per the comment at lines 386-390) was a value-level transmute:\n\nlet producer_val: spsc::Producer\u003cT\u003e = std::ptr::read(self.producer.get());\nlet consumer    : spsc::Consumer\u003cT\u003e = std::mem::transmute(producer_val);\nwhich is layout-sound iff Producer\u003cT\u003e and Consumer\u003cT\u003e have identical layouts (they do, both are single-Arc newtypes). The shipped code is one indirection off.\n\nReachability\nThe branch is not reachable single-threaded. Receiver::drop (line 332) stores connected = false before setting counter = DISCONNECTED; Sender::send (line 359) early-returns on connected == false. The trigger is a TOCTOU race:\n\nSender\u0027s self.inner.connected.load(SeqCst) reads true.\nReceiver-drop runs: stores connected = false and counter.compare_exchange(_, DISCONNECTED, SeqCst, SeqCst).\nSender\u0027s self.inner.counter.fetch_add(1, SeqCst) (line 379) sees DISCONNECTED and enters the unsafe block.\nUnder heavy contention this reproduces ~3/10 trials in release mode.\n\nProof of concept (race shape)\n// Cargo.toml: unbounded-spsc = \"0.2\"\nuse std::thread;\nuse unbounded_spsc::channel;\n\nfn main() {\n    for trial in 0..500 {\n        let (tx, rx) = channel::\u003cBox\u003cu64\u003e\u003e();\n        let started = std::sync::Arc::new(\n            std::sync::atomic::AtomicBool::new(false));\n        let s = started.clone();\n        let h = thread::spawn(move || {\n            s.store(true, std::sync::atomic::Ordering::SeqCst);\n            for _ in 0..10_000 {\n                let _ = tx.send(Box::new(0xDEAD_BEEF));\n            }\n        });\n        while !started.load(std::sync::atomic::Ordering::SeqCst) {\n            std::hint::spin_loop();\n        }\n        drop(rx);\n        let _ = h.join();\n        eprintln!(\"trial {trial} ok\");\n    }\n}\nObserved:\n\nRelease-mode (no sanitizer): Segmentation fault (core dumped) reliably within a few trials. The non-segfaulting trials are masked by the separate send_new.send(new_consumer).unwrap() panic, see Secondary defect below.\n-Zsanitizer=address -Zbuild-std (nightly): ASan reports stack-buffer-overflow / stack-use-after-scope from the fake-Consumer\u0027s try_pop walking off the Sender frame.\nThis matches the SIGSEGV reported in your own issue #3.\n\nSmoking-gun upstream evidence\nsrc/lib.rs:975 in the project\u0027s test suite carries a TODO:\n\n// TODO: failures\n// - failed with assertion on line 394 in send fn\n//   assert!(second.is_none())\nThat is the assertion site of the transmute block (line 396 in 0.2.0 / master). You have observed try_pop() returning a non-None value where logically there should be none, which is exactly what reading random bytes from the Sender\u0027s send_new / inner fields produces, and the symptom has been marked as a flaky test rather than recognised as UB.\n\nImpact\nReachable from 100% safe Rust. Concrete UB primitives:\n\nOOB read of bytes adjacent to the Sender\u003cT\u003e struct via fake Consumer\u003cT\u003e::try_pop(). The popped T is returned through Err(SendError(t)) to safe-code, an allocator-layout-controlled leak of process memory.\nOOB write via fake Arc::drop AtomicUsize::fetch_sub on bytes that are actually the real Arc::ptr value of the Sender.\nAllocator corruption via fake Arc::drop calling dealloc(Layout::for_value(...)) on a non-allocated address. The Sender struct holds the real Arc\u003cInner\u003e immediately after the producer field; the deallocator call therefore uses a layout the allocator never allocated, which on glibc is a confirmed double-free / arbitrary-bucket-poisoning primitive, and on hardened allocators (jemalloc-secure, mimalloc-secure) is an immediate abort.\nSecondary defect (same call path, bonus)\nSender::send line 369:\n\nself.send_new.send(new_consumer).unwrap();\nWhen the Sender\u0027s message queue is full, a fresh bounded_spsc_queue::Channel is allocated and the new Consumer\u003cT\u003e is shipped over an std::sync::mpsc side-channel to the Receiver. If the Receiver has already been dropped, receive_new is gone and this unwrap() panics. The panic surfaces in your own test suite, issue #2 (tests::port_gone_concurrent panicked at src/lib.rs:369) and the in-source TODO at lines 365-368 already note the question \"Are we sure that this is safe to unwrap or should we handle the result explicitly ?\".\n\nThe fix is to return Err(SendError(t)) instead of unwrapping, same shape as the channel-closed result the function already returns on the connected-false path. This is not a memory-safety defect, only a panic, but it lives on the same TX/RX-race code path and a single coordinated patch can address both. Filing it here so we cover the full call site in one cycle.\n\nSuggested patch (primary defect)\nReplace the pointer-as-value transmute with a value-level read and a ManuallyDrop to suppress the alias\u0027s Producer::drop on subsequent exit:\n\nunsafe {\n    use core::mem::ManuallyDrop;\n\n    // Sound value-level transmute: Producer\u003cT\u003e and Consumer\u003cT\u003e are both\n    // newtypes around Arc\u003cBuffer\u003cT\u003e\u003e, so the value layouts match.\n    // ptr::read takes ownership of the Producer\u0027s bytes without running\n    // Producer\u0027s Drop.\n    let producer_val: spsc::Producer\u003cT\u003e = std::ptr::read(self.producer.get());\n    let consumer    : spsc::Consumer\u003cT\u003e = std::mem::transmute(producer_val);\n\n    let first  = consumer.try_pop();\n    let second = consumer.try_pop();\n    assert!(second.is_none());\n    if let Some(t) = first {\n        return Err(SendError(t));\n    }\n\n    // consumer drops here; the same memory backs `producer`, so suppress\n    // the double Producer drop:\n    let _ = ManuallyDrop::new(consumer);\n}\nCleaner: restructure Sender\u003cT\u003e to hold producer and consumer in a private enum Endpoint\u003cT\u003e so no transmute is required, or use the bounded_spsc_queue::Producer\u003cT\u003e::reclaim() escape hatch if available.\n\nSuggested patch (secondary defect)\nif let Err(std::sync::mpsc::SendError(_)) = self.send_new.send(new_consumer) {\n    // Receiver has been dropped: take the message back as the public\n    // SendError, the same way the connected==false early-return does.\n    return Err(SendError(t));\n}\nRegression test (release-mode, race shape)\n#[test]\nfn race_disconnect_does_not_corrupt_sender_or_abort() {\n    for _ in 0..200 {\n        let (tx, rx) = unbounded_spsc::channel::\u003cBox\u003cu64\u003e\u003e();\n        let h = std::thread::spawn(move || {\n            for _ in 0..10_000 {\n                let _ = tx.send(Box::new(0xDEAD_BEEF));\n            }\n        });\n        drop(rx);\n        h.join().unwrap();\n    }\n}\nReverse dependencies\nTwo crates on crates.io depend on unbounded-spsc, both owned by you: apis (process-calculus framework) and gooey-rs (tile-UI library, unbounded-spsc gated behind opengl/fmod features). The OpenGL/FMOD callback-mailbox use is a natural rx-drop-during-tx-send scenario at scene-graph teardown. A single coordinated bump cycle is feasible.\n\nResearcher\nBerkant Koc me@berkoc.com\nPGP: 0C588DFD76204987284213EA0AC529C41F8AA5D6",
  "id": "GHSA-6m57-8r3p-pqx6",
  "modified": "2026-06-12T19:31:16Z",
  "published": "2026-05-29T19:05:21Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/spearman/unbounded-spsc/security/advisories/GHSA-6m57-8r3p-pqx6"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-46690"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/spearman/unbounded-spsc"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "unbounded-spsc: Sender::send pointer-as-value transmute causes OOB read and fake-Arc drop under TX/RX race"
}

GHSA-6M68-W836-P72W

Vulnerability from github – Published: 2026-03-06 21:30 – Updated: 2026-03-19 15:31
VLAI
Details

GNU Binutils thru 2.46 readelf contains a double free vulnerability when processing a crafted ELF binary with malformed relocation data. During GOT relocation handling, dump_relocations may return early without initializing the all_relocations array. As a result, process_got_section_contents() may pass an uninitialized r_symbol pointer to free(), leading to a double free and terminating the program with SIGABRT. No evidence of exploitable memory corruption or code execution was observed; the impact is limited to denial of service.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2025-69650"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-415"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-03-06T19:16:10Z",
    "severity": "HIGH"
  },
  "details": "GNU Binutils thru 2.46 readelf contains a double free vulnerability when processing a crafted ELF binary with malformed relocation data. During GOT relocation handling, dump_relocations may return early without initializing the all_relocations array. As a result, process_got_section_contents() may pass an uninitialized r_symbol pointer to free(), leading to a double free and terminating the program with SIGABRT. No evidence of exploitable memory corruption or code execution was observed; the impact is limited to denial of service.",
  "id": "GHSA-6m68-w836-p72w",
  "modified": "2026-03-19T15:31:10Z",
  "published": "2026-03-06T21:30:37Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2025-69650"
    },
    {
      "type": "WEB",
      "url": "https://sourceware.org/bugzilla/show_bug.cgi?id=33698"
    },
    {
      "type": "WEB",
      "url": "https://sourceware.org/bugzilla/show_bug.cgi?id=33700"
    },
    {
      "type": "WEB",
      "url": "https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=81e90cf63a10ad11772c2437c8f2a88f1a00c739"
    },
    {
      "type": "WEB",
      "url": "https://sourceware.org/git/?p=binutils-gdb.git;a=commitdiff;h=ea4bc025abdba85a90e26e13f551c16a44bfa92"
    },
    {
      "type": "WEB",
      "url": "https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=ea4bc025abdba85a90e26e13f551c16a44bfa921"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-6M7R-J2XG-CVHQ

Vulnerability from github – Published: 2024-04-03 15:30 – Updated: 2025-01-14 18:31
VLAI
Details

In the Linux kernel, the following vulnerability has been resolved:

ext4: fix double-free of blocks due to wrong extents moved_len

In ext4_move_extents(), moved_len is only updated when all moves are successfully executed, and only discards orig_inode and donor_inode preallocations when moved_len is not zero. When the loop fails to exit after successfully moving some extents, moved_len is not updated and remains at 0, so it does not discard the preallocations.

If the moved extents overlap with the preallocated extents, the overlapped extents are freed twice in ext4_mb_release_inode_pa() and ext4_process_freed_data() (as described in commit 94d7c16cbbbd ("ext4: Fix double-free of blocks with EXT4_IOC_MOVE_EXT")), and bb_free is incremented twice. Hence when trim is executed, a zero-division bug is triggered in mb_update_avg_fragment_size() because bb_free is not zero and bb_fragments is zero.

Therefore, update move_len after each extent move to avoid the issue.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-26704"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-415"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-04-03T15:15:53Z",
    "severity": "HIGH"
  },
  "details": "In the Linux kernel, the following vulnerability has been resolved:\n\next4: fix double-free of blocks due to wrong extents moved_len\n\nIn ext4_move_extents(), moved_len is only updated when all moves are\nsuccessfully executed, and only discards orig_inode and donor_inode\npreallocations when moved_len is not zero. When the loop fails to exit\nafter successfully moving some extents, moved_len is not updated and\nremains at 0, so it does not discard the preallocations.\n\nIf the moved extents overlap with the preallocated extents, the\noverlapped extents are freed twice in ext4_mb_release_inode_pa() and\next4_process_freed_data() (as described in commit 94d7c16cbbbd (\"ext4:\nFix double-free of blocks with EXT4_IOC_MOVE_EXT\")), and bb_free is\nincremented twice. Hence when trim is executed, a zero-division bug is\ntriggered in mb_update_avg_fragment_size() because bb_free is not zero\nand bb_fragments is zero.\n\nTherefore, update move_len after each extent move to avoid the issue.",
  "id": "GHSA-6m7r-j2xg-cvhq",
  "modified": "2025-01-14T18:31:49Z",
  "published": "2024-04-03T15:30:43Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-26704"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/185eab30486ba3e7bf8b9c2e049c79a06ffd2bc1"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/2883940b19c38d5884c8626483811acf4d7e148f"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/55583e899a5357308274601364741a83e78d6ac4"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/559ddacb90da1d8786dd8ec4fd76bbfa404eaef6"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/afba9d11320dad5ce222ac8964caf64b7b4bedb1"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/afbcad9ae7d6d11608399188f03a837451b6b3a1"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/b4fbb89d722cbb16beaaea234b7230faaaf68c71"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/d033a555d9a1cf53dbf3301af7199cc4a4c8f537"
    },
    {
      "type": "WEB",
      "url": "https://lists.debian.org/debian-lts-announce/2024/06/msg00017.html"
    },
    {
      "type": "WEB",
      "url": "https://lists.debian.org/debian-lts-announce/2024/06/msg00020.html"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-6MF3-XX2M-8J4W

Vulnerability from github – Published: 2026-07-24 18:31 – Updated: 2026-08-13 18:31
VLAI
Details

In the Linux kernel, the following vulnerability has been resolved:

usb: gadget: net2280: Fix double free in probe error path

usb_initialize_gadget() installs gadget_release() as the release callback for the embedded gadget device. The struct net2280 instance is therefore released through gadget_release() when the gadget device's last reference is dropped.

The probe error path calls net2280_remove(), which tears down the partially initialized device and drops the gadget reference with usb_put_gadget(). Calling kfree(dev) afterwards can free the same object again.

Drop the explicit kfree() and let the gadget device release callback handle the final free. This issue was found by a static analysis tool I am developing.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-64242"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-415"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-24T16:16:53Z",
    "severity": "HIGH"
  },
  "details": "In the Linux kernel, the following vulnerability has been resolved:\n\nusb: gadget: net2280: Fix double free in probe error path\n\nusb_initialize_gadget() installs gadget_release() as the release\ncallback for the embedded gadget device.  The struct net2280 instance is\ntherefore released through gadget_release() when the gadget device\u0027s last\nreference is dropped.\n\nThe probe error path calls net2280_remove(), which tears down the\npartially initialized device and drops the gadget reference with\nusb_put_gadget().  Calling kfree(dev) afterwards can free the same object\nagain.\n\nDrop the explicit kfree() and let the gadget device release callback\nhandle the final free.  This issue was found by a static analysis tool\nI am developing.",
  "id": "GHSA-6mf3-xx2m-8j4w",
  "modified": "2026-08-13T18:31:10Z",
  "published": "2026-07-24T18:31:28Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-64242"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/085652fda7f38040d1a2c42d72614f418feb843f"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/48f89ead20e48d447ad29fa937b43b9fa981cf28"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/550fa4d071a8c8e53072900869d37ae6abf4999d"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/71b3391dc81655ff058492f8e9d013b2c6e5747b"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/c5b9fdb1e8ddf50bc6272927edb118679f170350"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/c8547c74988e0b5f4cbb1b895e2a57aae084f070"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/db2b72e83a0208ae2b3b270bf91662b1c6849a9b"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/e6f8be12f0307145b9a6010f044925952b37de8b"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-6MQ7-W565-8RF7

Vulnerability from github – Published: 2023-06-19 18:30 – Updated: 2024-04-04 04:58
VLAI
Details

A vulnerability was found in drivers/cpufreq/qcom-cpufreq-hw.c in cpufreq subsystem in the Linux Kernel. This flaw, during device unbind will lead to double release problem leading to denial of service.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2023-3312"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-415"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2023-06-19T18:15:09Z",
    "severity": "HIGH"
  },
  "details": "A vulnerability was found in drivers/cpufreq/qcom-cpufreq-hw.c in cpufreq subsystem in the Linux Kernel. This flaw, during device unbind will lead to double release problem leading to denial of service.",
  "id": "GHSA-6mq7-w565-8rf7",
  "modified": "2024-04-04T04:58:02Z",
  "published": "2023-06-19T18:30:49Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2023-3312"
    },
    {
      "type": "WEB",
      "url": "https://patchwork.kernel.org/project/linux-pm/patch/20230323174026.950622-1-krzysztof.kozlowski%40linaro.org"
    },
    {
      "type": "WEB",
      "url": "https://patchwork.kernel.org/project/linux-pm/patch/20230323174026.950622-1-krzysztof.kozlowski@linaro.org"
    },
    {
      "type": "WEB",
      "url": "https://security.netapp.com/advisory/ntap-20230731-0005"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-6P6F-GC59-4W3F

Vulnerability from github – Published: 2024-08-07 18:30 – Updated: 2024-08-08 15:31
VLAI
Details

In the Linux kernel, the following vulnerability has been resolved:

mm: fix crashes from deferred split racing folio migration

Even on 6.10-rc6, I've been seeing elusive "Bad page state"s (often on flags when freeing, yet the flags shown are not bad: PG_locked had been set and cleared??), and VM_BUG_ON_PAGE(page_ref_count(page) == 0)s from deferred_split_scan()'s folio_put(), and a variety of other BUG and WARN symptoms implying double free by deferred split and large folio migration.

6.7 commit 9bcef5973e31 ("mm: memcg: fix split queue list crash when large folio migration") was right to fix the memcg-dependent locking broken in 85ce2c517ade ("memcontrol: only transfer the memcg data for migration"), but missed a subtlety of deferred_split_scan(): it moves folios to its own local list to work on them without split_queue_lock, during which time folio->_deferred_list is not empty, but even the "right" lock does nothing to secure the folio and the list it is on.

Fortunately, deferred_split_scan() is careful to use folio_try_get(): so folio_migrate_mapping() can avoid the race by folio_undo_large_rmappable() while the old folio's reference count is temporarily frozen to 0 - adding such a freeze in the !mapping case too (originally, folio lock and unmapping and no swap cache left an anon folio unreachable, so no freezing was needed there: but the deferred split queue offers a way to reach it).

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2024-42234"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-415"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2024-08-07T16:15:46Z",
    "severity": "MODERATE"
  },
  "details": "In the Linux kernel, the following vulnerability has been resolved:\n\nmm: fix crashes from deferred split racing folio migration\n\nEven on 6.10-rc6, I\u0027ve been seeing elusive \"Bad page state\"s (often on\nflags when freeing, yet the flags shown are not bad: PG_locked had been\nset and cleared??), and VM_BUG_ON_PAGE(page_ref_count(page) == 0)s from\ndeferred_split_scan()\u0027s folio_put(), and a variety of other BUG and WARN\nsymptoms implying double free by deferred split and large folio migration.\n\n6.7 commit 9bcef5973e31 (\"mm: memcg: fix split queue list crash when large\nfolio migration\") was right to fix the memcg-dependent locking broken in\n85ce2c517ade (\"memcontrol: only transfer the memcg data for migration\"),\nbut missed a subtlety of deferred_split_scan(): it moves folios to its own\nlocal list to work on them without split_queue_lock, during which time\nfolio-\u003e_deferred_list is not empty, but even the \"right\" lock does nothing\nto secure the folio and the list it is on.\n\nFortunately, deferred_split_scan() is careful to use folio_try_get(): so\nfolio_migrate_mapping() can avoid the race by folio_undo_large_rmappable()\nwhile the old folio\u0027s reference count is temporarily frozen to 0 - adding\nsuch a freeze in the !mapping case too (originally, folio lock and\nunmapping and no swap cache left an anon folio unreachable, so no freezing\nwas needed there: but the deferred split queue offers a way to reach it).",
  "id": "GHSA-6p6f-gc59-4w3f",
  "modified": "2024-08-08T15:31:28Z",
  "published": "2024-08-07T18:30:43Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-42234"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/be9581ea8c058d81154251cb0695987098996cad"
    },
    {
      "type": "WEB",
      "url": "https://git.kernel.org/stable/c/fc7facce686b64201dbf0b9614cc1d0bfad70010"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H",
      "type": "CVSS_V3"
    }
  ]
}

Mitigation
Architecture and Design

Choose a language that provides automatic memory management.

Mitigation
Implementation

Ensure that each allocation is freed only once. After freeing a chunk, set the pointer to NULL to ensure the pointer cannot be freed again. In complicated error conditions, be sure that clean-up routines respect the state of allocation properly. If the language is object oriented, ensure that object destructors delete each chunk of memory only once.

Mitigation
Implementation

Use a static analysis tool to find double free instances.

No CAPEC attack patterns related to this CWE.