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

cz_init_cp_jump_table: NULL mec2_fw dereference on Carrizo when the MEC2 firmware request fails

Summary

MEC2 request_firmware failure is swallowed (err=0, mec2_fw=NULL) but on Carrizo cz_init_cp_jump_table sets max_me=5 and the me==4 slot dereferences adev->gfx.mec2_fw->data plus header fields - NULL-page read -> kernel panic during rlc_init/sw_init whenever amdgpufw_carrizo_mec2 is missing/unreadable. Stoney unaffected (max_me stays 4). Distinct from DF-1134 (jt bounds in the same function): this is the NULL reachability of the 5th slot itself. Carrizo machine with a broken install or root-deleted MEC2 module: boot/probe-time panic. Availability-only, privileged precondition.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3599 Β· 7 files
FileTypeDescriptionSize
run.sh β€” 89 B view raw
run.log β€” 1.7 KB view raw
README.md β€” 1.9 KB ↓ raw
VERDICT.md β€” 2.5 KB ↓ raw
fix.diff β€” 307 B view raw
manifest.json β€” 801 B view raw
verdict.json β€” 2.1 KB view raw

DF-3599 β€” cz_init_cp_jump_table NULL dereference when Carrizo MEC2 firmware is absent

What

gfx_v8_0_init_microcode() treats a failed MEC2 firmware request as non-fatal on every non-Topaz/non-Stoney asic:

    gfx_v8_0.c:1156-1173
    err = request_firmware(&adev->gfx.mec2_fw, fw_name, adev->dev);
    ...
    } else {
        err = 0;
        adev->gfx.mec2_fw = NULL;      /* :1172 β€” load continues OK */
    }

but on Carrizo the RLC init path unconditionally walks a 5th jump-table slot that dereferences that pointer:

    gfx_v8_0.c:1309-1310
    if (adev->asic_type == CHIP_CARRIZO)
        max_me = 5;
    ...
    gfx_v8_0.c:1347-1349
    } else if (me == 4) {
        const struct gfx_firmware_header_v1_0 *hdr =
            (const struct gfx_firmware_header_v1_0 *)adev->gfx.mec2_fw->data;  /* NULL->data */

struct firmware.data sits at a small offset, so with mec2_fw == NULL this reads from the unmapped NULL page and the subsequent le32_to_cpu(hdr->header.ucode_array_offset_bytes) at :1352 and the copy loop at :1357-1359 fault β€” kernel panic during gfx_v8_0_rlc_init() (:1418, reached from gfx_v8_0_sw_init :2091) β†’ driver probe failure / boot-time panic on a CZ system whose amdgpufw_carrizo_mec2 module is missing or unreadable.

Stoney is unaffected (its max_me stays 4 and it never loads mec2 anyway).

Threat model / severity

Requires the MEC2 firmware file to be absent or unrequestable on a Carrizo machine: broken install, root-deleted/replaced firmware module, or distro image gap. Availability-only (NULL-page read β†’ panic), privileged precondition β†’ Low.

Reproduce

Not executable on the audit guest (virtio-only, no AMD GPU, no amdgpu module). Source assertion:

./run.sh

prints the NULL-assigning fallback, the CZ-only max_me = 5, and the unguarded mec2_fw->data dereference on the me == 4 branch.

Fix

fix.diff: only take the 5th slot when adev->gfx.mec2_fw != NULL.

VERDICT.md
↓ download raw

DF-3599 β€” VERDICT

status: inconclusive (kernel run) / source certain reproduced: 0

Proof

  1. gfx_v8_0_init_microcode :1156-1173 β€” on MEC2 request_firmware failure the else branch resets err = 0 and sets adev->gfx.mec2_fw = NULL; initialization therefore succeeds with a NULL MEC2 pointer (this fallback is intentional for asics where mec2 is optional β€” but Carrizo's RLC init is not optional).
  2. cz_init_cp_jump_table :1309-1310 β€” if (adev->asic_type == CHIP_CARRIZO) max_me = 5;
  3. :1347-1352 β€” the me == 4 iteration computes (const struct gfx_firmware_header_v1_0 *)adev->gfx.mec2_fw->data and then le32_to_cpu(hdr->header.ucode_array_offset_bytes) at :1352, hdr->jt_offset/hdr->jt_size at :1321-1322 pattern β†’ with mec2_fw == NULL these read from the NULL page (offset of data within struct firmware is small) β†’ page fault β†’ kernel panic during gfx_v8_0_rlc_init :1418 β†’ gfx_v8_0_sw_init :2091 returns after the probe has already emitted no error for the missing fw.
  4. Reachability precondition: Carrizo system + missing/unreadable amdgpufw_carrizo_mec2 firmware module (broken install, deleted or replaced module). Availability-only; privileged control of firmware files β†’ severity Low.

Stoney never reaches me == 4 (max_me stays 4, and Stoney never loads mec2 β€” :1147-1148), so only Carrizo is affected.

Why not a kernel run

Audit guest is virtio-only β€” no AMD PCI device, no drm/amdgpu module, no /boot/modules. The dereference path requires a Carrizo APU to probe. inconclusive at runtime; the NULL-deref control flow is unambiguous from source (see run.log excerpts).

Fix validation

fix.diff gates the 5th slot on adev->gfx.mec2_fw != NULL. Kernel build/run not possible on this guest β€” fix_status: not_testable; diff line-matched to gfx_v8_0.c:1309-1310.

kernel_refs

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.

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

fix.diff adds the mec2_fw presence check; not buildable/testable on the virtio-only guest (amdgpu module absent).

findings/poc/DF-3599/fix.diff
↓ fix.diffper-fix-DF-3599

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log (source excerpts of the three linked defects); VERDICT.md; fix.diff

PoC changes

authored fresh (pass 2)

Verified recommended fix

Gate the 5th jump-table slot on mec2_fw presence: if (asic_type == CHIP_CARRIZO && adev->gfx.mec2_fw) max_me = 5;

Verdict

On MEC2 firmware request failure gfx_v8_0_init_microcode sets adev->gfx.mec2_fw = NULL and err = 0 (:1170-1173) so initialization succeeds, but on Carrizo cz_init_cp_jump_table sets max_me = 5 (:1309-1310) and the me==4 iteration dereferences adev->gfx.mec2_fw->data (:1349) and the header fields at :1321-1322-pattern reads without a NULL check -> NULL-page read -> kernel panic during gfx_v8_0_rlc_init (:1418) / sw_init (:2091) whenever amdgpufw_carrizo_mec2 is missing or unreadable. Availability-only with a privileged precondition (firmware file manipulation/broken install) -> Low. Not executable on the virtio-only audit guest (no AMD GPU, no amdgpu module) -> inconclusive at runtime; control flow proven from source.