Skip to content

Fix use-after-free and double-free in RemoveFilament - #6358

Open
tritsystem wants to merge 1 commit into
cfengine:masterfrom
tritsystem:fix-removefilament-use-after-free
Open

tritsystem wants to merge 1 commit into
cfengine:masterfrom
tritsystem:fix-removefilament-use-after-free

Conversation

@tritsystem

Copy link
Copy Markdown

Summary

RemoveFilament() in libpromises/dbm_test_api.c frees filament (and destroys filament->items) when StringSetSize(filament->items) == 0, but had no return after that branch — every other early-exit branch in this function does.

Without it, execution falls through unconditionally to:

bool success = OpenDB(&db, filament->db_id);

a use-after-free read of the just-freed filament. At the end of the function, StringSetDestroy(filament->items) and free(filament) run again unconditionally on that same path — a double-free of both filament and its items set.

Fix is a single return; added right after the free(filament);, matching the pattern every other early-exit branch in this same function already follows.

Test plan

  • Verified with a standalone AddressSanitizer repro: RemoveFilament()'s real body extracted verbatim, with minimal stub types matching the actual DBFilament/StringSet shapes. Built with -fsanitize=address, called with a filament whose items set has size 0 (the branch that frees filament).
    • Without the fix: ASan reports a genuine heap-use-after-free at the OpenDB(&db, filament->db_id) line.
    • With the fix: clean exit, no ASan report.
  • Could not build/run the full libpromises test suite in this environment (no configured build toolchain for this repo here); the fix is a minimal, one-line change matching the existing early-return convention used throughout the same function, so the risk of the change itself is low.
  • codespell was run against the modified file; it flagged one pre-existing, unrelated typo elsewhere in the file (fillament at line 662), not touched by this change.

🤖 Generated with Claude Code

RemoveFilament() frees `filament` (and destroys filament->items) when
StringSetSize(filament->items) == 0, but had no `return` after that
branch -- every other early-exit branch in this function does. Execution
fell through unconditionally to `OpenDB(&db, filament->db_id)`, a
use-after-free read of the just-freed filament, and at the end of the
function `StringSetDestroy(filament->items)` / `free(filament)` ran
again unconditionally -- a double-free of both filament and its items
set on that path.

Verified with a standalone AddressSanitizer repro (RemoveFilament's body
extracted verbatim, stub types matching the real DBFilament/StringSet
shapes): unfixed, ASan reports a real heap-use-after-free at the
OpenDB(&db, filament->db_id) line; with this one-line `return;` added
(matching every other early-exit branch's own pattern in this file),
clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@larsewi

larsewi commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Thanks for the contribution @tritsystem

@cf-bottom Jenkins please :)

@larsewi larsewi self-assigned this Sep 17, 2026
@larsewi larsewi added the cherry-pick? Fixes which may need to be cherry-picked to LTS branches label Sep 17, 2026
@cfengine cfengine deleted a comment from cf-bottom Sep 17, 2026
@cf-bottom

Copy link
Copy Markdown

@tritsystem

Copy link
Copy Markdown
Author

Thanks for the quick response, @larsewi — appreciate you triggering the build. Let me know if there's anything else you'd like me to check or adjust.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cherry-pick? Fixes which may need to be cherry-picked to LTS branches

Development

Successfully merging this pull request may close these issues.

3 participants