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

ichsmb_pci_attach requests 16 I/O-port bytes first while ICH5+ SMBus BARs decode 32 (64 under QEMU's ICH9) β€” on BIOS-unprogrammed BARs pci_alloc_map under-claims the decode window, leaving the tail bytes hardware-decoded but rman-unowned

Summary

The 16-first request ordering asks for half (or a quarter) of the fixed hardware decode of every ICH5+ part in the driver's own ID table: on firmware-programmed BARs the request is silently upgraded to the rl entry's full count (measured live: 16 asked -> 64 allocated), but when the BIOS left the BAR unprogrammed pci_add_map records no rl entry and the small count goes to pci_alloc_map - BAR+0x10..+0x1f stay decoded-by-SMBus but owned-by-nobody, so a later IOPORT allocation landing there aliases into SMBus register space. Root/config-triggered only, no in-tree claimant. Largest-first ordering fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5599 Β· 7 files
FileTypeDescriptionSize
run.sh β€” 141 B view raw
run.log β€” 586 B view raw
env.txt β€” 570 B view raw
fix.diff β€” 550 B view raw
VERDICT.md β€” 2.8 KB ↓ raw
README.md β€” 1.1 KB ↓ raw
verdict.json β€” 3.5 KB view raw

DF-5599 β€” ichsmb IOPORT claim sizing vs ICH5+ BAR decode (Info, hardening)

File: sys/bus/smbus/ichsmb/ichsmb_pci.c (pass 2)

What this pack demonstrates

The driver requests 16 I/O-port bytes first (ichsmb_pci.c:233-234) but ICH5+ SMBus BARs decode 32 (64 under QEMU's ICH9 model β€” measured). On firmware-programmed BARs the newbus rl-count upgrade rescues the request (measured: 16 asked β†’ 64 allocated); on BIOS-unprogrammed BARs (pci.c:2598-2610 records no rl entry β†’ pci.c:4058-4061 pci_alloc_map honors count=16) the tail of the decode window stays hardware-decoded but rman-unowned.

Run (as root, inside the q35-rig guest with ichsmb+smb loaded)

sh run.sh

Expected output

bar   [20] = type I/O Port, range 32, base 0x700, size 64, enabled
...
            I/O ports:
                0x700-0x73f

(64 bytes allocated for a 16-byte request β€” the ulmax upgrade of subr_bus.c:2697-2700.)

Artifacts

run.sh, run.log, env.txt, fix.diff (32-first/16-fallback swap), VERDICT.md (both code paths), manifest.json, verdict.json.

VERDICT.md
↓ download raw

DF-5599 VERDICT β€” 16-first IOPORT claim under-claims ICH5+ SMBus BARs (Info)

Bottom line

not_reproduced / impact none / certain (mechanism measured, hazard conditional). ichsmb_pci_attach asks for 16 I/O-port bytes first (ichsmb_pci.c:233-234), falling back to 32 (:236-237). Every ID in the driver's table from ICH5 (0x24d3) onward is an ICH5+ part whose SMBus BAR decodes 32 bytes (Intel datasheet; QEMU's ICH9 model even decodes 64 β€” measured size 64 via pciconf -lb). Two paths:

  • Path A β€” firmware-programmed BAR (this rig): the PCI enumeration recorded a resource-list entry with the full BAR count (pci_add_map, pci.c:2551-2553 sizing + :2620 recording). The driver's default-window request (0, ~0, count=16) hits resource_list_alloc's upgrade count = ulmax(count, rle->count) (subr_bus.c:2697-2700), so the rman reserves the full decode (measured: requested 16 β†’ allocated 0x700-0x73f = 64 bytes). No under-claim. Live-measured in run.log.
  • Path B β€” BIOS-unprogrammed or BIOS-disabled BAR: pci_add_map deliberately records no rl entry when base == 0 or the BIOS wrote all-1s ("BIOS in an attempt to disable the resources", pci.c:2598-2610). pci_alloc_resource then routes the request to pci_alloc_map(..., count=16) (pci.c:4058-4061), which reserves only 16 bytes and programs the BAR base β€” while the hardware's fixed decode mask still covers 32 (or 64) bytes. The tail (BAR+0x10 .. BAR+0x1f) is then decoded by the controller but owned by nobody in the global I/O rman: any later 16-byte IOPORT allocation that lands there aliases into SMBus register space (both devices respond; reads merge, writes side-effect). Classic under-claim hazard; root/config-triggered, no in-tree claimant exists on the standard rig β€” hence Info.

Path B could not be exercised on this guest without changing firmware behavior (the rl entry is built at boot from the programmed BAR; SeaBIOS on q35 always programs it), so the finding stands on the code path plus the measured path-A upgrade.

Fix

fix.diff β€” swap the order: request 32 first, 16 as the fallback (largest documented decode first), so the pci_alloc_map path reserves the full window on ICH5+ while pre-ICH5 16-byte BARs still attach via the fallback. Info hardening; not kernel-rebuild-validated (fix_status= not_testable) since no bad behavior is reproducible on the standard rig.

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

not_testable
baseline no→ patch + rebuild →patched clean

No reproducible bad behavior on the standard rig (the programmed-BAR path is auto-upgraded and the unprogrammed path needs firmware changes to reach); fix.diff is a two-line order swap supplied for review.

fix.diff; run.log shows the measured upgrade that motivates keeping the request aligned with the rl count
↓ fix.diffper-fix-DF-5599

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (pciconf -lb: 'bar [20] = type I/O Port, range 32, base 0x700, size 64, enabled' vs devinfo -r allocation 0x700-0x73f = 64 bytes for a 16-byte request); VERDICT.md (both code paths cited); run.sh

PoC changes

Measurement-only pack derived from the same rig boot; no seed PoC existed. The initially drafted 'under-claim always happens' framing was corrected after live measurement showed the subr_bus.c:2699 ulmax upgrade rescues the programmed-BAR case -- the finding was narrowed to the pci_alloc_map path, which is the honest residual.

Verified recommended fix

In ichsmb_pci_attach(), request 32 I/O bytes first and fall back to 16, so the firmware-unprogrammed path (pci_alloc_map) reserves the full ICH5+ decode window instead of leaving half of it decoded-but-unowned.

Verdict

ichsmb_pci_attach() requests 16 I/O-port bytes first with a 32-byte fallback (ichsmb_pci.c:233-237) although every device ID in the table from ICH5 (0x24d3) onward has an SMBus BAR that decodes 32 bytes (64 under QEMU's ICH9 model, measured). On a firmware-programmed BAR the default-window request is silently upgraded to the resource-list entry's full count (subr_bus.c:2697-2700, count = ulmax(count, rle->count)) -- live-measured on the q35 rig: requested 16, rman allocated 0x700-0x73f (64 bytes), so no under-claim manifests there. The hazard lives on the BIOS-unprogrammed/disabled-BAR path: pci_add_map deliberately records no rl entry when base==0 or the BIOS wrote all-1s (pci.c:2598-2610), so pci_alloc_resource routes the request to pci_alloc_map with count=16 (pci.c:4058-4061) and reserves only 16 bytes while the fixed hardware decode still covers the full 32/64-byte window -- the tail bytes are then decoded-by-SMBus but owned-by-nobody in the global I/O rman, and any later allocation landing there aliases into SMBus register space. Root/config-triggered, no in-tree claimant on the standard rig, not exercisable without changing firmware behavior: Info hardening. fix.diff swaps the request order to 32-first/16-fallback so the pci_alloc_map path always reserves the full window on ICH5+.