NULL pointer dereference in ppb_pnp_detect via crafted IEEE 1284 PnP string
Summary
ppb_pnp_detect at ppbconf.c:242/249/255/259/264/271 calls search_token(token,UNKNOWN_LENGTH,":") and does NULL+1 unconditionally without checking return value. search_token at ppbconf.c:177-206 returns NULL when the requested sub-token is not found within the scan window (line 205). With UNKNOWN_LENGTH=-1, search_token scans from token to next NUL (lines 185-188); if keyword token (MFG/MDL/VER/REV/CLS/CMD) found but no : appears before next NUL, loop at 197-203 exhausts without match and returns NULL. Caller computes NULL+1=(char*)0x1 and passes to kprintf("%s",...) which dereferences address 1 in kernel VA - unmapped, page fault, kernel panic. Six call sites: MFG/MANUFACTURER at 242, MDL/MODEL at 249, VER at 255, REV at 259, CLS at 264 (also feeds class to search_token(class,...) at 278 if survived), CMD/COMMAND at 271. Trigger: malicious IEEE 1284 peripheral sends PnP string with keyword but no : separator (e.g. MFG\0 or MFG; where ; replaced by \0 at line 237). Runs at every ppbus attach (boot or kldload) via ppbus_attach -> ppb_scan_bus -> ppb_pnp_detect at :378. DONTPROBE_1284 is NOT defined by default. Impact: reliable kernel panic at boot before multi-user mode.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1093 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df1093_harness.c | trigger-source | userspace harness with SIGSEGV handler proving NULL+1 deref | 3.4 KB | view raw |
| verify.sh | trigger-source | 7 static source-tree checks; counts 6 unguarded +1 sites | 1.5 KB | view raw |
| verify.log | run-log | verify.sh output (all 7 pass) | 436 B | view raw |
| run.log | run-log | harness output: SIGSEGV at addr 1 unpatched; safe skip patched | 325 B | view raw |
| fix.diff | suggested-fix | add NULL check at each of the 6 search_token(:) + 1 sites | 2.2 KB | view raw |
| build.sh | build-script | build both harness variants | 301 B | view raw |
| run.sh | run-script | verify.sh + both harness variants | 252 B | view raw |
| env.txt | environment | uname, cc, securelevel, HW presence | 526 B | view raw |
| README.md | readme | how to reproduce + bug shape + impact | 2.0 KB | β raw |
| VERDICT.md | verdict | full narrative: mechanism, harness, fix | 3.9 KB | β raw |
| fix_build.log | build-log | compile-validation: kernel+module build with fix applied, rc=0, no errors | 5.7 MB | β download |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1093 β NULL pointer dereference in ppb_pnp_detect via crafted IEEE 1284 PnP string
Build
cc -O0 -o df1093_harness df1093_harness.c cc -O0 -DFIX -o df1093_harness_fix df1093_harness.c
Run
./df1093_harness # SIGSEGV at addr 1 (kernel panic equivalent) ./df1093_harness_fix # safe return, no deref sh verify.sh # 7 static source checks
Expected (bug present)
verify.shreportsPASS=7 FAIL=0and counts 6 unguardedsearch_token(token, UNKNOWN_LENGTH, ":") + 1sites inppbconf.c.df1093_harnesstraps a SIGSEGV at address 1 withBUG: NULL+1 deref trapped (SIGSEGV at addr 1).df1093_harness_fixreturns normally withMFG keyword found but no ':' β skip safely.
Bug shape
ppb_pnp_detect at sys/bus/ppbus/ppbconf.c:213-288 parses an IEEE 1284
PnP ID string and prints the device info. For each of six keywords
(MFG/MANUFACTURER, MDL/MODEL, VER, REV, CLS, CMD/COMMAND)
the function does:
if ((token = search_token(str, len, "MFG")) != NULL || ...)
kprintf("ppbus%d: <%s", unit,
search_token(token, UNKNOWN_LENGTH, ":") + 1); /* :242 */
search_token (:177-206) returns NULL when the inner token (here
":") is not found in the scan window. When the malicious peripheral
supplies MFG\0 (keyword found, no : before the next NUL), the inner
search_token returns NULL. The unconditional + 1 then yields
(char *)0x1, which kprintf("%s", ...) dereferences β unmapped address
in kernel VA, page fault, kernel panic.
A malicious peripheral can craft any of the six keyword-only strings to
trigger the panic at boot or on kldload ppbus. DONTPROBE_1284 is not
defined by default.
Impact / preconditions
The trigger is physical (AV:P) β a malicious IEEE 1284 peripheral
plugged into a parallel port. The audit QEMU guest has no parallel port
HW (pciconf -l shows no ISA/parallel bridge; dmesg | grep -c pnp = 0),
so the path is not exercised at runtime here; the bug is confirmed by
source trace + harness.
DF-1093 β NULL pointer dereference in ppb_pnp_detect
Verdict
NOT REPRODUCED at runtime (hardware-gated) β STATIC VERIFICATION + HARNESS CONFIRMED.
The bug exists verbatim in sys/bus/ppbus/ppbconf.c:213-288. Six call
sites in ppb_pnp_detect compute search_token(token, UNKNOWN_LENGTH, ":") + 1
unconditionally without checking whether search_token returned
NULL. search_token (:177-206) returns NULL when the inner token is
not found in the scan window, which a malicious IEEE 1284 peripheral can
arrange by sending a PnP ID string with a keyword (MFG, MDL, VER,
REV, CLS, CMD etc.) but no : separator before the next NUL.
NULL + 1 = (char *)0x1 is then dereferenced by kprintf("%s", ...),
which on the kernel heap/stack-less low addresses page-faults β kernel
panic at every ppbus attach (boot or kldload ppbus).
The audit QEMU guest has no parallel-port hardware (pciconf -l
shows no ISA/parallel bridge; dmesg | grep -c pnp = 0), so the path is
not exercised at runtime here. The trigger requires attacker-controlled
IEEE 1284 hardware (AV:P/AC:L) β same hardware-gating class as the
DF-1071 PnP finding.
The df1093_harness userspace C program installs a SIGSEGV handler,
mirrors search_token and the MFG-handling block, then feeds it the
malicious string "MFG" (keyword, no :). The unpatched harness
traps the SIGSEGV at address 1 β the kernel equivalent of which is a
page-fault panic. The patched harness (NULL check before the + 1)
returns normally.
Mechanism (confirmed by source trace)
search_token (ppbconf.c:177-206):
static char *
search_token(char *str, int slen, char *token)
{
...
if (slen == UNKNOWN_LENGTH)
for (slen = 0, p = str; *p != '\0'; p++)
slen++; /* :187 scan to next NUL */
...
for (i = 0; i <= slen-tlen; i++) { /* :197 */
for (j = 0; j < tlen; j++)
if (str[i+j] != token[j])
break;
if (j == tlen)
return (&str[i]); /* :202 match */
}
return (NULL); /* :205 miss */
}
In ppb_pnp_detect the ; characters of the PnP string are first
replaced by \0 (:236-237), so each keyword lives in its own
NUL-terminated slice. A malicious peripheral supplies MFG\0 (no :),
and the inner search_token(token, UNKNOWN_LENGTH, ":") scans from
MFG to the next NUL (which is the \0 immediately after MFG),
finds no :, returns NULL. The six affected sites at :242, :249,
:255, :259, :264, :271 all do NULL + 1 and pass the result to
kprintf("%s", ...), which dereferences (char *)0x1 β kernel page
fault, panic.
This is a particularly reliable crash because (a) it happens at attach
time (boot or module load), not at first use; (b) it requires no
particular privilege β merely plugging the peripheral in is enough;
(c) DONTPROBE_1284 is NOT defined by default, so the probe runs
unconditionally when a ppbus is present.
Reproduction
$ sh verify.sh # 7/7 static checks; 6 unguarded +1 sites counted $ cc -O0 -o df1093_harness df1093_harness.c $ cc -O0 -DFIX -o df1093_harness_fix df1093_harness.c $ ./df1093_harness # SIGSEGV at addr 1 (kernel: panic at boot) $ ./df1093_harness_fix # safe skip
Fix
fix.diff adds an explicit if (val != NULL) guard at each of the six
sites. The pattern is mechanical:
char *val = search_token(token, UNKNOWN_LENGTH, ":");
if (val != NULL)
kprintf("...", val + 1);
For the MFG and MDL sites (where the empty-string fallback is harmless),
the ternary val != NULL ? val + 1 : "" is used. For the CLS site, the
NULL check additionally prevents the later search_token(class, len, ...)
call at :278 from receiving a garbage class pointer. The
nativekernel build of the patched file succeeds; the harness validates
the algorithm-level correctness.
Fix verification
fixedvalidated
kernel build rc=0 + harness before/after
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source+harness. ppb_pnp_detect search_token NULL+1 -> panic at attach. No parallel port HW.
No comments yet.