# DF-3957 VERDICT

**Verdict: INCONCLUSIVE at kernel level (no AMD GPU) — source-certain NULL
`enc_priv` dereference family including two NULL-page WRITES; the verbatim
`NULL+offset` read/write expressions fault in the userland simulation exactly
as predicted. Confidence: likely (NULL requires missing LVDS_Info table or
kzalloc failure — both real-world-observed conditions, neither forceable
without hardware).**

## Trace

1. **enc_priv can be NULL for a digital encoder.**
   radeon_add_atom_encoder (atombios_encoders.c:2829-2883): every LCD-supporting
   digital encoder takes `enc_priv = radeon_atombios_get_lvds_info(...)`
   (:2838, :2872). That function (radeon_atombios.c:1617-1755) returns NULL
   both when `atom_parse_data_header(... LVDS_Info ...)` fails (missing/short
   table) and when its `kzalloc(sizeof(struct radeon_encoder_atom_dig))`
   fails — the return is never checked at the assignment sites. An eDP/LVDS
   connector in the BIOS connector table with a missing LVDS_Info DATA table
   therefore yields a live digital encoder with `enc_priv == NULL`.

2. **Sinks** (all verified unguarded; list in README). The most decisive:
   - `atombios_dig_transmitter_setup2` :1074/:1085 loads `dig->dig_encoder`
     at NULL+0 — called at BOOT for every UNIPHY-family encoder by
     `radeon_atom_encoder_init` (:2314).
   - `radeon_atom_encoder_dpms_dig` :1744 **writes** `dig->panel_mode` at
     NULL+12 on the first DPMS ON of an LCD-supporting UNIPHY encoder
     (DCE4.1+, i.e. every KV/BT APU).
   - `radeon_atom_encoder_disable` :2661-2663 reads `dig->dig_encoder` then
     **writes** −1 to NULL+0 on encoder teardown.
   Neighboring code proves the invariant is not trusted elsewhere: NULL
   checks exist at :100 (`set_backlight_level`), :240 (`backlight_init`),
   :629 (`digital_setup`), :2551 (`prepare`) — the file is simply
   inconsistent, and upstream Linux radeon is identical (inherited).

3. **Non-issues ruled out while auditing this family** (pass-2 depth):
   `afmt[dig->dig_encoder]` (:2557) is in-bounds — RADEON_MAX_AFMT_BLOCKS=7
   (radeon_mode.h:52) and dig_encoder ≤ 6; MST fake encoders get
   `encoder_id = 0` (NONE) so `radeon_encoder_is_digital()` is false for them
   — no type confusion through `enc_priv` (`struct radeon_encoder_mst`);
   `sysctl_backlight_handler` (:197-218) is registered only after the
   `enc_priv` NULL check at :240, so it adds no new NULL path (its lifetime
   vs. encoder destruction is a detach-race hardening note only).

## Harness

Replicates the leading `struct radeon_encoder_atom_dig` fields; child1
executes the verbatim read-then-write(-1) of `dig->dig_encoder` (:2661-2663),
child2 the `dig->panel_mode = ...` write (:1744) — both SIGSEGV sig=11 at
NULL+0 / NULL+12. Analysis mode prints all four offsets (0, 8, 12).

## Why Low

NULL-page read/write at fixed small offsets → panic (DoS); no control of the
written value beyond a constant −1; trigger requires radeon hardware with a
BIOS whose connector table promises a panel the LVDS_Info table doesn't
describe (or boot OOM). No unprivileged-local or remote vector. Pass 1 filed
nothing on this file; this is the depth delta of pass 2.

## Fix validation

fix.diff adds the missing NULL guards at all nine sinks (same pattern the
file already uses at :629/:2551). Not built on the guest (no AMD GPU):
`fix_status: not_testable`. Trivially sufficient by inspection.

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