β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-5627

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5627 Β· 13 files
FileTypeDescriptionSize
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.

VERDICT.md
↓ download raw

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, whose read_ivar always writes PCP_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 garbage epp it takes the default: 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

fixed
baseline reproduced→ patch + rebuild →patched clean

Applied 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
↓ fix.diffper-fix-DF-5627

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.