# Double free on error and cleanup paths

A buffer freed on an error or teardown path without clearing the pointer or its length, then freed again

_Area: Memory corruption and native code. Seen in 5 disclosed reports; 2 confirmed bounties: $200 and $1,500; latest 2022-01-01. Updated 2026-10-23._

## How it works
A buffer or state object is freed on an error or teardown path, but the owning pointer is not cleared, or two cleanup paths both run. A later free of the same address corrupts allocator metadata. The broken assumption is that "freed" and "pointer is now invalid" are the same thing; they are not unless the code nulls the pointer or tracks ownership. Two shapes recur: a pointer freed without NULLing so teardown frees it again [E1](https://bugbountylibrary.com/r/p0%3ACVE-2022-22265.md)[E4](https://bugbountylibrary.com/r/h1%3A1269242.md), and a parser that re-processes the same stack entry so a field is freed twice across two passes of one element [E2](https://bugbountylibrary.com/r/h1%3A146255.md). A third: a shared allocation freed by each child that aliases it [E3](https://bugbountylibrary.com/r/h1%3A193719.md).

## Where it shows up
- Error branches that free/kfree/DESTROY and then fall through to a common exit that frees again [E5](https://bugbountylibrary.com/r/h1%3A73256.md).
- ioctl handlers that free a heap pointer on a bad-argument path without setting it to NULL, then free it again at stream/queue teardown [E1](https://bugbountylibrary.com/r/p0%3ACVE-2022-22265.md).
- Send/recv state machines that free a buffer after partial I/O but leave the length/count field set, so the next call re-enters the free path [E4](https://bugbountylibrary.com/r/h1%3A1269242.md).
- SAX-style deserializers where the character-data callback re-processes a stack entry already handled by the start-element callback [E2](https://bugbountylibrary.com/r/h1%3A146255.md).
- Cleanup that nulls a shared pointer only at the top level while nested objects still alias it [E3](https://bugbountylibrary.com/r/h1%3A193719.md).

## How to review
For every allocation, trace all paths that free it and confirm exactly one free per allocation. The invariant: after any free, the owning pointer is NULL or ownership is transferred, and no other live reference aliases it. Concrete checks: grep for free/kfree/DESTROY without a following `= NULL` (a heuristic that flags candidates, not proof—confirm each by tracing); trace common-exit labels to see whether an error branch and the normal exit both free [E5](https://bugbountylibrary.com/r/h1%3A73256.md); for state machines, confirm the length/count is reset alongside the buffer [E4](https://bugbountylibrary.com/r/h1%3A1269242.md); for parsers, confirm a stack entry is not processed twice [E2](https://bugbountylibrary.com/r/h1%3A146255.md); for shared allocations, require refcounts or ownership flags rather than pointer equality [E3](https://bugbountylibrary.com/r/h1%3A193719.md). Fuzz invalid arguments through ioctl and parse paths to force error branches [E1](https://bugbountylibrary.com/r/p0%3ACVE-2022-22265.md)[E2](https://bugbountylibrary.com/r/h1%3A146255.md). Run ASAN or a double-free sanitizer and allocator hardening to catch what review misses.

## Variants seen
- Error path frees without NULLing; teardown frees again [E1](https://bugbountylibrary.com/r/p0%3ACVE-2022-22265.md).
- Parser re-processes the same stack entry, freeing a field twice [E2](https://bugbountylibrary.com/r/h1%3A146255.md).
- Shared filename freed by nested irep that does not own it [E3](https://bugbountylibrary.com/r/h1%3A193719.md).
- Partial-send free leaves stale count, re-free on next call [E4](https://bugbountylibrary.com/r/h1%3A1269242.md).
- Free in error branch plus free before return [E5](https://bugbountylibrary.com/r/h1%3A73256.md).

## Impact and severity
Kernel memory corruption and local privilege escalation, exploited in the wild [E1](https://bugbountylibrary.com/r/p0%3ACVE-2022-22265.md); heap corruption enabling controlled allocation and potential RCE in PHP [E2](https://bugbountylibrary.com/r/h1%3A146255.md)[E5](https://bugbountylibrary.com/r/h1%3A73256.md); UAF and double-free in libcurl [E4](https://bugbountylibrary.com/r/h1%3A1269242.md); compiler crash [E3](https://bugbountylibrary.com/r/h1%3A193719.md). Severity rises when reachable from an unprivileged or sandboxed context [E1](https://bugbountylibrary.com/r/p0%3ACVE-2022-22265.md) and reliably triggerable by malformed input.

## Evidence
- E1: https://bugbountylibrary.com/r/p0%3ACVE-2022-22265.md
- E2: https://bugbountylibrary.com/r/h1%3A146255.md
- E3: https://bugbountylibrary.com/r/h1%3A193719.md
- E4: https://bugbountylibrary.com/r/h1%3A1269242.md
- E5: https://bugbountylibrary.com/r/h1%3A73256.md
- E6: https://bugbountylibrary.com/r/h1%3A2621057.md
