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

drm_dp_sideband_msg_build OOB write past chunk[48] via unchecked msg_len from malicious MST hub

Summary

drm_dp_sideband_msg_build at drm_dp_mst_topology.c:349: msg->curchunk_len=recv_hdr.msg_len (6-bit 0-63 from attacker-controlled sideband header, CRC4 computed by attacker). chunk[48] (drm_dp_mst_helper.h:216). Non-hdr branch :367-368: memcpy(&chunk[curchunk_idx],replybuf,replybuflen) with NO bounds check on curchunk_idx+replybuflen vs sizeof(chunk). msg_len=63 -> curchunk_idx grows to 61 -> writes past chunk into msg[]. Multi-chunk: curlen += curchunk_len-1 (62), after 5 chunks curlen wraps msg[256] -> overwrites curchunk_len/curchunk_idx/initial_hdr. UNAUTHENTICATED: malicious MST hub/dock/monitor (HPD IRQ -> drm_dp_mst_hpd_irq). Fix: check curchunk_len<=sizeof(chunk), check curchunk_idx+replybuflen, check curlen+curchunk_len-1.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1264 Β· 11 files
FileTypeDescriptionSize
dp_sideband_oob.c trigger-source trigger / documentation PoC 2.1 KB view raw
fix.diff suggested-fix git-apply-able patch closing the cited path 919 B view raw
VERDICT.md verdict full source trace + reachability + fix analysis 3.5 KB ↓ raw
README.md readme build/run/expected 1.4 KB ↓ raw
build.sh build-script cc -O2 -Wall -o dp_sideband_oob dp_sideband_oob.c 117 B view raw
run.sh run-script ./dp_sideband_oob 49 B view raw
run.log run-log decisive run, full output 350 B view raw
fix_build.log build-log nativekernel compile-validation excerpt, rc=0 32.5 KB view raw
env.txt environment uname, cc version, device/module state 583 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme build/run/expected
↓ download raw

DF-1264 PoC β€” drm_dp_sideband_msg_build chunk[48] OOB write

Status

INCONCLUSIVE on the audit guest: bug confirmed real by source trace at sys/dev/drm/drm_dp_mst_topology.c:326, but hardware-gated and not triggerable here. fix.diff authored and validated to apply + compile (nativekernel rc=0, -Werror). See VERDICT.md for the full trace and fix.diff for the patch.

Build

./build.sh

(equivalent: cc -O2 -Wall -o dp_sideband_oob dp_sideband_oob.c)

Run (as unprivileged user)

./run.sh

Expected on the audit guest

The PoC runs and reports that the kernel trigger surface is absent on this QEMU/KVM guest (no LSI MegaRAID controller / no AMD Tonga GPU / no DP MST hardware; mfi is in-kernel but /dev/mfi0 is never created; drm/amdgpu are loadable modules that are not loaded and would not attach). It exits 0 without exercising the sink. The bug is therefore a latent, hardware-dependent defect, not a false positive.

Files

  • dp_sideband_oob.c β€” trigger / documentation PoC
  • fix.diff β€” standalone git apply -p1 patch closing the cited path
  • VERDICT.md β€” full source-level mechanism + reachability + fix analysis
  • build.log / run.log β€” captured build/run output
  • fix_build.log β€” nativekernel compile-validation excerpt (rc=0)
  • env.txt β€” guest environment
  • manifest.json β€” machine-readable artifact catalog
VERDICT.md verdict full source trace + reachability + fix analysis
↓ download raw

DF-1264 β€” drm_dp_sideband_msg_build chunk[48] OOB write

Verdict

INCONCLUSIVE (hardware-gated latent bug, not triggerable on this guest). The OOB write is confirmed real by source tracing; not reachable on the audit QEMU guest because the sink lives in the loadable drm.ko module and is driven only by DisplayPort MST sideband replies from real MST hardware (absent). fix.diff authored and validated to apply + compile (nativekernel rc=0, -Werror).

Mechanism (source trace)

drm_dp_sideband_msg_build (sys/dev/drm/drm_dp_mst_topology.c:326):

struct drm_dp_sideband_msg_rx { u8 chunk[48]; u8 msg[256]; ... };   /* helper.h:215-225 */

if (hdr) {
    ...
    msg->curchunk_len = recv_hdr.msg_len;          /* 6-bit field, 0..63  (:349) */
    msg->curchunk_hdrlen = hdrlen;
    /* NO bound vs sizeof(msg->chunk)=48 */
    msg->curchunk_idx = min(msg->curchunk_len, (u8)(replybuflen - hdrlen));
    memcpy(&msg->chunk[0], replybuf + hdrlen, msg->curchunk_idx);   /* can be >48 (:365) */
} else {
    memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen);  /* OOB  (:367) */
    msg->curchunk_idx += replybuflen;
}
  • msg->curchunk_len comes from recv_hdr.msg_len, a 6-bit value (0–63) parsed from the sideband header, with a CRC4 the attacker controls.
  • The header branch can copy up to curchunk_len (63) bytes into chunk[0], already 15 bytes past chunk[48].
  • The non-header branch memcpy's replybuflen bytes at chunk[curchunk_idx] with no check that curchunk_idx + replybuflen <= sizeof(chunk). Across chunks, curchunk_idx grows toward 61 and the write runs off the end of chunk[48] into msg[256] / the count fields / initial_hdr of the drm_dp_sideband_msg_rx struct (and, depending on the embedding struct, adjacent heap).
  • Input is an attacker-controlled MST sideband message β€” i.e. a malicious DP MST branch device / dock / monitor (or a fuzz-injected AUX reply).

Reachability on the audit guest

  • drm_dp_sideband_msg_build is module-only: it is in drm.ko (nm /boot/kernel/drm.ko β‡’ drm_dp_sideband_msg_build), NOT in the base kernel (nm /boot/kernel/kernel | grep drm_dp_sideband β‡’ 0 hits). drm.ko is not loaded on the guest.
  • Even loaded, it is only reached via the MST topology worker processing sideband replies from a DP MST branch device. The guest's only GPU is a virtio-class vgapci0; there is no DP MST topology. Not triggerable here.

Exploit chain

None β€” not exercisable on this guest (module-only + no DP MST HW). The primitive is a bounded-but-over-48 OOB write of attacker-shaped bytes (replybuf) into the drm_dp_sideband_msg_rx struct; reachable only with a malicious/buggy MST device on real display hardware.

PoC changes

Authored dp_sideband_oob.c β€” a documentation stub that records the reachability finding (sink is module-only / HW-driven; no userspace program can reach it on this guest).

Fix validation

  • fix.diff applies cleanly: git apply --check -p1 β‡’ OK (2 hunks).
  • Compiles: applied to /usr/src, make nativekernel rebuilt drm.ko β‡’ NK_DONE rc=0, drm_dp_mst_topology.c built with -Werror (fix_build.log).
  • Functional test: not_testable (no DP MST HW; module not loadable into a path that reaches the sink).
  • Reject curchunk_len > sizeof(msg->chunk) right after it is assigned (bound the header-branch copy too).
  • In the non-header branch, reject curchunk_idx + replybuflen > curchunk_len before the memcpy. See fix.diff.

Fix verification

not_testable

compile validated -Werror

module/kernel build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. drm_dp_sideband_msg_build curchunk_len 6-bit no bounds vs chunk[48] -> OOB write. drm module only, no DP MST HW.