# DF-5156 VERDICT — NOT REPRODUCED (containment proven at runtime)

**Bottom line:** the missing `sc->rnum < MAX_ROMS` bound in `orm_identify()`
is real as written (orm.c:104-150 writes `sc->rid[sc->rnum]`/`sc->res[sc->rnum]`
at orm.c:146-147 with no bound), but the overflow **cannot be reached on any
supported DragonFly platform**: the only ISA bus implementation
(sys/bus/isa/x86_64/isa.c, platforms pc64+vkernel64) silently refuses to create
SYS_RES_MEMORY resource-list entries with rid ≥ ISA_NMEM(8), which is below
MAX_ROMS(16). The PoC injected 48 valid option-ROM signatures into the ISA hole
via QEMU `-option-rom`; the driver recorded exactly 8 regions and the guest
stayed healthy across two independent boots.

## Reproduction narrative (attempts)

1. **121,856-byte ROM** — SeaBIOS did not copy it (no space left in the option
   ROM area after the VGA BIOS/kvmvapic; nothing appeared in the hole).
2. **97,280-byte ROM, no entry stub** — SeaBIOS *enters ISA ROMs at seg:0003*
   (the init vector at offset 3 is ignored on this path). The zero body was
   executed, IP walked off the end of the ROM and the machine hung pre-boot
   (empty serial log; SeaBIOS debug stream ends right after
   "Running option rom at cb00:0003"). Fixed by placing `0xCB` (RETF) at
   offset 3 of every 2KB block — see `seabios-attempts.txt`.
3. **97,280-byte ROM with RETF stubs (the final PoC)** — boots cleanly:

       orm0: <ISA Option ROMs> at iomem 0xcb800-0xcbfff,0xcc000-0xcc7ff,
       0xcc800-0xccfff,0xcd000-0xcd7ff,0xcd800-0xcdfff,0xce000-0xce7ff,
       0xce800-0xcefff,0xcf000-0xcf7ff on isa1

   * `devinfo -r` shows the same 8 memory ranges (devinfo does NOT truncate;
     the dmesg printer caps at ISA_NMEM=8 too — isa_common.c:575-577 — but
     devinfo reads the resource list directly, so 8 is the true count).
   * /dev/mem dump proves the signatures were present and unscanned past the
     8th: `55 aa 04 cb` at 0xcb800 (recorded), **0xcf800** (9th, NOT
     recorded), **0xe2800** (47th, NOT recorded).
   * Repeat boot: identical result.

## Why rnum cannot exceed 8 (line-by-line)

* orm.c:105 `bus_set_resource(child, SYS_RES_MEMORY, sc->rnum=8, …)` →
  BUS_SET_RESOURCE(isa) → `isa_set_resource()` isa_common.c:897-899:
  `if (type == SYS_RES_MEMORY && rid >= ISA_NMEM) return EINVAL;`
  → **no resource-list entry is created** (and orm ignores the return value).
* orm.c:108/140 `bus_alloc_resource_any(child, SYS_RES_MEMORY, &rid=8, …)` →
  `isa_alloc_resource()` x86_64/isa.c:91 (isdefault=1 skips the rid-cap
  branch) → `resource_list_alloc()` subr_bus.c:2693-2695:
  `rle = resource_list_find(rl, type, *rid); if (!rle) return 0;` → NULL.
* orm.c:141-144: res==NULL → delete (no-op) → `chunk += IOMEM_STEP` → the
  2KB region is silently skipped; rnum stays 8. Every later signature fails
  identically (rid is pinned at 8).

## Secondary observations (killed / downgraded)

* **Zero-size header (`55 AA 00`)** — rom_size=0 would make `chunk += 0`
  (orm.c:149) and, if a 0-length alloc succeeded, loop forever while writing
  OOB. Killed: `resource_list_alloc()` clamps `count = ulmax(count,
  rle->count)` = max(1,0) = 1 (subr_bus.c:2690-2691), so a 1-byte region is
  allocated once and the next 2KB probe at the same chunk fails (overlap) →
  the scan advances. No infinite loop.
* **Resource range overreach** — a signature near the top of the hole (e.g.
  55 AA FC at 0xff800) reserves [0xff800, 0x11d7ff), past IOMEM_END into
  physical RAM above 1MB. Harmless resource overreach at boot (no OOB access);
  folded into the finding as a hardening note.
* No copyin/uiomove, no privilege surface, no runtime entry point (identify
  runs once at boot; rescans no-op via the DS_ATTACHED check at orm.c:92).

## Impact

None demonstrated and none reachable on supported platforms. The finding is
filed as **Info (defense-in-depth)**: the missing bound is a latent kernel
heap overflow that becomes live the moment ISA_NMEM is raised above MAX_ROMS
(two constants in different files, no compile-time or runtime tie between
them). fix.diff adds the explicit bound.

## Fix validation

Not performed — memory corruption did not reproduce (containment proven), so
the mandatory rebuild-and-test cycle does not apply. fix.diff is a
defense-in-depth one-liner authored against the read-only sys/ tree.

## 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.
