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)
PoC verification
Evidence pack
findings/poc/DF-5551 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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
fixedsame 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
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.
No comments yet.