Off-by-one stack buffer overflow in crom_next depth guard allows kernel stack corruption
Summary
crom_next() checks cc->depth >= CROM_MAX_DEPTH before incrementing depth but CROM_MAX_DEPTH is 10 and stack[] has exactly 10 elements (indices 0-9). At depth==9 the guard 9>=10 is false so execution continues to depth++ (now 10) then ptr=&cc->stack[10] which is one past end. Writes ptr->dir (8 bytes attacker-controlled pointer) and ptr->index (4 bytes). Total 16 bytes written past stack array. Correct guard should be >= CROM_MAX_DEPTH-1. After overflow goto check dereferences ptr->dir->crc_len - attacker-controlled pointer read causing info leak or kernel panic. Trigger: malicious FireWire device with Config ROM containing chain of 10 nested CSRTYPE_D directory entries. Stack[10] writes at offset 168 of crom_context struct into saved registers/adjacent locals.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2289 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | gate analysis + off-by-one depth-guard trace | 3.5 KB | β raw |
| fix.diff | suggested-fix | guard depth >= CROM_MAX_DEPTH-1 | 371 B | view raw |
| build.sh | build-script | documents HW gate | 145 B | view raw |
| run.sh | run-script | prints gate proof | 257 B | view raw |
| env.txt | environment | guest env | 1.1 KB | view raw |
| gate_proof.txt | gate-proof | no fwohci, no /dev/fw* | 61 B | view raw |
DF-2289 β Off-by-one stack overflow in crom_next depth guard (fwcrom.c)
Verdict: NOT REPRODUCED (HW-gated); REAL BUG IN SOURCE (defense-in-depth fix warranted)
Hardware gate (why the PoC cannot run on this guest)
crom_next() parses IEEE 1394 Configuration ROM and is reached via the FireWire
stack (e.g. crom_init_context / crom_search_key invoked while enumerating
remote nodes). The audit QEMU/KVM guest has no FireWire controller and no
/dev/fw* device:
$ pciconf -l | grep -iE "fwohci|firewire" # (no output) $ ls /dev/fw* # /dev/fw*: No such file or directory $ ifconfig -l # vtnet0 lo0
Without an fwohci controller and a live 1394 peer there is no path to feed a
crafted Config-ROM into crom_next. The unprivileged maxx user cannot open
/dev/fw* (it does not exist), so no ioctl or userspace path reaches the parser
on this guest.
Source trace β the bug is REAL (sys/bus/firewire/fwcrom.c)
Layout (iec13213.h:200-209):
#define CROM_MAX_DEPTH 10
struct crom_ptr { struct csrdirectory *dir; int index; };
struct crom_context { int depth; struct crom_ptr stack[CROM_MAX_DEPTH]; };
/* stack[0..9], 10 elements */
crom_next() (fwcrom.c:106-143):
reg = crom_get(cc); /* cc->stack[cc->depth] */
if ((reg->key & CSRTYPE_MASK) == CSRTYPE_D) {
if (cc->depth >= CROM_MAX_DEPTH) { /* line 115: checks BEFORE incr */
kprintf("crom_next: too deep\n");
goto again;
}
cc->depth ++; /* line 119: now depth == 10 */
ptr = &cc->stack[cc->depth]; /* line 121: &stack[10] = ONE PAST END */
ptr->dir = (struct csrdirectory *)(reg + reg->val); /* 8-byte attacker ptr */
ptr->index = 0; /* + 4 bytes */
goto check;
}
The guard at line 115 fires only when depth >= 10, but the array's last legal
index is 9. So when the parser enters crom_next at depth == 9 (10th level
of nesting already reached) and the current entry is a directory
(CSRTYPE_D), the guard 9 >= 10 is false, execution proceeds to cc->depth++
(now 10) and ptr = &cc->stack[10] β one past the end of the 10-element array.
ptr->dir (an 8-byte attacker-controlled pointer derived from reg->val) and
ptr->index (4 bytes) are then written, a 16-byte stack overflow into
whatever follows crom_context.stack[] (saved registers / adjacent locals). The
correct guard is cc->depth >= CROM_MAX_DEPTH - 1 (i.e. >= 9), or move the
increment-before-check.
After the overflow, goto check dereferences ptr->dir->crc_len
(fwcrom.c:130) β an attacker-controlled pointer read β kernel info-leak or
panic.
Trigger: a malicious FireWire device whose Config ROM contains a chain of 10
nested CSRTYPE_D directory entries. No authentication; physical bus access.
Exploit chain status
Not pursuable β primitive is behind absent FireWire hardware (valid Phase-6 hard blocker: dead path at runtime + no harness can drive it on this guest without a live 1394 peer). The bug is a real 16-byte controlled stack overflow on FireWire-equipped hardware.
PoC changes
None. No PoC runnable: /dev/fw* absent, fwohci controller absent. Verified
by source trace only.
Recommended fix
Tighten the depth guard so the increment never reaches the sentinel index. See
fix.diff (matches the finding proposal's intent β guard against
depth >= CROM_MAX_DEPTH - 1).
Fix verification
not_testablenot_testable: PoC cannot run on this guest (HW-gated, no target device). fix.diff validated by git apply --check (clean) + line-accurate source trace confirming it closes the cited path.
git apply --check findings/poc/DF-2289/fix.diff -> OK (clean apply). No runtime test possible (HW-gated).
Confirmed kernel references
Detail
Exploit chain
none β valid hard blocker (driver/device path dead at runtime on this guest: no target HW / no attached device). No unprivileged->root path.
Evidence (decisive lines)
usbconfig list -> No device match or lack of permissions.; pciconf -l -> no target controller HW; ifconfig -l -> vtnet0 lo0; kldstat -> kernel/ehci/xhci only; ls /dev/<target> -> No such file or directory; id maxx -> uid=1001 groups=1001 (not operator). Source confirmed at cited lines.
PoC changes
Created findings/poc/DF-2289/{VERDICT.md,fix.diff,build.sh,run.sh,env.txt,gate_proof.txt,manifest.json}. No PoC source (HW-gated).
Verified recommended fix
change guard to cc->depth >= CROM_MAX_DEPTH-1. Full git-apply-able diff in findings/poc/DF-2289/fix.diff (git apply --check OK).
Verdict
NOT REPRODUCED (HW-gated). The bug is REAL in source (traced line-by-line). fwcrom crom_next off-by-one stack overflow (depth>=10 guard is one late); no FireWire. Gate confirmed via usbconfig list (No device match / no /dev/ugen*), pciconf -l (no target controller HW), ifconfig (vtnet0 lo0 only), kldstat (no target module), and ls /dev (no target nodes). maxx (uid 1001, not in operator) cannot reach any /dev/usbctl write path.
No comments yet.