dc_submit_i2c indexes dc->links[] with an unvalidated, wrong-domain index (DDC line number from VBIOS): NULL/wild deref panic and silent wrong-connector DDC routing
Summary
dc->links[link_index] is used with no check against link_count, and the only caller passes ddc_pin->hw_info.ddc_channel - an enum gpio_ddc_line DDC line number sourced from the VBIOS object table, NOT a link index. Slots >= link_count in the kzalloc'd links[12] are NULL -> panic on the first DDC/EDID transaction; -1 gives a far-OOB pointer; mismatched-but-valid values route DDC to the wrong physical connector. Upstream later fixed the caller to pass link_index - this tree retains the pre-fix form. Bounds-check + caller-domain fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4216 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.6 KB | β raw | |
| VERDICT.md | β | 1.6 KB | β raw | |
| env.txt | β | 992 B | view raw | |
| fix.diff | β | 562 B | view raw | |
| manifest.json | β | 798 B | view raw | |
| verdict.json | β | 2.0 KB | view raw |
DF-4216 β dc_submit_i2c indexes dc->links[] with an unvalidated, wrong-domain index (DDC line number): NULL/wild deref and wrong-connector routing
- File:
sys/dev/drm/amd/display/dc/core/dc.c(consumer); callersys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm.c:4008-4010 - Lines: 1736β1748 (dc_submit_i2c), amdgpu_dm.c:3983β4015 (caller)
- Class: unvalidated array index / index-domain confusion (CWE-129 β CWE-476)
- Hardware gate: AMD GPU with DC; mis-mapped DDC line β link numbering (VBIOS object table)
Root cause
bool dc_submit_i2c(struct dc *dc, uint32_t link_index, struct i2c_command *cmd)
{
struct dc_link *link = dc->links[link_index]; /* 1742: NO check */
struct ddc_service *ddc = link->ddc; /* 1743: deref */
return dce_i2c_submit_command(dc->res_pool, ddc->ddc_pin, cmd);
}
link_index is never validated against dc->link_count (array is
links[MAX_PIPES*2] = 12, kzalloc'd β slots β₯ link_count are NULL,
dc.h:287, dc.c:731). The only in-tree caller passes
dc_submit_i2c(ddc_service->ctx->dc,
ddc_service->ddc_pin->hw_info.ddc_channel, /* !!! */
&cmd)
hw_info.ddc_channel is i2c_info.i2c_line β a enum gpio_ddc_line
DDC line number from the VBIOS connector object table
(dc_link_ddc.c:269, gpio_types.h:206-220: GPIO_DDC_LINE_UNKNOWN = -1,
DDC1..DDC6 = 0..5, DDC_VGA = 6, VIP/I2C pad = 7), not a link index
(link->link_index). Upstream Linux later fixed exactly this β current
amdgpu passes ddc_service->link->link_index.
Impact
GPIO_DDC_LINE_UNKNOWN (-1)βdc->links[0xffffffff]β read far out of thedcstruct β wilddc_link*βlink->ddcderef β panic/corruption.- Any DDC line β₯
link_count(sparse connector maps: e.g. board exposing 2 connectors whose lines are DDC3/VGA) βlinks[n] == NULLβ NULL deref at 1743 β kernel panic on the first DDC transaction (EDID read during hotplug/detect). - Even when in range and non-NULL, line N β link N in general β DDC/I2C transactions are routed to the wrong physical connector (EDID reads from the wrong monitor; functional correctness / info integrity).
Verification status
untested β hardware-gated (no AMD GPU on the guest, env.txt). Source
certainty for the missing validation and the domain mismatch; real-world
trigger depends on board/VBIOS wiring.
Fix
fix.diff (defensive, in-file): bounds-check link_index against
dc->link_count. Real fix belongs in amdgpu_dm.c (pass
ddc_service->link->link_index), noted for the upstream report.
VERDICT β DF-4216
status: untested (hardware-gated) Β· impact: NULL/wild deref panic on DDC access; wrong-connector I2C routing Β· confidence: certain (source) / likely (trigger)
No AMD GPU on the audit guest (env.txt), so dc_submit_i2c cannot run.
Source proof:
- dc.c:1742 dc->links[link_index] with no comparison against
dc->link_count; array size is 12 (dc.h:287 links[MAX_PIPES*2],
MAX_PIPES=6, hw_shared.h:37), kzalloc'd so unused slots are NULL.
- Caller amdgpu_dm.c:4008-4010 passes ddc_pin->hw_info.ddc_channel,
assigned from i2c_info.i2c_line (dc_link_ddc.c:269) β enum
gpio_ddc_line (gpio_types.h:206-220), including UNKNOWN = -1 and
values up to 7 β an unrelated index domain (DDC line β link index).
- Outcomes: -1 β far OOB pointer read then deref; line β₯ link_count β
NULL->ddc panic (dc.c:1743); mismatched but non-NULL β silent
wrong-connector DDC routing.
- Confirms upstream divergence: Linux amdgpu_dm later switched to
ddc_service->link->link_index in amdgpu_dm_i2c_xfer(); this tree kept
the pre-fix form.
Low severity (board/VBIOS-dependent), high code certainty. In-file defensive
fix + recommended caller fix documented; not runtime-validated (no hardware)
β fix_status: not_testable.
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_testablediff authored and git-apply-verified against the read-only sys/ tree; runtime fix validation impossible without the triggering hardware on the QEMU guest
findings/poc/DF-4216/fix.diff (git apply --check passed)
Confirmed kernel references
Detail
Evidence (decisive lines)
findings/poc/DF-4216/VERDICT.md, findings/poc/DF-4216/env.txt (guest pciconf: no triggering hardware), findings/poc/DF-4216/fix.diff
PoC changes
no runnable PoC: hardware-gated path absent from the audit guest (env.txt)
Verified recommended fix
bounds-check link_index in dc_submit_i2c and pass ddc_service->link->link_index from amdgpu_dm
Verdict
dc_submit_i2c (dc.c:1742) indexes dc->links[link_index] with no validation against dc->link_count, and the only caller (amdgpu_dm.c:4008-4010) passes ddc_pin->hw_info.ddc_channel β a gpio_ddc_line DDC line number (gpio_types.h:206-220, UNKNOWN=-1..7) from the VBIOS object table, not a link index. -1 gives a far-OOB pointer read then deref; line>=link_count gives NULL->ddc panic on the first DDC/EDID transaction; otherwise DDC traffic is silently routed to the wrong connector. Upstream Linux fixed the caller to pass ddc_service->link->link_index.
No comments yet.