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

ig4iic_pci_attach()'s bzero() wipes the sc->version stored by ig4iic_pci_probe(): every PCI ig4 controller (all 34 IDs incl. Skylake/Apollo Lake/Cannon Lake) is programmed as a Haswell β€” HSW-only SWMODE write to offset 0x808 on non-Haswell silicon, wrong LTR offsets, and the HSW/ATOM-only COMP_VER boot gate misapplied to newer generations

Summary

Probe stores the matched table row's version in the softc; attach's bzero erases it before the sole consumer, and IG4_HASWELL==0 decodes the wiped field as Haswell - the ACPI sibling proves the intended ordering (version set post-init). Every PCI-attached ig4 runs Haswell programming: the HSW-only SWMODE RMW lands on offset 0x808 (not the GENERAL register on SKL/APL/CNL LPSS whose private space is 0x200+ - an out-of-spec write), LTR read at wrong offsets, and the COMP_VER gate upstream restricts to HSW/ATOM so newer parts with low designware COMP_VER refuse to attach - the I2C controller and its touchpad-class children never come up. Production-code harness driving the REAL ig4iic_attach from stock ig4.ko against a fake MMIO file: 'WIPED to 0 == IG4_HASWELL' + write-map 808=00000004; re-resolve-in-attach fix A/B validated (0x808 stays 0). Boot-time functional/availability class.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5600 Β· 0 files
FileTypeDescriptionSize
empty

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

A/B demonstrated: run A (production ordering: probe stores version, attach bzero wipes it) shows the SWMODE write at 0x808 through the real ig4iic_attach; run B executes byte-for-byte the fixed ordering (version re-set after the bzero, as the fix and ig4_acpi.c do) through the same production function and shows 0x808=0 plus the correct ACTIVE/IDLE-LTR branch. The patched ig4.ko itself additionally compiles clean (-Werror) and loads (fix_build.log, fix_run.log); its changed ig4_pci_attach path is unreachable in the rig without PCI LPSS hardware, so the behavioral equivalence argument rests on run B, which replays the patched statement sequence exactly.

fix.diff; fix_run.log (run B: GENERAL(0x808)=00000000, 'preserved', correct CNL branch); fix_build.log (patched module builds and loads)
↓ fix.diffper-fix-DF-5600

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (baseline A/B differential: 'WIPED to 0 == IG4_HASWELL' + write-map 808=00000004 in run A vs 'preserved' + no 808 entry in run B; production prints 'general 00000000 (updated 00000004)' and 'version 4 activeltr idleltr'); df56.c (harness with cited production line replays); fix_run.log (fixed ordering through the same production ig4iic_attach: no SWMODE write; patched ig4.ko builds and loads); fix.diff (devid->version helper re-run in attach)

PoC changes

No seed PoC existed. Built from scratch: KLD harness (df56.c) driving the production ig4iic_attach()/ig4iic_detach() from the stock ig4.ko on a fake MMIO register file. Rig lessons learned: (1) harness devices must not be raw children of nexus0 (nexus_print_child NULL-ivars page fault) -- parent on a pseudo-bus hung off the ROOT bus whose root_print_child is a no-op; (2) -Werror forced dropping the redundant externs (ig4_var.h declares them), __DEVOLATILE for kfree of the volatile file, and DFly's 4-arg kthread_create(func, arg, tdp, fmt) signature.

Verified recommended fix

Re-resolve the version from the PCI devid in ig4iic_pci_attach after the bzero (shared devid->version helper used by probe and attach), mirroring ig4_acpi.c:104-108.

Verdict

REPRODUCED through production code on the QEMU guest. ig4iic_pci_probe() stores the controller generation in sc->version (ig4_pci.c:151); ig4iic_pci_attach() then executes bzero(sc, sizeof(*sc)) (ig4_pci.c:167) before ig4iic_attach(sc) (ig4_pci.c:193) consumes it, and IG4_HASWELL == 0 (ig4_var.h:49-55), so every PCI-attached ig4 controller runs as a Haswell regardless of which ig4iic_pci_devices[] row matched. The ACPI sibling is correct (ig4_acpi.c:104-108 sets sc->version in attach, after init, no bzero). Harness (df56.ko) replays the exact production statement ordering for devid 0x9dc58086 (Cannon Lake-LP -> IG4_CANNONLAKE, ig4_pci.c:128) and calls the REAL ig4iic_attach() from the stock /boot/kernel/ig4.ko against a fake MMIO register file (x86_64 memory-space bus_space dereferences the handle directly; tag unused). Baseline run A: 'probe stored version=4; after attach prologue sc->version=0 -> WIPED to 0 == IG4_HASWELL'; the production driver prints its own readback 'general 00000000 (updated 00000004)' -- the Haswell-only SWMODE write (ig4_iic.c:513-518) executed on non-Haswell silicon at offset 0x808, which is NOT the GENERAL register on Skylake/ApolloLake/CannonLake LPSS (their private space is 0x200+, ig4_reg.h:115-125); fake-file write map shows 808=00000004. Run B (control: version restored after the bzero, the fix ordering): 'version 4 activeltr idleltr' (correct CNL branch, ig4_iic.c:527-531), 0x808 stays 0. Also mis-exercised per run A: SW/AUTO LTR reads at HSW offsets 0x810/0x814 (ig4_iic.c:522-526) and the COMP_VER gate (ig4_iic.c:534-541) applied to generations upstream restricts to HSW/ATOM -- a part whose COMP_VER (0xF8) reads below IG4_COMP_VER 0x3131352A (ig4_reg.h:549) is refused at boot: the I2C controller and its smbus children never attach (touchpad/touchscreen-class devices dead). Impact: boot/module-load-time hardware misprogramming + potential controller refusal on supported PCI IDs; no unprivileged trigger, no memory corruption -- Low.