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

ppc_pc873xx_detect() static probe index never reset: OOB reads past pc873xx_basetab[] used as x86 I/O port numbers (wild inb/outb)

Summary

'static int index = 0' is never reset, so the second and later calls resume past the 5-entry basetable; the reads pick up adjacent kernel .data (and eventually rnametab POINTER values) and each value is used as the idport for inb/outb - semi-arbitrary I/O port writes (0x60/0x64/0x70/0x71/0xcf9 in reach) with the index advancing further on every call. Gated on PPC_PROBE_CHIPSET (LINT64 only) and >=2 parallel ports. One-word fix (drop 'static').

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4138 Β· 3 files
FileTypeDescriptionSize
README.md file 2.3 KB ↓ raw
manifest.json file 366 B view raw
verdict.json file 1.1 KB view raw
README.md file
↓ download raw

DF-4138 β€” ppc_pc873xx_detect() static probe index is never reset:

OOB read past pc873xx_basetab[] used as an I/O port number

Root cause

sys/dev/misc/ppc/ppc.c:432-439:

static int
ppc_pc873xx_detect(struct ppc_data *ppc, int chipset_mode)
{
    static int  index = 0;          /* <-- static, never reset */
    ...
    while ((idport = pc873xx_basetab[index++])) {
    (void)inb(idport); ...
    outb(idport, PC873_SID);
    val = inb(idport + 1);

pc873xx_basetab[] (ppc.c:415) has 5 entries (4 ports + 0 sentinel). index is a function-static that is never re-initialized; every call to ppc_pc873xx_detect resumes where the previous one stopped.

  • 1st call (1st parallel port): walks the table from 0.
  • 2nd call (2nd port, or a re-probe): pc873xx_basetab[5], [6], ... β€” out-of-bounds reads of kernel .data past the table. The values read there (e.g. pc873xx_porttab[] = 0x378/0x3bc/0x278, then pc873xx_irqtab[], then the pc873xx_regstab[] contents and finally the pointer values of pc873xx_rnametab[]) are used as idport: inb(idport) / outb(idport, PC873_SID) / inb(idport + 1) are executed on arbitrary port numbers β€” including writes to semi-arbitrary x86 I/O ports (keyboard 0x60/0x64, POST/reset 0xCF9, CMOS 0x70/0x71 are all in reach depending on adjacent .data), and the loop keeps advancing on every call, reading further and further past the array.

Reachability

Gated on options PPC_PROBE_CHIPSET (ppc.c:406/1274/1295): not in GENERIC (X86_64_GENERIC does not compile ppc at all); enabled only in LINT64 or custom kernels, and needs >= 2 ppc attach attempts through the chipset detect loop (2+ ISA parallel ports, or repeated probes). The ppc driver itself is not in the shipped GENERIC kernel and the paravirtual guest has no ISA parallel port, so this is source-level certainty only.

Fix

--- a/sys/dev/misc/ppc/ppc.c
+++ b/sys/dev/misc/ppc/ppc.c
@@ -433,7 +433,7 @@
 static int
 ppc_pc873xx_detect(struct ppc_data *ppc, int chipset_mode)
 {
-    static int index = 0;
+    int        index = 0;
     int        idport, irq;
     int        ptr, pcr, val, i;

(the variable was pointlessly static; it must start at 0 per probe).

Verification

Skipped live (hardware-gated: guest has no parallel port; driver not in GENERIC; option not enabled). Analysis-certain: the missing reset is unambiguous from source.

Fix verification

not_testable
per-fix-DF-4138

Confirmed kernel references

Detail

Evidence (decisive lines)

README.md analysis (ppc.c:415,435,439-460)

PoC changes

n/a - analysis only

Verified recommended fix

Make the probe index a local (reset per call).

Verdict

ppc_pc873xx_detect uses a function-static index that is never reset (ppc.c:435); on the second and later invocations pc873xx_basetab[index++] reads past the 5-entry table and the garbage value is used as an x86 I/O port for inb/outb (including writes) - unbounded OOB reads of kernel .data turned into semi-arbitrary port I/O. Gated on options PPC_PROBE_CHIPSET (LINT64-only; ppc is not in GENERIC) and multiple probe invocations, so hardware/option-gated: source-level certainty, no live guest (no ISA parallel port).