Write-reply stack buffer overflow in aux_engine: missing bound of returned_byte against reply_data[16]
| Field | Value |
|---|---|
| ID | DF-1868 |
| Status | new |
| Severity | Medium |
| CVSS 3.1 | CVSS:3.1/AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-787 Out-of-bounds Write |
| File | sys/dev/drm/amd/display/dc/i2caux/aux_engine.c |
| Lines | 415-422, 328 |
| Area | dev/drm/amd (DisplayPort AUX channel write reply) |
| Confidence | likely |
| Discovered | 2026-07-20 |
| Reported | pending |
| Known CVE | none |
| CVE match | variant |
Summary
In process_write_request(), the AUX reply byte count (ctx->returned_byte,
sourced from the HW AUX_SW_REPLY_BYTE_COUNT register β a 5-bit field, max value
31) is assigned to ctx->reply.length and paired with ctx->reply_data, a fixed
16-byte stack buffer (DEFAULT_AUX_MAX_DATA_SIZE=16). No check bounds
returned_byte against the actual buffer size. A crafted DisplayPort AUX reply
from a malicious monitor/dock/dongle carrying more than 16 data bytes causes up
to 14 bytes of stack overflow past reply_data[16], corrupting the
transaction_complete and operation_succeeded fields and the stack frame.
Root cause
process_write_request() (aux_engine.c:415-422) unconditionally sets:
ctx->reply.length = ctx->returned_byte;
ctx->reply.data = ctx->reply_data; /* 16-byte stack array at line 328 */
without verifying returned_byte <= DEFAULT_AUX_MAX_DATA_SIZE. It then calls
process_write_reply() β process_channel_reply() β read_channel_reply()
which reads the HW AUX_SW_REPLY_BYTE_COUNT field (5-bit, mask 0x1F000000) into
bytes_replied, decrements by 1 for the status byte, then checks
if (bytes_replied > size) return -1 β but size is reply->length which
equals ctx->returned_byte (the same register value minus 1). The check compares
the register against itself and provides zero defensive bound against the 16-byte
buffer.
The write loop then writes bytes_replied bytes (up to 30) into the 16-byte
buffer.
Contrast with the read path (aux_engine.c:196) which correctly checks
returned_byte > current_read_length against the caller-provided bound β the
developer was careful on read but missed write.
Threat model & preconditions
- Attacker position: a malicious DisplayPort device (monitor, KVM switch, dock, USB-C DP-alt-mode adapter, or active converter dongle) with physical or evil-maid access to a DP/USB-C/Thunderbolt port.
- Privileges gained or impact: kernel stack corruption in
write_command_context. The overflow data is attacker-controlled (the malicious reply payload). On x86-64, the 14 overflow bytes can corrupt saved RBP/RIP β RIP control β kernel code execution β root. Floor: kernel panic. - Required config or capabilities: AMD GPU with DC enabled, display attached (default on all AMD APU/dGPU systems).
- Reachability: AUX write is triggered automatically during link training after HPD assertion β no user interaction needed beyond plugging the malicious device.
Proof of concept
Hardware PoC (evil-dock threat model): build a malicious USB-C DP-alt-mode or
DisplayPort peripheral using an RP2040/STM32 + FUSB302 PD controller (or an FPGA)
that implements AUX Manchester bit-banging. On any AUX write request from the
host, respond with a valid AUX ACK (4-bit status = 0x0) followed by 30 data bytes
instead of the standard 0 data bytes. The host AMD GPU AUX receiver will count 31
bytes in AUX_SW_REPLY_BYTE_COUNT.
To verify the code bug without custom hardware: temporarily instrument
aux_engine_dce110.c:431 to force *returned_bytes = 30 and trigger any display
hotplug β observe the overflow in a KASAN build.
Expected output
kernel panic: stack corruption / page fault on corrupted return address stack trace through process_write_reply / process_write_request / write_command
Impact
Medium-severity kernel stack overflow reachable from a malicious DisplayPort peripheral (physical access / evil dock). With careful payload crafting the overflow can control saved RBP/RIP β kernel code execution β root. No purely software trigger exists; exploitation requires malicious AUX-layer input from attached hardware.
Recommended fix
Add a defensive bound check in process_write_request(), mirroring the read
path's bound check at aux_engine.c:196.
--- a/sys/dev/drm/amd/display/dc/i2caux/aux_engine.c
+++ b/sys/dev/drm/amd/display/dc/i2caux/aux_engine.c
@@ -414,11 +414,19 @@ static void process_write_request(
switch (operation_result) {
case AUX_CHANNEL_OPERATION_SUCCEEDED:
ctx->timed_out_retry_aux = 0;
ctx->invalid_reply_retry_aux = 0;
+ /* The reply_data scratch buffer is only DEFAULT_AUX_MAX_DATA_SIZE
+ * bytes. A malicious or misbehaving DP source could cause the HW
+ * AUX_SW_REPLY_BYTE_COUNT field (5 bits) to report more than 16
+ * data bytes, so bound-check before copying. */
+ if (ctx->returned_byte > DEFAULT_AUX_MAX_DATA_SIZE) {
+ ctx->status =
+ I2CAUX_TRANSACTION_STATUS_FAILED_PROTOCOL_ERROR;
+ ctx->operation_succeeded = false;
+ break;
+ }
+
ctx->reply.length = ctx->returned_byte;
ctx->reply.data = ctx->reply_data;
process_write_reply(engine, ctx);
break;
References
- Read path bound check (correct): aux_engine.c:196.
- HW register field:
AUX_SW_REPLY_BYTE_COUNT5-bit mask 0x1F000000.
Timeline
- 2026-07-20 Discovered during automated audit.
- 2026-07-20 Reported to DragonFlyBSD security contact (pending).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1868 Β· 2 files| File | Type | Description | Size | |
|---|---|---|---|---|
| fix.diff | suggested-fix | git-apply-able unified diff; validated as part of combined 41-finding kernel build (rc=0, -Werror clean) | 641 B | view raw |
| VERDICT.md | verdict | source-only confirmation + HW/module gating explanation | 1.6 KB | β raw |
DF-1868 Verification
Verdict
SOURCE-CONFIRMED, INCONCLUSIVE-RUNTIME (HW/module gated).
The cited defect exists in the audited source at sys/dev/drm/amd/display/dc/i2caux/aux_engine.c:415-422. Reproduction
on the running guest is not possible because the affected code path is
gated behind hardware that is not present in the audit QEMU/KVM guest
(no AMD/i915 GPU, no LSI MegaRAID, no MMC/SDHCI controller, no FireWire, no
ATAPI floppy, etc.) and/or lives in a kernel module that is not loaded on the
GENERIC-running guest.
Mechanism (source-only confirmation)
amd display DC (not in GENERIC, no AMD HW). Source: process_channel_reply case AUX_CHANNEL_OPERATION_SUCCEEDED at L415-422 unconditionally sets ctx->reply.length=ctx->returned_byte, ctx->reply.data=ctx->reply_data (16-byte stack array). HW AUX_SW_REPLY_BYTE_COUNT register (5 bits, so 0..31) is not validated against DEFAULT_AUX_MAX_DATA_SIZE=16; >16 overflows the stack buffer.
Recommended fix
Clamp ctx->returned_byte to DEFAULT_AUX_MAX_DATA_SIZE before assigning reply.length.
The full git apply-able diff lives in fix.diff in this folder; it was
applied as part of a single combined 41-finding kernel build that compiled
cleanly (rc=0, -Werror clean) β see ../fix_build_summary.txt.
Build validation
git apply --checkon this fix.diff: OK- Combined kernel build (
X86_64_GENERIC, INVARIANTS ON) with all 41 findings' fix.diffs applied: rc=0, no warnings, no errors. - The patched kernel was not booted/run because the affected code path requires hardware that the audit guest does not have.
Confirmed kernel references
- s
- y
- s
- /
- d
- e
- v
- /
- d
- r
- m
- /
- a
- m
- d
- /
- d
- i
- s
- p
- l
- a
- y
- /
- d
- c
- /
- i
- 2
- c
- a
- u
- x
- /
- a
- u
- x
- _
- e
- n
- g
- i
- n
- e
- .
- c
- :
- 4
- 1
- 5
- -
- 4
- 2
- 2
Detail
Exploit chain
none β non-corruption classes (info leak / DoS / div0 / logic) or HW/module gated. No memory-corruption primitive reachable from userspace on this guest.
Evidence (decisive lines)
Source-only confirmation. Combined kernel build with all 41 fix.diffs applied: === NK_DONE rc=0 === at Wed Jul 22 18:05:21 UTC 2026 (no errors, no warnings). See findings/poc/fix_build_summary.txt.
PoC changes
Authored findings/poc/DF-1868/fix.diff (minimal targeted guard). VERDICT.md and manifest.json written. fix.diff validated by combined build.
Verified recommended fix
Clamp ctx->returned_byte to DEFAULT_AUX_MAX_DATA_SIZE before assigning reply.length. Full git-apply-able diff in findings/poc/DF-1868/fix.diff; validated as part of combined 41-finding kernel build (rc=0).
Verdict
SOURCE-CONFIRMED, INCONCLUSIVE-RUNTIME. The cited defect exists at sys/dev/drm/amd/display/dc/i2caux/aux_engine.c:415-422. amd display DC (not in GENERIC, no AMD HW). process_write_request case AUX_CHANNEL_OPERATION_SUCCEEDED at L415-422 unconditionally sets ctx->reply.length=ctx->returned_byte, ctx->reply.data=ctx->reply_data (16-byte stack array). HW AUX_SW_REPLY_BYTE_COUNT (5 bits, 0..31) not validated against DEFAULT_AUX_MAX_DATA_SIZE=16; >16 overflows the stack buffer. HW gated.
No comments yet.