radeon_router_select_ddc_port/cd_port write an uninitialized kernel-stack byte into the DDC/CD router i2c mux registers when the router read fails (and leak it via DRM_DEBUG)
Summary
Both router-select helpers declare 'u8 val' uninitialized; radeon_i2c_get_byte writes *val only on success, but on NAK (broken mux, KVM/dock quirks) the callers unconditionally mask and write the garbage byte into router config registers 0x3/0x1 and print it to the debug log. Inherited verbatim from upstream - upstreamable fix. val=0 init (or a real return value from get_byte).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4445 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.7 KB | β raw | |
| VERDICT.md | β | 3.1 KB | β raw | |
| df4445_probe.c | β | 809 B | view raw | |
| fix.diff | β | 750 B | view raw | |
| verdict.json | β | 2.0 KB | view raw | |
| manifest.json | β | 626 B | view raw |
DF-4445 evidence pack
- What:
radeon_router_select_ddc_port()/radeon_router_select_cd_port()useu8 val;uninitialized (sys/dev/drm/radeon/radeon_i2c.c:1108-1133,:1136-1161).radeon_i2c_get_byte()only writes*valwhen the i2c transfer succeeds (radeon_i2c.c:1077-1083); on failure the caller'svalkeeps its garbage stack contents and is then unconditionally masked/OR-ed and written back to the router's i2c config registers (0x3 / 0x1) β garbage in every bit outsideddc_mux_control_pin. The failure branch ofradeon_i2c_get_byte()alsoDRM_DEBUG-prints the uninitialized byte. Upstream Linux has the same defect (inherited). - Severity: Low (needs a router-equipped connector + failing router read; consequence is wrong mux configuration / display misrouting and a debug-log stack-byte leak). Verification skipped per audit contract for Low findings; no radeon hardware on the audit guest anyway (virtio-gpu only).
- Fix: initialize
u8 val = 0;in both functions (or bail out whenradeon_i2c_get_bytefailed β it would need a return value for that; minimal safe fix is the zero-init).
Reproduce (on radeon hardware)
cc -O2 -o /tmp/df4445 df4445_probe.c && /tmp/df4445 # optional helper
The defect itself is visible only with drm.debug=0x02 while a DDC
probe runs on a connector whose router read NAKs (broken/unplugged
mux chip): dmesg shows i2c 0x.. 0x<garbage> read failed with a
changing garbage byte derived from prior stack contents.
Files
VERDICT.mdβ static line-cited analysisdf4445_probe.cβ optional compiled-but-inert demonstration stubfix.diffβ two-line zero-init fixverdict.json(statusuntested),manifest.json
DF-4445 β uninitialized val written into DDC/CD router mux registers when the router i2c read fails
Verdict
Status: untested (Low severity β verification skipped per audit contract; additionally no radeon hardware exists on the audit guest). Static analysis: certain.
Line-cited trace
sys/dev/drm/radeon/radeon_i2c.c:1110βu8 val;uninitialized (same inradeon_router_select_cd_portat:1138).radeon_i2c_get_byte(router_bus, i2c_addr, 0x3, &val)(radeon_i2c.c:1118-1120): its only write to*valis insideif (i2c_transfer(...) == 2)(radeon_i2c.c:1077-1079). On transfer failure β NAK from a missing/broken router chip βvalkeeps the previous stack contents. The failure branch also leaks that byte to the log:DRM_DEBUG("i2c 0x%02x 0x%02x read failed\n", addr, *val)(radeon_i2c.c:1081-1083).radeon_i2c.c:1121βval &= ~ddc_mux_control_pin;β garbage survives in every bit not owned bycontrol_pin.radeon_i2c.c:1122-1124β the garbage-derived byte is written back to router register 0x3; the same pattern repeats for register 0x1 at:1125-1132(val |= ddc_mux_stateonly constrains the mux bits).
Consequences: unpredictable corruption of the external i2c router's config
registers (other bits of 0x3/0x1 control unrelated mux functions on those
chips β display routing malfunctions), plus a one-byte kernel-stack disclosure
into the debug log when drm.debug is enabled (log readable per sysctl
policy).
Reachability: radeon_ddc_probe() calls radeon_router_select_ddc_port()
(radeon_i2c.c:63-64) on every connector probe of a router-equipped
connector (HPD poll / modeset). Trigger requires the router read to fail β
i.e. a router chip that NAKs (common with KVM tricks/DP-mST docks/hardware
faults) β while the subsequent put_byte of garbage goes out on the same
dead bus (no effect on DF's stubbed bit-bang shim; real effect on
working-hardware configurations, incl. upstream Linux which has the identical
code).
Divergence note: this is not DF-specific code β verified byte-for-byte
against upstream's radeon_i2c.c (the DF file's only functional divergences
are the lockinit/i2c_add_adapter shim lines at :932 and :970-973).
Filed because it is a real, fixable defect in the audited file
(CWE-457, upstreamable).
Fix
u8 val = 0; in both functions (fix.diff). The stronger fix β make
radeon_router_select_* bail when the read fails β needs an API change to
radeon_i2c_get_byte (return int) and can be proposed upstream separately.
Why not runtime-verified
Low severity (contract: skip); no radeon hardware on the audit guest (virtio-gpu 0x1234:1111 only β see DF-4444/env.txt for the PCI inventory).
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_testablenot_testable (Low-severity static finding; no radeon hw on guest)
fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md line-cited trace; fix.diff (zero-init)
PoC changes
n/a (static finding; helper stub only)
Verified recommended fix
Initialize u8 val = 0 in radeon_router_select_ddc_port/radeon_router_select_cd_port
Verdict
Static trace is complete and certain (see VERDICT.md): radeon_i2c.c:1110/:1138 declare 'u8 val' uninitialized; radeon_i2c_get_byte() only writes it on i2c success (radeon_i2c.c:1077-1083) but the callers unconditionally mask/OR and write the byte back into router config registers 0x3/0x1 (radeon_i2c.c:1121-1132, 1149-1160) and the failure branch DRM_DEBUG-prints the uninitialized byte. Inherited verbatim from upstream Linux (not DF-specific code). Impact ceiling: wrong external-router configuration bits (display misrouting) + one-byte kernel-stack disclosure into debug logs; Low. Verification skipped per audit contract for Low findings; no radeon hardware exists on the audit guest regardless.
No comments yet.