DF-2315 / run.log
=== DF-2315 BASELINE RUN (userland `sh run.sh`, root, PNPH harness kernel #5
DragonFly 6.5-DEVELOPMENT #5: Sun Oct 18 15:58:28 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/PNPH)
=== kernel console (dmesg) lines of the same boot ===
PNPHARNESS-READY: pnp.c verbatim (TU-compiled) isa0=0xfffff8008bb622a0 /dev/pnph0
PNPH layout: sizeof(isa_config)=960 off ic_mem=0 ic_port=128 ic_irqmask=928 ic_drqmask=936 ic_nmem=944 ic_nport=948
PNPH layout: ic_port[50] at +928 (ic_irqmask[0]) ic_port[51] at +944 (ic_nmem)
-> struct proof: ic_port[50] (16 bytes: ir_start,ir_end,ir_size,ir_align)
lands EXACTLY on ic_irqmask[0],ic_irqmask[1],ic_drqmask[0],ic_drqmask[1]
and ic_port[51] lands EXACTLY on ic_nmem,ic_nport,ic_nirq,ic_ndrq.
--- scenario 4: direct REAL pnp_check_quirks(vendor=0xc1008c0e SB64,
logical=0x22008c0e, ldn=0, cfg->ic_nport=49), canaries in masks/counters ---
== scenario 4 (DF-2315) rc=0
nport: before=49 after=0x33(51)
irqmask before/after: {0xcafe0001,0xcafe0002} -> {0xa20,0xa2f}
drqmask before/after: {0xcafe0003,0xcafe0004} -> {0x10,0x10}
counters(nmem,nport,nirq,ndrq) before: {0xc0ffee,0x31,0xc0fff0,0xc0fff1}
counters(nmem,nport,nirq,ndrq) after : {0xc0ffee,0x33,0xc0fff0,0xc0fff1}
PNPH 2315 direct before: nport=49 irq={0xcafe0001,0xcafe0002} drq={0xcafe0003,0xcafe0004} cnt={0xc0ffee,0x31,0xc0fff0,0xc0fff1}
PNPH 2315 direct after : nport=51 irq={0xa20,0xa2f} drq={0x10,0x10} cnt={0xc0ffee,0x33,0xc0fff0,0xc0fff1}
-> arg1 (0x400) wrote ic_port[49] in bounds (nport 49->50);
arg2 (0x800) wrote ic_port[50] = port0+0x800 = {0xa20,0xa2f,0x10,0x10}
-> ALIASED INTO ic_irqmask[0]=0xa20, ic_irqmask[1]=0xa2f,
ic_drqmask[0]=0x10, ic_drqmask[1]=0x10. Canary values overwritten.
--- scenario 5: direct, ic_nport=50 (both quirk writes OOB) ---
== scenario 5 (DF-2315) rc=0
nport: before=50 after=0xa2f(2607)
irqmask before/after: {0xcafe0001,0xcafe0002} -> {0x620,0x62f}
drqmask before/after: {0xcafe0003,0xcafe0004} -> {0x10,0x10}
counters(nmem,nport,nirq,ndrq) before: {0xc0ffee,0x32,0xc0fff0,0xc0fff1}
counters(nmem,nport,nirq,ndrq) after : {0xa20,0xa2f,0x10,0x10}
-> arg1 wrote ic_port[50] -> masks = port0+0x400 = {0x620,0x62f,0x10,0x10};
arg2 wrote ic_port[51] -> COUNTERS CLOBBERED:
ic_nmem=0xa20, ic_nport=0xa2f (2607!), ic_nirq=0x10, ic_ndrq=0x10.
A config claiming 2607 IO ports in a 50-entry array.
--- scenario 6: REAL stock pnp_parse_resources, 17 IO_RANGE tags (SB64 ids) ---
== scenario 6 (DF-2315) rc=0
PNPH 2315 parse(17 tags): stored ice_config: nport=0x33 irq={0xa20,0xa2f} drq={0x10,0x10} cnt={0,0x33,0,0}
-> the corrupted config ESCAPED into the device (isa_config_entry stored on
the child via the real ISA_ADD_CONFIG): the device now believes its IRQ
masks are 0xa20/0xa2f and DMA masks 0x10/0x10 - port-range-derived
nonsense that pnp_set_config/isa allocation will program.
(each IO tag: parser +1 port, then quirk +2 => +3/tag; tag 17: parser
port[48], quirk port[49] (in bounds) then port[50] -> masks.)
--- scenario 7: 18 IO tags ---
== scenario 7 (DF-2315) rc=0
PNPH 2315 parse(18 tags): stored ice_config: nport=0x36 irq={0xa20,0xa2f} drq={0x10,0x10} cnt={0x220,0x36,0x10,0x10}
-> tag 18: parser wrote ic_port[51] -> ic_nmem=0x220, ic_nirq/ic_ndrq=0x10
(counter clobber on the real path); subsequent quirk writes re-read the
wild ic_nport and land at ic_port[52]/[53] = offsets 960/976 - i.e. in
configs[1] (the kmalloc is 9 x 960 = 8640 bytes for 9 alternatives).
--- scenario 8: 200 IO tags ---
== scenario 8 (DF-2315) rc=0
PNPH 2315 parse(200 tags): stored ice_config: nport=0x258(600) irq={0xa20,0xa2f} drq={0x10,0x10} cnt={0x220,0x258,0x10,0x10}
PNPH 2315 parse(200 tags): configs alloc=8640 bytes; last quirk write at ic_port[599]=configs+9712 (escape past alloc: YES)
-> with enough tags the unbounded index writes ESCAPE the 8640-byte
kmalloc block entirely (offsets beyond 8640 = adjacent kernel heap),
with card-controlled ir_start (16-bit card base) / ir_size / ir_align
bytes. Silent on this guest (16KB malloc size class absorbs the slack),
but it is a linear, attacker-paced heap overwrite.