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

cam_periph_invalidate()'s underflow guard is dead code: `u_int32_t refcount < 0` never fires, so periph refcount underflows (DF-5273/DF-5327 classes) are silent

Summary

The CAM core's ONLY periph-refcount underflow detector compares unsigned < 0 - can never fire. Guest-verified: the DF-5273 burn drove a live cd periph to -5, invalidate ran on it, zero 'refcount < 0' warnings in dmesg - the underflowed periph silently became a permanent zombie. This dead guard is why the whole DF-5273/5327 corruption class shipped unnoticed. Signed-compare + release-underflow-trap fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5329 Β· 7 files
FileTypeDescriptionSize
README.md β€” 1.7 KB ↓ raw
VERDICT.md β€” 1.8 KB ↓ raw
poc_guard.c β€” 2.1 KB view raw
poc_ref.c β€” 2.6 KB view raw
poc_ref.out β€” 284 B view raw
fix.diff β€” 861 B view raw
verdict.json β€” 2.2 KB view raw

DF-5329 evidence pack β€” cam_periph_invalidate() underflow guard is dead code (u_int32_t refcount < 0 is always false)

Finding (one line)

cam_periph_invalidate() (sys/bus/cam/cam_periph.c:576-577) is the only place the CAM core notices a periph refcount underflow, and its check else if (periph->refcount < 0) can never be true because struct cam_periph.refcount is u_int32_t (sys/bus/cam/cam_periph.h:123). Every underflow of the class proven by DF-5273 / DF-5327 therefore sails through silently as 0xFFFFFFxx: no warning, no assert β€” and the periph becomes permanently unfreeable (zombie) or freeable at a wrapped-wrong moment.

Reproduce (guest, root; evilcd2 kld loaded)

cd /root/df5327
cc -O -o poc_ref poc_ref.c          # DF-5273 burn PoC (shared rig)
timeout 45 usched :1 ./poc_ref /dev/cd1
sysctl -n debug.evilcd.refcnt       # observe negative refcount
sysctl -n debug.evilcd.invalidate=1 # run cam_periph_invalidate() on it
dmesg | grep -c "refcount < 0"      # expect 0 (guard never fires)

Expected (vulnerable kernel)

[poc] round 0 burn 0: ioctl rc=-1 errno=4(Interrupted system call)  refcount=-1
[poc] round 0 burn 1: ioctl rc=-1 errno=4(Interrupted system call)  refcount=-2
[poc] round 0 burn 2: ioctl rc=-1 errno=4(Interrupted system call)  refcount=-3
...
sysctl refcnt -> -5
dmesg grep "refcount < 0" -> 0      <-- guard dead while rc < 0

Observed on guest (see poc_ref.out): refcount driven to -5 across runs, cam_periph_invalidate() invoked (debug.evilcd.invalidate) with the count negative, zero occurrences of the "cam_invalidate_periph: refcount < 0!!" message. The only observability hook for this corruption class provably never fires on the running kernel.

VERDICT.md
↓ download raw

DF-5329 VERDICT

Status: REPRODUCED (the guard provably never fires)

What was reproduced

cam_periph_invalidate() (sys/bus/cam/cam_periph.c:576-577) contains the CAM core's ONLY underflow detector:

else if (periph->refcount < 0)
        kprintf("cam_invalidate_periph: refcount < 0!!\n");

refcount is u_int32_t (sys/bus/cam/cam_periph.h:123), so the comparison is always false and the message can never print.

Guest proof (evilcd2 rig, cd periph; DF-5273's burn PoC drives the refcount negative):

[poc] round 0 burn 0: ... refcount=-1
[poc] round 0 burn 1: ... refcount=-2
[poc] round 0 burn 2: ... refcount=-3
sysctl debug.evilcd.refcnt -> -5
sysctl debug.evilcd.invalidate=1   # runs cam_periph_invalidate()
dmesg | grep -c "refcount < 0" -> 0

With the count at 0xFFFFFFFB, cam_periph_invalidate() ran and printed nothing; the periph became a silent zombie (not freed at 0, not reported negative, permanently stuck until reboot). Repeated across two guest sessions.

Impact

Observability/containment gap only (the underlying underflows are DF-5273/DF-5327's bugs). It converts a detect-and-warn condition into a silent integrity failure and removes the only diagnostic that would have surfaced this bug class in the field. Info severity.

Fix

fix.diff: signed comparison in the guard plus an underflow check (and loud report, no release) in cam_periph_release()'s fast path so a wrapped count cannot keep sinking. Validated by inspection only (Info finding; no kernel rebuild spent).

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched clean

fix.diff authored (guard cast + release-side underflow trap); not kernel-validated -- Info severity, validation budget spent on DF-5327

fix.diff; code inspection
↓ fix.diffper-fix-DF-5329

Confirmed kernel references

Detail

Evidence (decisive lines)

poc_ref.out (refcount -1 -> -3 lines), sysctl refcnt=-5, `dmesg | grep -c "refcount < 0" -> 0` after invalidate; README.md reproduce block

PoC changes

reused DF-5273's poc_ref burn unchanged; added poc_guard.c wrapper that also invokes invalidate and greps dmesg

Verified recommended fix

signed comparison in the guard plus an underflow check + loud report in cam_periph_release()'s fast path (fix.diff)

Verdict

cam_periph_invalidate()'s `else if (periph->refcount < 0)' is dead code (u_int32_t). Guest proof: DF-5273 burn drove the cd periph refcount to -5 (0xFFFFFFFB), cam_periph_invalidate() was invoked on it, and zero occurrences of the warning appeared in dmesg. The guard this audit relied on for detection never fires on a stock kernel.