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

camq_insert() into a full queue writes a kernel pointer one slot past the array (full-queue guard is DIAGNOSTIC-only)

Summary

camq_insert's only capacity guard is #ifdef DIAGNOSTIC: inserting into a full queue writes queue_array[array_size+1] (one pointer past the kmalloc) and sets entries=array_size+1 - heap_up/down then walk OOB slots on every later op. Production-primitives harness: 5th entry into 4-capacity -> wrote 0xfffff8008d2e51c0 PAST the array + adjacent canary chunk CORRUPTED, all silent on the stock kernel. Standalone reachability needs accounting drift (ccbq/devq opening miscounts under the split-lock design) or is the automatic aftermath of DF-5550's persistent state - hence Medium. Refuse-return fix validated.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5551 Β· 3 files
FileTypeDescriptionSize
README.md β€” 1.5 KB ↓ raw
verdict.json β€” 2.5 KB view raw
env.txt β€” 239 B view raw

DF-5551 β€” camq_insert() into a full queue writes one pointer past the array (guard is DIAGNOSTIC-only)

File: sys/bus/cam/cam_queue.c:145-157 Severity: Medium Evidence: harness test T3 in findings/poc/DF-5550/ (same file, same root-cause family, same harness; fix hunk included in DF-5550's fix.diff).

The full-queue guard at cam_queue.c:148-151 is #ifdef DIAGNOSTIC β€” compiled out of every stock kernel. On a full queue the insert writes queue_array[array_size+1] (a kernel pointer one slot past the kmalloc'd array) and sets entries = array_size+1; heap_up/heap_down then walk out-of-bounds slots on every later operation.

Reachability standalone: any accounting drift that lets a caller exceed capacity β€” e.g. the DF-5550 corrupted state (entries > array_size persists after an unguarded shrink, so every subsequent insert writes OOB), or ccbq/devq opening miscounts under the split-lock design (devq mutated under xpt_topo_lock in xpt_alloc_device/xpt_release_device, everything else under the per-sim lock; struct camq itself carries no lock, cam_queue.h:46-52).

Proof (guest, stock kernel, production primitives):

[CAMQTEST] T3 inserting 5th entry into 4-capacity queue (guard is DIAGNOSTIC-only)...
[CAMQTEST] T3 after: entries=5 array_size=4 (wrote queue_array[5] == 0xfffff8008d403fb0 PAST the array)
[CAMQTEST] T3: canary 63 (0xfffff8008d482500) CORRUPTED at +0: 80 (was a5)
[CAMQTEST] T3: 1 CANARY(IES) OVERWRITTEN -> heap OOB WRITE proven

Fix: unconditional refusal (see DF-5550 fix.diff hunk 2).

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

same patched-kernel validation run as DF-5550: insert refused, entries==array_size==4, all canaries intact; baseline corruption gone.

DF-5550/fix_run.log T3 section vs DF-5550/run.log T3 section
per-fix-DF-5551

Confirmed kernel references

Detail

Exploit chain

kldload camqtest.ko; sysctl debug.camqtest.run=3 -> one-pointer heap OOB write past an M_CAMQ array + entries=array_size+1 persistent corruption; every later heap_up/heap_down/camq_remove on that queue then walks past the array. Standalone reachability needs an accounting drift (e.g. the DF-5550 corrupted state); no uid=0 chain developed.

Evidence (decisive lines)

DF-5550/run.log T3 lines: 'T3 after: entries=5 array_size=4 (wrote queue_array[5] == ... PAST the array)' + 'canary 63 ... CORRUPTED'; DF-5550/fix_run.log: 'entries=4 array_size=4 ... all 64 canaries intact'

PoC changes

authored fresh; shares camqtest.ko with DF-5550 (T3 is this finding's test)

Verified recommended fix

unconditionally refuse the insert when entries >= array_size (see DF-5550 fix.diff hunk 2)

Verdict

camq_insert()'s full-queue guard (cam_queue.c:148-151) is #ifdef DIAGNOSTIC-only and compiled out of stock kernels. Harness T3 on the unmodified production kernel: inserting a 5th entry into a 4-capacity camq wrote queue_array[5] -- a kernel pointer one slot past the kmalloc'd array -- set entries=5 > array_size=4, and overwrote the adjacent canary chunk ('CORRUPTED at +0: d0 (was a5)'). Post-fix (same fix.diff hunk 2, validated in DF-5550's pack) the insert is refused: entries stays 4, all canaries intact.