dc_link_dp_set_test_pattern NULL-pointer dereference on inactive pipe (pipes[i].stream->sink unchecked) plus wrong-object blank (pipes[0] instead of matched pipe_ctx) β sink-triggerable panic with no active stream
Summary
The scan 'if (pipes[i].stream->sink->link == link)' has no NULL check; pipe_ctx[].stream is NULL for every inactive pipe, and the function is reachable from a sink-initiated automated test with NO stream on the tested link. First NULL hole => panic. Second defect: :2653 blanks pipes[0]'s stream_enc instead of the matched pipe_ctx. Independent of lane_count. NULL-check + correct-object fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3988 Β· 9 files| File | Type | Description | Size | |
|---|---|---|---|---|
| trigger.c | trigger | hardware-precondition model / protocol replica | 2.9 KB | view raw |
| README.md | readme | build/run/expected | 895 B | β raw |
| VERDICT.md | verdict | full path:line trace | 3.6 KB | β raw |
| build.sh | script | exact build commands | 65 B | view raw |
| run.sh | script | exact run commands | 89 B | view raw |
| build.log | log | full untrimmed build output | 298 B | view raw |
| run.log | log | full untrimmed runtime output | 457 B | view raw |
| env.txt | env | guest hardware/src-md5 proof | 1.1 KB | view raw |
| fix.diff | fix | git-apply-able unified diff | 1.1 KB | view raw |
DF-3988 β dc_link_dp_set_test_pattern dereferences pipes[i].stream->sink
without a NULL check (dc_link_dp.c:2606): pipe_ctx[].stream is NULL for
every inactive pipe, and sink-initiated automated-test requests arrive
without any active stream on the tested link β NULL-pointer deref panic.
Also :2653 blanks pipes[0]'s stream_enc instead of the matched pipe_ctx
(wrong object; second NULL deref when pipe0 is an inactive hole).
BUILD cc -O2 -Wall -Wextra -o trigger trigger.c
RUN ./trigger (model of the :2605-2610 scan: pipes[0]=NULL hole, match at pipes[1] β NULL DEREF on i=0)
On the audit guest: builds and runs clean (run.log). Hardware-gated: guest has bochs VGA only, zero 0x1002 PCI devices, amdgpu.ko not loaded.
EXPECTED "i=0 pipes[0].stream=0x0 -> ...->sink->link: NULL DEREF (panic)" and the :2653 wrong-object note; TRIGGER_RC=0.
DF-3988 VERDICT β source-level certain, runtime hardware-gated (inconclusive)
Root cause (path:line)
dc_link_dp_set_test_pattern β dc_link_dp.c:2588-2740:
2595: struct pipe_ctx *pipes = link->dc->current_state->res_ctx.pipe_ctx;
2596: struct pipe_ctx *pipe_ctx = &pipes[0];
2605: for (i = 0; i < MAX_PIPES; i++) {
2606: if (pipes[i].stream->sink->link == link) { /* no NULL check */
2607: pipe_ctx = &pipes[i];
2608: break;
2609: }
2610: }
pipe_ctx[].stream is NULL for every inactive pipe: after boot with no
display commit, for all pipes during DPMS-off/idle, and for holes left in
the array after unplug/re-plug re-arrangements (e.g. stream on pipe1 while
pipe0 is free). The function is reachable from a sink-initiated automated
test with NO stream on the tested link:
HPD short pulse β dc_link_handle_hpd_rx_irq :2045-2054
(AUTOMATED_TEST bit) β handle_automated_test :1990 (LINK_TEST_PATTRN)
β dp_test_send_link_test_pattern :1957 / or :1994 (PHY_TEST_PATTERN)
β dp_test_send_phy_test_pattern :1909 β dc_link_dp_set_test_pattern.
A malicious sink/dongle (or a compliance test box hitting during a
no-commit window, or any tested link whose pipe sits after an inactive
hole) reaches :2606 with pipes[0].stream == NULL β NULL->sink β
kernel panic.
Second defect in the same function, :2653:
pipes->stream_res.stream_enc->funcs->dp_blank(pipe_ctx->stream_res.stream_enc);
This uses pipes (i.e. &pipes[0]) β NOT the matched pipe_ctx β for the
object deref (wrong pipe: blanks pipe 0's stream encoder regardless of
which pipe carries the tested link), and dereferences its stream_enc
without a NULL check (inactive pipe 0 β second NULL deref), while passing
pipe_ctx's (possibly different) stream_enc as the argument. Discovered
while auditing the automated-test surface for pass 2; both defects are in
the sink-reachable test-pattern path.
Distinguishing from DF-1319/DF-3987
DF-1319 = lane_count stack overflow in LT helpers. DF-3987 = poisoned cur_link_settings indexed past lane arrays. This finding is a NULL-deref / wrong-object deref on the pipe array β independent of lane_count, reachable even with byte-perfect DPCD data (a compliant test appliance triggering a link test pattern with no active stream is enough).
Verification on the guest (honest)
- trigger.c models the exact scan shape (NULL hole at [0], match at [1]): builds and runs on the guest (run.log, RC=0, NULL-DEREF reported at i=0).
- Real chain hardware-gated: zero 0x1002 PCI devices, amdgpu.ko not loaded (env.txt). Status inconclusive per family convention.
- Guest src md5 matches audited tree (env.txt).
Threat model / impact
Malicious DP sink/dongle (physical) OR benign compliance-test traffic at an unlucky instant (no active stream / pipe hole) β deterministic NULL-deref panic of the kernel thread handling the HPD work. Local DoS; no info leak, no write primitive. Severity Medium (matches DF-1319 family threat model, lower impact class).
Fix
fix.diff: NULL-check pipes[i].stream != NULL && pipes[i].stream->sink !=
NULL in the scan; blank the matched pipe_ctx with a stream_enc NULL
check. git-apply clean; not runtime-validable on this guest.
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_testablefix.diff adds pipes[i].stream != NULL && pipes[i].stream->sink != NULL to the scan and switches the blank to the matched pipe_ctx with a stream_enc NULL check. Cannot be runtime-validated: no AMD GPU on the guest. Mechanical NULL-guard on an already-compiled path; git-apply clean.
fix.diff; hardware absence proof in env.txt
Confirmed kernel references
- sys/dev/drm/amd/display/dc/core/dc_link_dp.c:2595
- sys/dev/drm/amd/display/dc/core/dc_link_dp.c:2605
- sys/dev/drm/amd/display/dc/core/dc_link_dp.c:2606
- sys/dev/drm/amd/display/dc/core/dc_link_dp.c:2653
- sys/dev/drm/amd/display/dc/core/dc_link_dp.c:2045
- sys/dev/drm/amd/display/dc/core/dc_link_dp.c:1990
- sys/dev/drm/amd/display/dc/core/dc_link_dp.c:1909
Detail
Exploit chain
On real hardware: DP sink/test appliance sends AUTOMATED_TEST short HPD with LINK_TEST_PATTERN or PHY_TEST_PATTERN request while no stream is committed on the tested link (or the link's pipe sits behind a NULL hole) -> pipes[0].stream == NULL -> NULL pointer dereference panic in the HPD work thread. DoS only; no write primitive, no leak.
Evidence (decisive lines)
run.log (model: pipes[0]=NULL hole, match at pipes[1] -> NULL DEREF at i=0; RC=0), env.txt (NO_AMD_GPU / amdgpu.ko unloaded / guest-src md5 match), VERDICT.md (path:line trace incl. reachability without any stream), trigger.c, fix.diff (NULL checks + matched-pipe blank, git-apply clean)
PoC changes
No seed PoC. Authored trigger.c as an annotated hardware-precondition + scan-shape model; honest hardware-absent verification per family convention.
Verified recommended fix
NULL-check pipes[i].stream/sink in the scan loop and blank the matched pipe_ctx (not pipes[0]) with a stream_enc NULL check (see fix.diff).
Verdict
Source-level certain, runtime hardware-gated (inconclusive per family convention). dc_link_dp_set_test_pattern (dc_link_dp.c:2588) scans current_state->res_ctx.pipe_ctx with if (pipes[i].stream->sink->link == link) (:2605-2610) with NO NULL check on pipes[i].stream. pipe_ctx[].stream is NULL for every inactive pipe - after boot with no display commit, during DPMS-off, and in holes left after unplug re-arrangements - and the function is reachable from a sink-initiated automated test with NO active stream on the tested link: dc_link_handle_hpd_rx_irq:2045 (AUTOMATED_TEST) -> handle_automated_test:1990/:1994 -> dp_test_send_link_test_pattern:1957 / dp_test_send_phy_test_pattern:1909 -> dc_link_dp_set_test_pattern. First NULL hole at i=0 -> NULL->sink deref -> panic. Second defect in the same function: :2653 blanks pipes[0]'s stream_enc (pipes->stream_res.stream_enc->funcs->dp_blank(pipe_ctx->stream_res.stream_enc)) instead of the MATCHED pipe_ctx - wrong-object access and another NULL deref when pipe0 is an inactive hole. Independent of lane_count: even byte-perfect DPCD data (a compliant test appliance during a no-commit window, or any tested link whose pipe sits after an inactive hole) triggers it. Runtime: guest has bochs VGA only, zero 0x1002 devices, amdgpu.ko not loaded (env.txt); trigger.c model of the scan builds and runs on the guest (run.log RC=0, NULL DEREF reported at i=0). Guest src md5 028326495b845611c38ec70dcd655bf3 == audited tree.
No comments yet.