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

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.

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_COUNT 5-bit mask 0x1F000000.

Timeline

  • 2026-07-20 Discovered during automated audit.
  • 2026-07-20 Reported to DragonFlyBSD security contact (pending).

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1868 Β· 2 files
FileTypeDescriptionSize
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
VERDICT.md verdict source-only confirmation + HW/module gating explanation
↓ download 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.

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 --check on 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

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.