acquire_i2c_hw_engine bounds the signed gpio_ddc_line index only above (line < GPIO_DDC_LINE_COUNT): GPIO_DDC_LINE_UNKNOWN(-1) passes and reads i2c_hw_engines[-1] β dce110 twin of DF-2089 (dce80)
Summary
Signed enum with UNKNOWN=-1 passes the upper-only check. Traced benign today only by a struct-layout accident ([-1] aliases an always-NULL sw_engines slot); fragile under layout changes. Crafted-VBIOS reachability on real DCE11 hw. Enum MIN/MAX lower-bound check fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5034 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.8 KB | β raw | |
| VERDICT.md | β | 3.4 KB | β raw | |
| analysis.sh | β | 847 B | view raw | |
| fix.diff | β | 458 B | view raw | |
| verdict.json | β | 2.8 KB | view raw | |
| manifest.json | β | 899 B | view raw |
DF-5034 β i2caux_dce110 acquire_i2c_hw_engine: missing lower-bound check on signed gpio_ddc_line allows negative-index array read (dce110 twin of DF-2089)
- File:
sys/dev/drm/amd/display/dc/i2caux/dce110/i2caux_dce110.c - Line: 98 (
if (line < GPIO_DDC_LINE_COUNT)) - Severity: Info (hardening). Confidence: certain (code shape), no demonstrated impact.
What the code does
acquire_i2c_hw_engine() derives the engine-slot index from the DDC object:
if (ddc->hw_info.hw_supported) {
enum gpio_ddc_line line = dal_ddc_get_line(ddc);
if (line < GPIO_DDC_LINE_COUNT) /* only UPPER bound */
engine = i2caux->i2c_hw_engines[line]; /* i2caux_dce110.c:98-99 */
}
enum gpio_ddc_line is SIGNED and its first member is
GPIO_DDC_LINE_UNKNOWN = (-1) (sys/dev/drm/amd/display/include/gpio_types.h:206-220).
The enum API explicitly ships GPIO_DDC_LINE_MIN/GPIO_DDC_LINE_MAX for
bounds checks, but this site (and its dce80 twin, DF-2089) only compares
against GPIO_DDC_LINE_COUNT. A -1 passes (-1 < 8) and reads
i2c_hw_engines[-1].
Why it is benign today (and why it is filed anyway)
struct i2caux layout (i2caux.h:55-98): ctx, funcs,
i2c_sw_engines[8], i2c_hw_engines[8], aux_engines[8], β¦
so i2c_hw_engines[-1] aliases i2c_sw_engines[7]. In the whole dce110
constructor family (dce100/dce110/dce112/dce120/dcn10 all funnel through
dal_i2caux_dce110_construct, i2caux_dce110.c:202-295) the SW-engine branch
is dead (use_i2c_sw_engine = false, i2caux_dce110.c:213) and only line
slots DDC1..DDC6 (indices 0..5) are ever written, so i2c_sw_engines[7]
stays NULL and the function returns NULL. No OOB past the struct occurs.
Reachability of line == -1 additionally requires a ddc whose
hw_info.hw_supported is true (a VBIOS-derived flag: i2c_hw_assist,
core/dc_link_ddc.c:270) while dal_ddc_get_line() β which returns
dal_gpio_get_enum(ddc->pin_data) (gpio/gpio_service.c:566-570), an enum
produced by the register-offsetβid translation (gpio_service.c:418) β
yields UNKNOWN. No such path was found on a well-formed ASIC/BIOS; a crafted
VBIOS on real AMD hardware is the only conceivable trigger, and it still
lands on NULL. Filed as the dce110 bookkeeping twin of DF-2089 (dce80,
identical shape, Info) so the family record is complete and the next
refactor does not have to re-derive this.
Fix (one line)
- if (line < GPIO_DDC_LINE_COUNT)
+ if (line >= GPIO_DDC_LINE_MIN && line < GPIO_DDC_LINE_COUNT)
engine = i2caux->i2c_hw_engines[line];
Reproduce / evidence
No PoC run: the finding is an Info hardening note gated on AMD display
hardware (DCE11 ASIC) that the QEMU test guest does not have
(guest has only a virtio GPU; no radeon/amdgpu KLD). Run
./analysis.sh to print the cited source lines. See VERDICT.md.
DF-5034 β VERDICT
status: untested (PoC intentionally not run β Info hardening finding, hardware-gated; no AMD ASIC in the test guest) reproduced: 0, impact: none, confidence: certain (about the code shape)
Narrative
Pass-2 audit of sys/dev/drm/amd/display/dc/i2caux/dce110/i2caux_dce110.c
found that acquire_i2c_hw_engine() validates the DDC-line index only
against the UPPER bound:
i2caux_dce110.c:95-100if (ddc->hw_info.hw_supported) { enum gpio_ddc_line line = dal_ddc_get_line(ddc); if (line < GPIO_DDC_LINE_COUNT) engine = i2caux->i2c_hw_engines[line]; }
enum gpio_ddc_line is signed with GPIO_DDC_LINE_UNKNOWN = (-1)
(include/gpio_types.h:207), so a -1 passes the check and indexes the array
one element low.
Why this is NOT a demonstrable memory-safety bug here (traced, not guessed):
i2c_hw_engines[-1]aliasesi2c_sw_engines[7]per the struct layout ini2caux/i2caux.h:67-71(sw array immediately precedes hw array).i2c_sw_engines[7]is never assigned in the dce110 constructor family:use_i2c_sw_engineis hardcoded false (i2caux_dce110.c:213), and even the dce80 variant only fills hw lines DDC1..DDC6 = indices 0..5 (i2caux_dce80.c:222-245, loop bounded byARRAY_SIZE(hw_ddc_lines)). The slot is therefore NULL and the function returns NULL β benign.- Reaching line == -1 at all needs a
hw_supportedddc (flag comes from VBIOSi2c_hw_assist,core/dc_link_ddc.c:270) whose gpio pin enum (dal_gpio_get_enumvia the offsetβid translation,gpio/gpio_service.c:418,566-570) evaluates to UNKNOWN. No in-tree creator does that; it would take a crafted VBIOS on real DCE11 hardware.
This is the dce110 twin of already-filed DF-2089 (dce80,
i2caux_dce80.c:122-127, Info). Filed for family completeness: the enum
provides GPIO_DDC_LINE_MIN (gpio_types.h:218) precisely for this check,
and the current "benign by struct-layout accident" property is fragile
under any future struct/layout or engine-population change.
Exploit chain
None. No attacker-controllable path to a non-NULL outcome was found; worst case remains a NULL deref-free early return.
Why untested (not not_reproduced)
The test guest (DragonFly 6.5-DEVELOPMENT, QEMU/KVM) has no AMD display
controller (pciconf shows only virtio GPU 1af4:1100) and no drm KLD
loaded; the DCE11 i2caux constructor/acquire path cannot execute there.
The finding is Info hardening; per the audit contract, Low/Info hardening
findings do not get a PoC phase. analysis.sh mechanically prints every
cited line for reviewers.
Fix validation
fix.diff adds the GPIO_DDC_LINE_MIN guard. Not built/booted (nothing to
observe without DCE11 hardware; the change is a pure index guard).
kernel_refs: - sys/dev/drm/amd/display/dc/i2caux/dce110/i2caux_dce110.c:92-100 - sys/dev/drm/amd/display/include/gpio_types.h:206-220 - sys/dev/drm/amd/display/dc/gpio/gpio_service.c:566-570 - sys/dev/drm/amd/display/dc/i2caux/i2caux.h:55-98 - twin: sys/dev/drm/amd/display/dc/i2caux/dce80/i2caux_dce80.c:122-127 (DF-2089)
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_testablen/a β finding not reproduced (no AMD ASIC in the test guest); fix is a pure index guard, reviewed against the enum definition only.
fix.diff (GPIO_DDC_LINE_MIN guard, mirrors the DF-2089 fix shape).
Confirmed kernel references
Detail
Evidence (decisive lines)
README.md and VERDICT.md (full trace); run ./analysis.sh to print every cited line: i2caux_dce110.c:92-112, gpio_types.h:206-220 (UNKNOWN=-1, GPIO_DDC_LINE_MIN exists at :218), i2caux.h:55-98 (array layout), gpio_service.c:566-570 (dal_ddc_get_line), dce80 twin i2caux_dce80.c:122-127.
PoC changes
No PoC written β finding is an index-guard hardening note; evidence is a mechanical source-print script (analysis.sh) instead.
Verified recommended fix
Add the enum-provided lower bound: if (line >= GPIO_DDC_LINE_MIN && line < GPIO_DDC_LINE_COUNT) β see fix.diff.
Verdict
acquire_i2c_hw_engine() bounds the DDC-line index only above (line < GPIO_DDC_LINE_COUNT, i2caux_dce110.c:98) although enum gpio_ddc_line is signed with GPIO_DDC_LINE_UNKNOWN = -1 (gpio_types.h:207); a -1 reads i2c_hw_engines[-1], which struct-layout-wise aliases i2c_sw_engines[7] (i2caux.h:67-71) β a slot that is never populated in the dce110 constructor family (use_i2c_sw_engine hardcoded false at i2caux_dce110.c:213; hw lines fill only indices 0..5), so the read is NULL and the function returns NULL. Benign today, fragile by accident, unreachable without a crafted VBIOS on real DCE11 hardware. Filed as the dce110 twin of DF-2089 (dce80). No PoC run: Info hardening finding, and the QEMU guest has no AMD display controller (virtio GPU only, no drm KLD).
No comments yet.