# DF-4556 — dce_aux: partial AUX reads reassembled at the wrong offset (kernel-assembled DPCD/EDID data corruption)

## What

`read_command()` (sys/dev/drm/amd/display/dc/dce/dce_aux.c:542-604) loops to
assemble a multi-transaction AUX read. On a *short read* (sink ACKs with fewer
bytes than requested — legal per DP 1.2a), `process_read_reply()`
(dce_aux.c:426-447) advances `ctx->offset += ctx->returned_byte` and reduces
`current_read_length`, and the loop re-requests the remainder
(`request.data = ctx->buffer + ctx.offset` at dce_aux.c:583).

But the *reply* destination is never offset: `process_read_request()` sets

```c
	ctx->reply.length = ctx->returned_byte;
	ctx->reply.data = ctx->buffer;          /* dce_aux.c:501-502 — base, not + offset */
```

so `read_channel_reply()` (dce_aux.c:288-294) copies the second chunk over
`buffer[0..R2-1]` — destroying the first chunk — while the tail region
`buffer[offset..]` was just zeroed by the loop's `memset()` (dce_aux.c:581)
and is never filled.

Result: the caller's DPCD register block / EDID buffer contains
`[chunk2 | zeros | stale-bytes]` instead of `[chunk1 | chunk2]`, while the
transaction is reported **SUCCEEDED**.

## Who is affected

Callers of `aux_engine->funcs->submit_request` with payload.length > one AUX
transaction (16 B), i.e. the i2caux path
(`dal_i2caux_submit_aux_command`, i2caux/i2caux.c:196-267) used for
I2C-over-AUX EDID segment reads and multi-byte DPCD reads. A DisplayPort sink
that ACKs a 16-byte native read with, say, 8 bytes forces the partial path;
the reassembly then silently corrupts the data the kernel subsequently uses
for link training decisions and mode selection.

Upstream status: the whole read_command/process_read_* machinery was replaced
in later DC releases (dce_aux_transfer_raw), so there is no upstream "fix
commit" to point at — the reassembly logic was discarded. Within this DC
version the `ctx->buffer` vs `ctx->buffer + ctx->offset` mismatch is provable
from the code alone.

## Impact

Data integrity only — **not** a memory-safety bug (the copy itself is bounded
by `returned_byte <= current_read_length <= payload.length`). A hostile or
marginal DP sink can make the driver train the link with garbage DPCD values
or parse a scrambled EDID; the worst downstream outcome is a misconfigured
link / rejected EDID (drm_edid re-checksums), plus repeated retries. This is
a correctness defect with attacker-influence, filed at Low severity.

## Reproduction

Hardware-gated (AMD GPU + DP sink returning short reads). The audit guest has
Bochs VGA only (see ../env.txt), so the path cannot execute there.
`trigger.md` documents the DP-sink behavior that forces the partial path.

## Fix

See fix.diff — carry the offset into the reply destination.

## Class considered and killed (why this is not DF-1694)

DF-1694 is the *write* path copying `bytes_replied` (re-read from HW, ≤31 via
the 5-bit AUX_SW_REPLY_BYTE_COUNT field) into the fixed 16-byte
`write_command_context.reply_data[]`. This finding is the *read* path writing
a correctly-bounded number of bytes to a correctly-sized caller buffer at the
wrong *offset* — no overflow, distinct defect in a different function.

## 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.
