Missing NUL-termination of PnP string enables OOB stack read in search_token()
Summary
ppb_pnp_detect() allocates 257-byte stack buffer str[PPB_PnP_STRING_SIZE+1] but ppb_1284_read_id only bzeros 256 bytes. The 257th byte uninitialized. If peripheral returns exactly 256 non-NUL bytes str[256] is uninitialized stack garbage and search_token UNKNOWN_LENGTH scan loop walks past str[255] into adjacent stack memory reading kernel stack contents (info leak of return addresses/pointers) and potentially crashing if walks into unmapped guard page. ppb_pnp_detect never executes str[len]=\0. [VERIFIED pass2: reproduced - 256-byte NUL-free ID crosses uninitialized str 256 plus adjacent stack, colon-scan leaves object, NULL+1 panic; same walk is the console leak when a colon is found; str 256 NUL fix-validated]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2328 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| ppbharness3.c | β | 13.1 KB | view raw | |
| Makefile | β | 484 B | β download | |
| run.log | β | 1.3 KB | view raw | |
| panic.txt | β | 3.3 KB | view raw | |
| panic.ref-2327.txt | β | 1.0 KB | view raw | |
| fix.diff | β | 584 B | view raw | |
| verdict.json | β | 2.5 KB | view raw | |
| README.md | β | 1.4 KB | β raw |
DF-2328 β missing NUL termination of the 257-byte PnP buffer enables OOB stack reads
What (sys/bus/ppbus/ppbconf.c:219 + ppb_1284.c:595)
ppb_pnp_detect declares char str[PPB_PnP_STRING_SIZE+1] (257 bytes)
but ppb_1284_read_id bzeros only max = 256 bytes and never writes a
terminator; ppb_pnp_detect never executes str[len]=0. With a 256-byte
NUL-free device ID, str[256] is uninitialized stack garbage and every
search_token(..., UNKNOWN_LENGTH, ...) scan walks past str[255] into
uninitialized and out-of-bounds stack (info leak of stack contents to
the console when a ':' is crossed; page fault when the walk returns
NULL and the caller computes NULL+1).
Rig / Reproduce (same ppbharness3 rig, one boot)
sysctl hw.ppbx3.test=2 # 256 'A's, "REV" at 252, no colon
Result (run.log + panic.txt)
ppb: <PnP> 256 characters: A ... A R E V A ppbus0: <unknown unknown.panic with 1 spinlocks held panic: page fault ... strlen() <- kprintf
The ID contains NO colon and NO NUL: the REV ':'-scan necessarily left the object (crossing str[256] and the adjacent stack) before returning NULL -> NULL+1 -> trap. The same unchecked walk is the info leak whenever the crossed stack bytes happen to contain ':'.
Fix
fix.diff terminates str[PPB_PnP_STRING_SIZE] = '\0' before parsing. Validated on the patched rig (VERDICT.md): no trap, clean parse.
Fix verification
fixedConfirmed kernel references
Detail
Exploit chain
256-byte NUL-free ID from malicious peripheral -> read_id clears only 256 of 257 bytes -> UNKNOWN_LENGTH scan leaves the object -> crosses uninitialized str[256] and stack (OOB read; console leak if a colon is crossed) -> NULL on colon-free stacks -> NULL+1 -> Fatal trap 12
Evidence (decisive lines)
run.log + panic.txt: console dump of the full 256-byte ID (A...REVA), the <unknown unknown. partial print, strlen/kprintf backtrace
PoC changes
Same rig as DF-2327 (test=2); crafted ID generated in the worker.
Verified recommended fix
Force str[PPB_PnP_STRING_SIZE] = 0 before parsing (fix.diff); belt-and-braces with the DF-2327 guard.
Verdict
REPRODUCED: with the same verbatim-ppbconf rig, a 256-byte NUL-free device ID ending in REVA (REV at offset 252, no colon anywhere) drives the REV branch search_token(&str[252], UNKNOWN_LENGTH, :) past str[255] into str[256] -- which ppb_1284_read_id never clears (it bzeros only max=256 of the 257-byte object, ppb_1284.c:595) -- and onward into adjacent stack: the walk finds no colon in the uninitialized/out-of-bounds bytes, returns NULL, and ppbconf.c:259 hands NULL+1 to kprintf: panic page fault in strlen() from kprintf (guest dies). The OOB walk itself is proven by construction: the delivered ID (console dump shows all 256 bytes, colon-free) contains no terminator and no colon, so every byte scanned past str[255] was uninitialized or out-of-bounds stack. The identical unchecked walk is the info-leak flavor whenever the crossed stack contains a colon (the bytes would be kprintf-ed to the console).
No comments yet.