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

Missing lower-bound check on signed gpio_ddc_line index in acquire_i2c_hw_engine allows negative-index array read

Field Value
ID DF-2089
Status new
Severity Info
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:N
CWE CWE-129 Improper Validation of Array Index
File sys/dev/drm/amd/display/dc/i2caux/dce80/i2caux_dce80.c
Lines 122-126
Area drm/amd
Confidence speculative
Discovered 2026-07-25
Reported pending
Known CVE none
CVE match dfly_specific

Summary

acquire_i2c_hw_engine guards the per-line engine lookup only with if (line < GPIO_DDC_LINE_COUNT) (i2caux_dce80.c:124). enum gpio_ddc_line is a signed type (it defines GPIO_DDC_LINE_UNKNOWN = -1 at gpio_types.h:207), so a returned UNKNOWN value satisfies -1 < 8 and the subsequent i2caux->i2c_hw_engines[line] (line 126) becomes a negative-index OOB read. No demonstrable impact in the current dce80 configuration because the aliased adjacent slot resolves to NULL and is caught at line 130, but the bound is incorrect and fragile.

Root cause

  • i2caux_dce80.c:122 enum gpio_ddc_line line = dal_ddc_get_line(ddc); β€” dal_ddc_get_line (gpio_service.c:566-570) returns (enum gpio_ddc_line)dal_gpio_get_enum(ddc->pin_data) and gpio->en is a raw uint32_t (gpio_base.c:122-126, set from an unvalidated en parameter at gpio_base.c:252 and hw_gpio.c:187), so it is not guaranteed to be a valid in-range enum value.
  • i2caux_dce80.c:124 if (line < GPIO_DDC_LINE_COUNT) performs a SIGNED comparison (the enum type holds -1) and omits the lower bound line >= 0 / line != GPIO_DDC_LINE_UNKNOWN.
  • i2caux_dce80.c:126 engine = i2caux->i2c_hw_engines[line]; indexes with the unchecked (possibly -1) value.

The identical missing-lower-bound pattern is present in all siblings (dce110 i2caux_dce110.c:98) and in the shared helper get_hw_supported_ddc_line (i2caux.c:287), so this is systemic.

Threat model & preconditions

  • Attacker position: privileged local user (root) with the ability to load a crafted VBIOS connector table, or a developer triggering the path before GPIO assignment.
  • Privileges gained or impact: none demonstrated.
  • Required config or capabilities: a struct ddc whose hw_info.hw_supported is true yet whose pin_data->en maps to GPIO_DDC_LINE_UNKNOWN or another out-of-range value when cast through the enum.
  • Reachability: plausible from a malformed/misprogrammed VBIOS connector table or a DDC handle used before its GPIO line is assigned.

Proof of concept

No working exploit β€” impact is not demonstrated. Under the current dce80 struct layout (i2caux.h:67-75: i2c_sw_engines[8] immediately precedes i2c_hw_engines[8]), the read at index -1 aliases i2c_sw_engines[7], which dce80's construct never writes, so engine resolves to NULL and the function returns NULL at line 130 β€” benign. Reproducing a crash would additionally require i2c_sw_engines[7] to hold a non-NULL stale/garbage pointer, which dce80 does not produce. Filed as Info/hardening.

Impact

Defense-in-depth gap, no demonstrated privilege or availability impact today. A future struct reordering, or a sibling that initializes i2c_sw_engines[7], would turn this into a real OOB read.

Add the missing lower bound so the signed UNKNOWN sentinel and any negative value are rejected before indexing. GPIO_DDC_LINE_MIN is already defined as GPIO_DDC_LINE_DDC1 (=0) at gpio_types.h:218.

--- a/sys/dev/drm/amd/display/dc/i2caux/dce80/i2caux_dce80.c
+++ b/sys/dev/drm/amd/display/dc/i2caux/dce80/i2caux_dce80.c
@@ -121,7 +121,8 @@ static struct i2c_engine *acquire_i2c_hw_engine(
    if (ddc->hw_info.hw_supported) {
        enum gpio_ddc_line line = dal_ddc_get_line(ddc);

-       if (line < GPIO_DDC_LINE_COUNT) {
+       if (line >= GPIO_DDC_LINE_MIN &&
+           line < GPIO_DDC_LINE_COUNT) {
            non_generic = true;
            engine = i2caux->i2c_hw_engines[line];
        }

The same one-line lower-bound guard should be applied to the siblings (dce100/110/112/120 acquire_i2c_hw_engine) and to get_hw_supported_ddc_line() in i2caux.c:287 for a complete fix; those are outside this file.

References

Timeline

  • 2026-07-25 Discovered during automated audit.
  • 2026-07-25 Reported to DragonFlyBSD security contact.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2089 Β· 2 files
FileTypeDescriptionSize
fix.diff suggested-fix git-apply-able fix 393 B view raw
VERDICT.md verdict source-trace confirmation 614 B ↓ raw
VERDICT.md verdict source-trace confirmation
↓ download raw

DF-2089 β€” i2caux_dce80 missing lower bound on signed gpio_ddc_line

Verdict

REPRODUCED (source-only confirmation). Bug confirmed by source tracing.

Mechanism

acquire_i2c_hw_engine (i2caux_dce80.c:124) checks only line < GPIO_DDC_LINE_COUNT. enum gpio_ddc_line is signed (GPIO_DDC_LINE_UNKNOWN=-1). If dal_ddc_get_line returns -1, the check passes and i2c_hw_engines[-1] is an OOB read before the array.

Fix

Add line >= 0 to the bounds check: if (line >= 0 && line < GPIO_DDC_LINE_COUNT).

Batch-build status

Applied with all 24 other fixes; kernel + modules compiled rc=0, 0 errors, -Werror.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Added line>=0 lower bound; batch build rc=0.

Added line>=0 lower bound; batch build rc=0.
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

i2caux_dce80 line<GPIO_DDC_LINE_COUNT misses negative OOB.

Verified recommended fix

i2caux_dce80 line<GPIO_DDC_LINE_COUNT misses negative OOB.

Verdict

i2caux_dce80 line<GPIO_DDC_LINE_COUNT misses negative OOB.