agp_amd64_via_init reads AGP3_VIA_ATTBASE as the RMW source for the AGP3_VIA_GARTCTRL write (register confusion, upstream-inherited)
Summary
The GARTCTRL write's preserve value is read from the adjacent ATTBASE register - the GATT physical address clobbers GARTCTRL's size/queue/enable bits. Every analogous RMW in the driver reads the register it writes; transcription slip inherited byte-identical from FreeBSD (verified 9.0 and stable/12). Boot-time config-space programming only; no memory safety. One-token fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4856 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 861 B | β raw | |
| VERDICT.md | β | 3.1 KB | β raw | |
| fix.diff | β | 503 B | view raw | |
| upstream_check.txt | β | 744 B | view raw | |
| manifest.json | β | 825 B | view raw | |
| verdict.json | β | 2.3 KB | view raw |
DF-4856 β agp_amd64_via_init programs GARTCTRL from the ATTBASE value
agp_amd64_via_init() writes the GATT base into AGP3_VIA_ATTBASE and then
writes AGP3_VIA_GARTCTRL β but reads the source value from
AGP3_VIA_ATTBASE (the GATT physical address just written), not from
GARTCTRL itself:
Result: GARTCTRL is programmed to gatt->ag_physical | 0x180, clobbering
whatever size/queue/translate bits the register held, instead of the intended
gartctrl | 0x180 (set bit 0x180 = GART enable + TLB-entry-size field on the
VIA secondary AGP3 bridge at 0:1:0).
No runtime PoC exists for a config-register programming bug on hardware the audit guest cannot present (needs a VIA K8T800-class host bridge with the 0:1:0 secondary AGP bridge). The evidence is the source itself plus the upstream comparison below.
DF-4856 verdict β certain in source, hardware-gated at runtime
Root cause
sys/dev/agp/agp_amd64.c:454-463 (agp_amd64_via_init):
459: agp_amd64_apbase_fixup(dev);
460: pci_cfgregwrite(0, 1, 0, AGP3_VIA_ATTBASE, sc->gatt->ag_physical, 4);
461: pci_cfgregwrite(0, 1, 0, AGP3_VIA_GARTCTRL,
462: pci_cfgregread(0, 1, 0, AGP3_VIA_ATTBASE, 4) | 0x180, 4);
Line 462 reads AGP3_VIA_ATTBASE (0x98) as the preserve-mask source for the AGP3_VIA_GARTCTRL (0x90) write. Every other GARTCTRL-style RMW in this driver family reads the register it writes (compare agp_amd64.c:233-237, :306-308, :362-364). The register addresses are adjacent defines (agpreg.h:98-100: GARTCTRL 0x90, APSIZE 0x94, ATTBASE 0x98) β a transcription slip.
Effect: the VIA secondary bridge's GARTCTRL is loaded with
sc->gatt->ag_physical | 0x180 β a 32-bit physical address of the GATT
(e.g. 0x3f000000-ish) OR 0x180 β instead of its prior configuration with
bits 0x180 set. GARTCTRL bits on VIA AGP3 control GART enable / queue /
size; an arbitrary address value can disable the secondary GART or program
nonsense sizes. On VIA K8T800/8385 boards where agp_amd64_via_match()
agp_amd64.c:130-142 accepts the host bridge, AGP GART translation through
the 0:1:0 bridge is misconfigured at attach. Boot-time hardware programming
only; no attacker-controlled input reaches these writes; no kernel memory
safety impact. Info severity.
Upstream provenance (inherited, not DF-introduced)
Verified byte-identical in FreeBSD:
- freebsd-src release/9.0.0 sys/dev/agp/agp_amd64.c:467-469
(
pci_cfgregread(0, 1, 0, AGP3_VIA_ATTBASE, 4) | 0x180) - freebsd-src stable/12 sys/dev/agp/agp_amd64.c:464-466 (same)
DF's copy (header: $FreeBSD: src/sys/dev/agp/agp_amd64.c,v 1.17 2009/01/12)
inherited the bug verbatim; upstream carried it until the file's removal.
Log as divergence-with-upstream-ownership per AGENT.md contrib rules β but
this is base sys/dev/agp code in DF's tree, so DF should fix it locally.
Why not_reproduced at runtime
Requires a VIA K8T800-class host bridge (ids 0x02041106/0x02381106/ 0x02821106/0x31881106, agp_amd64.c:104-111) plus the 0:1:0 secondary AGP bridge with AGPSEL bit clear. The QEMU audit guest presents a single i440FX host bridge (0x12378086); agp_amd64 cannot probe, so the register write never executes. There is no userspace-observable trigger for a config-space programming defect even on real hardware short of AGP not working β hence Info/likely (source-certain, impact reasoned not measured).
Fix
fix.diff (validated git apply --check): read GARTCTRL back from GARTCTRL:
pci_cfgregwrite(0, 1, 0, AGP3_VIA_GARTCTRL,
- pci_cfgregread(0, 1, 0, AGP3_VIA_ATTBASE, 4) | 0x180, 4);
+ pci_cfgregread(0, 1, 0, AGP3_VIA_GARTCTRL, 4) | 0x180, 4);
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_testableone-token register correction; git apply --check OK against the read-only tree; runtime validation hardware-gated
fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (root cause + upstream diff); upstream_check.txt (FreeBSD release/9.0.0 and stable/12 both carry the identical ATTBASE source read); fix.diff (git apply --check OK)
PoC changes
no PoC exists for a config-register programming defect; evidence pack is source + upstream comparison
Verified recommended fix
In agp_amd64_via_init, read the preserve value from AGP3_VIA_GARTCTRL, not AGP3_VIA_ATTBASE (see fix.diff)
Verdict
agp_amd64_via_init (agp_amd64.c:461-462) uses AGP3_VIA_ATTBASE (0x98) as the preserve source for the AGP3_VIA_GARTCTRL (0x90) read-modify-write, loading the VIA secondary AGP bridge's GARTCTRL with (gatt physical address | 0x180) and clobbering its size/queue/enable bits, instead of (gartctrl | 0x180). Every analogous RMW in the same file reads the register it writes. The bug is certain in source, inherited byte-identical from FreeBSD (verified release/9.0.0:467-469 and stable/12:464-466), and has no runtime trigger on the audit guest (needs a VIA K8T800-class host bridge + 0:1:0 secondary bridge; QEMU presents only i440FX 0x12378086, so agp_amd64 never probes). Impact ceiling: boot-time misprogramming of GART translation on ~2004 VIA K8 boards - no attacker-controlled input, no memory-safety consequence. Info.
No comments yet.