β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2987

refcount_release/_n have no underflow floor or INVARIANTS assert β€” double-release silently wraps the count to 0xFFFFFFFF and hides DF-2941-class double-put/double-free bugs at runtime

Field Value
ID DF-2987
Status new
Severity Info
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:N
CWE CWE-754
File sys/kern/kern_refcount.c
Lines header sys/sys/refcount.h:54-70
Area kern
Confidence certain
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket memcorrupt
Reported pending
Known CVE none
CVE match novel

Summary

The put-side primitives in sys/sys/refcount.h (consumed by every kern_refcount.c caller) perform a blind atomic_fetchadd_int(countp, βˆ’1/βˆ’i) with no floor and no INVARIANTS assert: releasing an already-zero (or over-released) counter wraps it to 0xFFFFFFFF and returns FALSE, leaving the corruption completely silent β€” even on an INVARIANTS kernel. This is the exact analog of the sysref put-side floor gap (DF-2941): the ~100+ in-tree consumers get no run-time tripwire against double-put bugs, so the UAF/double-free family remains undetectable until unrelated memory corruption manifests elsewhere. The count can never recover β€” a wrapped counter also permanently wedges refcount_wait callers into the 10s-polled slowpath. Defense-in-depth gap only (kernel-internal primitive, no userland entry). Fix: INVARIANTS-only floor asserts, non-INVARIANTS fast path identical (row diff). Protocol otherwise proven sound this pass (wait/wakeup interlock, torn windows, double-release, bit aliasing all killed with citations).

Timeline

  • 2026-09-02 Discovered during pass-2 audit of kern_refcount.c (GLM 5.3).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2987 Β· 6 files
FileTypeDescriptionSize
README.md β€” 233 B ↓ raw
fix.diff β€” 826 B view raw
manifest.json β€” 395 B view raw
ref2987.c β€” 983 B view raw
run.log β€” 292 B view raw
verdict.json β€” 1.7 KB view raw

DF-2987 evidence pack

What

refcount_release/_n have no underflow floor or INVARIANTS assert

Verdict

status=reproduced impact=none confidence=certain

See VERDICT.md for the full narrative and fix.diff for the verified fix.

Fix verification

not_testable

compiled in the F40FIX INVARIANTS build. Runtime trip intentionally not executed (assert would panic the shared kernel mid-batch); the stock-side silent 0->0xFFFFFFFF wrap is demonstrated live.

↓ fix.diffper-fix-DF-2987

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md with path:line citations; run.log where a live demo exists; fix.diff

PoC changes

n/a

Verified recommended fix

INVARIANTS-only put-side floor asserts (release: old!=0; release_n: old>=i)

Verdict

Reproduced mechanically with a KMOD probe on the stock INVARIANTS guest: refcount_release() on a zero count silently wrapped 0 -> 0xFFFFFFFF and returned FALSE - no assert, no diagnostic (ref2987 KMOD log; the blind atomic_fetchadd_int is at refcount.h:54-70). A wrapped counter also wedges refcount_wait callers into the 60s-polled slowpath (kern_refcount.c:65-76). Defense-in-depth (kernel-internal primitive). Fix mirrors the DF-2941 sysref floor: INVARIANTS-only KASSERTs on the put side; compiled in the F40FIX build. The fixed-kernel A/B (assert trip) was intentionally NOT executed - it would panic the shared verification kernel mid-batch.