ppb_get_epp_protocol() discards the BUS_READ_IVAR error and returns an uninitialized uintptr_t β stale kernel-stack bytes when the parent fails/omits the ivar (feeds mode2xfer's panic or wrong-protocol selection)
Summary
Uninitialized local + discarded ivar error: verified against production code with stack-dirt patterns (0x41414141/0x42/0x43 returned verbatim, deterministic across 4 loads). Latent in-tree (ppc always writes the ivar) but a failing/third-party controller turns the exported query into kernel-stack content consumed by a switch whose default panics (the DF-5576 sink). One-line initializer fix validated on rebuilt production source (patterns -> 0). The iicbus DF-5559 accessor-class twin.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5627 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.9 KB | β raw | |
| VERDICT.md | β | 3.1 KB | β raw | |
| harness.c | β | 15.2 KB | view raw | |
| Makefile | β | 273 B | β download | |
| build.sh | β | 143 B | view raw | |
| run.sh | β | 129 B | view raw | |
| build.log | β | 7.9 KB | view raw | |
| run.log | β | 2.8 KB | view raw | |
| run2.log | β | 5.4 KB | view raw | |
| fix_run.log | β | 445 B | view raw | |
| fix_build.log | β | 7.9 KB | view raw | |
| env.txt | β | 411 B | view raw | |
| fix.diff | β | 459 B | view raw |
DF-5627 β ppb_get_epp_protocol(): BUS_READ_IVAR failure ignored, uninitialized protocol returned (stale kernel stack)
File: sys/bus/ppbus/ppb_base.c:100-108
Build (inside the DragonFly guest)
sh build.sh # compiles VERBATIM /usr/src/sys/bus/ppbus/ppb_base.c into ppbbasehx.ko
Run (inside the guest)
sh run.sh
Expected (decisive lines, see run.log)
PPBHX: DF-5627 stack dirtied 0x41 -> protocol=0x41414141 PPBHX: DF-5627 stack dirtied 0x42 -> protocol=0x42424242 PPBHX: DF-5627 stack dirtied 0x43 -> protocol=0x43434343
ppb_get_epp_protocol() declares uintptr_t protocol; uninitialized,
discards the BUS_READ_IVAR(...) return value (ppb_base.c:105), and
returns protocol. Any parent that fails the ivar read without writing
*result β the generic behavior (e.g. ppc_read_ivar()'s own default
branch at ppc.c:2060, or any non-ppc controller, which by construction has
no PPC ivars) β makes the function return stale kernel-stack bytes. The
harness dirties the stack with 0x41/0x42/0x43 patterns immediately before
each production-code call; the return value reproduces those patterns
byte-for-byte.
In-tree today the only ppbus parent is ppc, which always writes
PCP_IVAR_EPP_PROTO (ppc.c:2053-2055), so the bug is latent in-tree; its
in-tree consumer is mode2xfer() (ppb_msq.c:85) whose default: branch
panics on any protocol other than {0,1} (the DF-5576 sink) β so garbage
either panics the kernel or silently selects the wrong EPP submicroseq.
Fix validation
fix_run.log (ppb_base.c patched with an initializer, rebuilt, re-run): PPBHX: DF-5627 stack dirtied 0x41 -> protocol=0x00000000 PPBHX: DF-5627 stack dirtied 0x42 -> protocol=0x00000000 PPBHX: DF-5627 stack dirtied 0x43 -> protocol=0x00000000
Guest /usr/src was restored to pristine afterwards (cmp-verified).
Harness note
One shared KLD harness demonstrates DF-5625/5626/5627/5628; this pack's
lines are the DF-5627 ones.
DF-5627 VERDICT β reproduced (Info, impact: none β latent uninitialized kernel-stack read)
Root cause
sys/bus/ppbus/ppb_base.c:100-108:
int
ppb_get_epp_protocol(device_t bus)
{
uintptr_t protocol; /* uninitialized */
BUS_READ_IVAR(device_get_parent(bus), bus, PPC_IVAR_EPP_PROTO,
&protocol); /* return value discarded */
return (protocol);
}
BUS_READ_IVAR is an error-returning method call; on failure the typical
implementation writes nothing through *result (see ppc_read_ivar()'s
own default: return (ENOENT); at sys/dev/misc/ppc/ppc.c:2060-2062, or
any bus without the ivar). Discarding the error and returning
protocol hands the caller stale stack contents.
Verification (production code)
Shared ppbbasehx KLD, guest's unmodified /usr/src/sys/bus/ppbus/
ppb_base.c. The fake controller's bus_read_ivar returns ENOENT
without writing (generic-controller behavior). Immediately before each
call a __noinline helper dirties 512 stack bytes with a uniform
pattern; ppb_get_epp_protocol's small frame then lands in the dirtied
region:
stack dirtied 0x41 -> protocol=0x41414141 stack dirtied 0x42 -> protocol=0x42424242 stack dirtied 0x43 -> protocol=0x43434343
The production function returned the dirtier's bytes verbatim,
deterministically, across 4 module loads. This is a CWE-457
use-of-uninitialized-variable in an EXPORT_SYMS interface.
Why latent / impact ceiling
- The only in-tree ppbus parent is
ppc, whoseread_ivaralways writesPCP_IVAR_EPP_PROTO(ppc.c:2053-2055) β the failure path needs a non-ppc/failed controller (third-party KLD controller, or future ivar-set changes). - In-tree consumer:
mode2xfer()(ppb_msq.c:85). With garbageeppit takes thedefault:branch βpanic("unknown EPP protocol")(the panic-as-error already filed as part of DF-5576) β or, if the garbage happens to be 0/1, silently selects the wrong EPP protocol/submicroseq. - The value itself never reaches userland, so this is not a direct disclosure; it is an uninitialized-read contract bug feeding a panic/logic sink. Info severity (Low under a third-party-controller threat model).
Fix validation β fixed
Applied uintptr_t protocol = 0; (fix.diff: = EPP_1_9, same value 0,
plus the comment) to the guest's /usr/src/sys/bus/ppbus/ppb_base.c,
rebuilt the same harness, re-ran:
stack dirtied 0x41 -> protocol=0x00000000 stack dirtied 0x42 -> protocol=0x00000000 stack dirtied 0x43 -> protocol=0x00000000
Baseline misbehavior gone; control (dirty patterns) unchanged; source
restored to pristine and cmp-verified (RESTORED-OK in session log).
Environment
DragonFly 6.5-DEVELOPMENT x86_64 (X86_64_GENERIC), cc 8.3, 6 vCPU QEMU guest, no parallel port.
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedApplied an initializer ('= 0', the same value as fix.diff's '= EPP_1_9') to the guest's /usr/src/sys/bus/ppbus/ppb_base.c, rebuilt the same harness, re-ran: all three stack-dirtier patterns returned protocol=0x00000000 instead of 0x41414141/0x42424242/0x43434343 β the uninitialized read is gone through production code. Source restored to pristine and cmp-verified.
fix_run.log (three 'protocol=0x00000000' lines); fix_build.log; VERDICT.md 'Fix validation' section
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log lines 'DF-5627 stack dirtied 0x41/0x42/0x43 -> protocol=0x41414141/0x42424242/0x43434343'; fix_run.log '-> protocol=0x00000000' x3 (patched production source); VERDICT.md
PoC changes
No seed PoC; harness written fresh. The stack-dirtying helper is __noinline with a volatile 512-byte array so the production callee's frame provably lands in the dirtied region.
Verified recommended fix
Initialize 'uintptr_t protocol = EPP_1_9;' (or propagate the BUS_READ_IVAR error to the caller) so a failed ivar read cannot return stale stack bytes.
Verdict
Reproduced via the KLD-harness-with-production-code evidence class: the unmodified /usr/src/sys/bus/ppbus/ppb_base.c compiled into ppbbasehx.ko. ppb_base.c:100-108 declares 'uintptr_t protocol;' uninitialized, discards the BUS_READ_IVAR return (a failing parent writes nothing through *result β cf. ppc.c:2060's own ENOENT default), and returns protocol. Against a controller whose read_ivar fails, the production function returned the caller's stack-dirtier bytes verbatim: 0x41414141 / 0x42424242 / 0x43434343 for 0x41/0x42/0x43 patterns, deterministic across 4 module loads. Latent in-tree: the only parent (ppc, ppc.c:2053-2055) always writes the ivar; the in-tree consumer mode2xfer (ppb_msq.c:85) would panic (DF-5576 sink) or silently pick the wrong EPP protocol on garbage. Info severity, CWE-457, no direct userland disclosure.
No comments yet.