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

DF i2c bit-bang shim is a stub: every radeon bit-banged DDC/EDID transfer is a silent no-op and the whole bit-bang block of radeon_i2c.c is dead code on DragonFly (display detection dead on default config)

Summary

The bit-bus branch calls i2c_bit_add_bus, which installs a stub algorithm whose master_xfer is '/* XXX Linux really does try to transfer some data here */ return 0' and never invokes the algo_bit callbacks - so radeon's pre/post_xfer/set/get clock/data are dead code, and with default radeon_hw_i2c=0 every DDC bus takes the bit branch: ddc_probe always false, EDID never readable. i915 unaffected (real GMBUS kept). Availability-of-function port defect, no memory-safety primitive; also masks DF-4445's hardware effect. Port-the-algo fix. Pack also records the latent register-then-free at :970-977 (dead today).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4446 Β· 4 files
FileTypeDescriptionSize
README.md β€” 1.5 KB ↓ raw
VERDICT.md β€” 3.1 KB ↓ raw
verdict.json β€” 1.9 KB view raw
manifest.json β€” 567 B view raw

DF-4446 evidence pack

  • What: On DragonFly the Linux-compat i2c shim replaces the bit-bang algorithm with a stub: i2c_bit_add_bus() (sys/dev/drm/linux_i2c.c:127-134) installs i2c_bit_algo whose master_xfer is bit_xfer() β€” "XXX Linux really does try to transfer some data here" β€” return 0; (linux_i2c.c:106-111), never calling algo_data callbacks. In radeon_i2c_create()'s bit-bus branch (sys/dev/drm/radeon/radeon_i2c.c:955-978) this makes pre_xfer/post_xfer/get_clock/get_data/set_clock/set_data (radeon_i2c.c:90-232) dead code, and every DDC/EDID transaction on a bit-banged bus "succeeds" with 0 messages transferred β€” so with the default radeon_hw_i2c=0 (all non-mm_i2c DDC buses take the bit branch), radeon_ddc_probe() always fails (ret != 2, radeon_i2c.c:72-74) and drm_do_probe_ddc_edid() never obtains an EDID: display detection is silently dead for pre-DCE3 radeons (and any bit-path bus) on DragonFly.
  • Severity: Info (functional availability defect of the port; no memory safety β€” no attacker primitive). Verification skipped per contract (Info), and the audit guest has no radeon hardware anyway.
  • Fix direction: implement the bit-bang protocol in sys/dev/drm/linux_i2c.c using i2c_algo_bit_data callbacks (port Linux's i2c-algo-bit.c bit_xfer), or route the DF radeon bit buses through the real hardware GPIO engine.

Files

  • VERDICT.md β€” static line-cited analysis
  • verdict.json (status untested), manifest.json
VERDICT.md
↓ download raw

DF-4446 β€” DF i2c bit-bang shim is a stub: all radeon bit-bang DDC/EDID transfers are silent no-ops; the entire bit-bang block of radeon_i2c.c is dead code on DragonFly

Verdict

Status: untested (Info severity β€” skipped per contract; also hardware-gated). Static analysis: certain.

Line-cited trace

  1. radeon_i2c_create() bit-bus branch (sys/dev/drm/radeon/radeon_i2c.c:955-978): sets i2c->adapter.algo_data = &i2c->bit; and the six callbacks (pre_xfer/post_xfer/set_clock/set_data/get_clock/get_data), then calls i2c_bit_add_bus(&i2c->adapter).
  2. DF's shim i2c_bit_add_bus() (sys/dev/drm/linux_i2c.c:127-134) does adapter->algo = &i2c_bit_algo; adapter->retries = 2; return 0; β€” overwriting nothing that matters, because i2c_bit_algo.master_xfer is bit_xfer() (linux_i2c.c:106-111): /* XXX Linux really does try to transfer some data here */ return 0; β€” it never looks at algo_data, so none of radeon's GPIO bit-bang callbacks (radeon_i2c.c:90-232) ever execute.
  3. With the module default radeon_hw_i2c == 0, the selection logic at radeon_i2c.c:933-955 routes every non-mm_i2c bus (i.e. every DDC bus) to this bit branch; only radeon_hw_i2c=1 (explicitly discouraged by radeon_i2c_init()'s own warning at :999-1000) selects the real r100/r500/atom hardware engines.
  4. Effect: i2c_transfer() (linux_i2c.c:91-104) calls bit_xfer β†’ 0; callers require ret == num: radeon_ddc_probe() (radeon_i2c.c:66-74) always returns false; drm_do_probe_ddc_edid() (sys/dev/drm/drm_edid.c:1513-1558) always fails β†’ no EDID on any bit-path bus β†’ connector reports disconnected. (Contrast i915: intel_i2c.c keeps the real GMBUS algorithm and only uses the stub as a rare fallback, so i915 DDC works.)

Also noted (no independent finding): the DF-only i2c_add_adapter(&i2c->adapter) call placed between i2c_bit_add_bus() and its error check (radeon_i2c.c:970-973) is a latent UAF pattern (adapter registered then chan freed on bit_add_bus failure), but on the current shim both functions unconditionally return 0, so the path is unreachable β€” recorded here for when the shim grows real error returns.

Impact

Functional: display hotplug/EDID detection dead for bit-banged buses (pre-DCE3 radeon, and any bus on the bit path) on DragonFly. No memory safety, no attacker primitive β†’ Info.

Fix direction

Port Linux's drivers/i2c/algos/i2c-algo-bit.c bit_xfer() into sys/dev/drm/linux_i2c.c (it only needs the i2c_algo_bit_data callbacks already declared in sys/dev/drm/include/linux/i2c-algo-bit.h), or bypass the shim for radeon buses. i915 is unaffected either way.

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.

(No fix.diff for this item: documented recommendation only - see Fix above.)

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

not_testable (Info functional finding; no hw)

VERDICT.md
per-fix-DF-4446

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md (line-cited: shim stub vs radeon bit-bus branch); README.md

PoC changes

n/a (static finding)

Verified recommended fix

Implement real bit_xfer in sys/dev/drm/linux_i2c.c using the i2c_algo_bit_data callbacks (port Linux i2c-algo-bit.c)

Verdict

Static proof certain: DF's i2c_bit_add_bus (sys/dev/drm/linux_i2c.c:127-134) installs a stub algorithm whose master_xfer returns 0 without touching hardware (linux_i2c.c:106-111), so radeon_i2c_create()'s bit-bus branch (radeon_i2c.c:955-978) leaves pre_xfer..set_data (radeon_i2c.c:90-232) as dead code and every DDC/EDID transfer on a bit path 'succeeds' with 0 messages β€” display detection silently dead for all default-config radeon DDC buses on DragonFly. Info severity (functional port defect, no memory safety, no attacker primitive); runtime verification skipped per contract and hardware-gated anyway (guest has virtio-gpu only).