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

amdgpu_atomfirmware_allocate_fb_scratch: VBIOS-controlled unclamped kernel allocation (up to 64MB) whose -ENOMEM return the caller ignores β€” NULL-scratch write chain on Linux-semantics allocators, dead error branch on DragonFly

Summary

used_by_driver_in_kb (untrusted u16)<<10 drives an unclamped kzalloc; on failure the caller drops the return and unguarded consumers write at NULL+4 (atombios_dp.c:74-76) / memcpy from NULL (atombios_i2c.c:104). On DFly the branch is provably dead (M_WAITOK never NULL - failure instead panics with a VBIOS-influenced 64MB request); on Linux-semantics allocators it is a deterministic NULL-page write (upstream master still ignores the return - verified). File byte-identical to Linux v5.0. Clamp + propagate fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-4980 Β· 5 files
FileTypeDescriptionSize
README.md β€” 3.4 KB ↓ raw
VERDICT.md β€” 3.7 KB ↓ raw
verdict.json β€” 3.0 KB view raw
fix.diff β€” 2.0 KB view raw
manifest.json β€” 1018 B view raw

DF-4980 β€” amdgpu_atomfirmware_allocate_fb_scratch: untrusted VBIOS-sized kernel

allocation whose error return the caller ignores

File: sys/dev/drm/amd/amdgpu/amdgpu_atomfirmware.c (pass 2) Severity: Info (defense-in-depth) Β· Bucket: base:dev Β· Status: untested (no AMD GPU on the verification guest; Phase V not applicable for an Info finding)

What the code does

  1. amdgpu_atomfirmware_allocate_fb_scratch() parses the VBIOS vram_usagebyfirmware table and takes used_by_driver_in_kb (u16, fully VBIOS-controlled) as the size of the ATOM interpreter scratch buffer:
  1. The only caller drops the return value on the floor:
  1. Unguarded consumers of ctx->scratch (no NULL check, no size check):

Impact analysis β€” why this is Info on DragonFly

  • On Linux-semantics allocators (kzalloc returns NULL): allocation failure β†’ -ENOMEM ignored β†’ ctx->scratch == NULL, scratch_size_bytes == 0 β†’ next DP AUX / hw-I2C transaction writes to (NULL+4) / reads from NULL β†’ kernel NULL-page fault panic. The interpreter itself is guarded (atom.c:273, atom.c:529 check scratch_size_bytes), so only the dp/i2c direct consumers fault.
  • On DragonFly: GFP_KERNEL maps to M_WAITOK | M_CACHEALIGN (sys/dev/drm/include/linux/gfp.h:40) β€” no M_NULLOK. DF kmalloc cannot return NULL for a M_WAITOK request; on kernel-map exhaustion the allocator itself panics (sys/kern/kern_slaballoc.c:1723-1724: panic("kmem_slab_alloc(): kernel_map ran out of space!")). Therefore :107-108 is dead code on DF and the NULL-write chain cannot occur here. Residual DF exposure: a crafted VBIOS can force a 64 MB M_WAITOK kernel allocation at attach (or the allocator's panic under kernel-map pressure) β€” defense-in-depth only, no demonstrated unprivileged impact.
  • Upstream status: the whole file is byte-identical to Linux v5.0, and upstream master still ignores the return (amdgpu_atombios.c:1935 on master, checked 2026-09-25). The unclamped untrusted size is likewise unfixed upstream.

Fix

Authoritative diff: fix.diff β€” clamp the untrusted size to the driver's own default (20 KB) and propagate the error from both branches of amdgpu_atombios_init().

Why no guest run

Info-grade finding; the DF verification guest has no AMD GPU (no amdgpu device attaches), and on DF the defective branch is provably dead (M_WAITOK semantics above). Nothing to build or run β€” analysis-only evidence.

VERDICT.md
↓ download raw

DF-4980 VERDICT

Status: untested Β· Impact: none (on DragonFly) Β· Confidence: likely (certainty of the code defect is high β€” the line-by-line chain is proven below; "likely" reflects that the exploitable manifestation requires Linux-allocator semantics or DF kernel-map exhaustion).

The defect (proven from source)

amdgpu_atomfirmware_allocate_fb_scratch() sizes a kernel allocation from an untrusted VBIOS field and returns -ENOMEM on failure (sys/dev/drm/amd/amdgpu/amdgpu_atomfirmware.c:99,106-108); the sole caller ignores the return (sys/dev/drm/amd/amdgpu/amdgpu_atombios.c:2046), and two consumers use atom_context->scratch without any NULL guard (sys/dev/drm/amd/amdgpu/atombios_dp.c:74-76 β€” write via copy_swap to scratch + 1; sys/dev/drm/amd/amdgpu/atombios_i2c.c:54,104 β€” read). The legacy twin has the identical shape (amdgpu_atombios.c:1781-1822, ignored at :2049) and is the reachable instance on classic ASICs (dce8/10/11 use atombios_dp/atombios_i2c by default).

Why the impact ceiling on DragonFly is "none"

  1. kzalloc(size, GFP_KERNEL) β†’ kmalloc(size, M_DRM, M_WAITOK|M_CACHEALIGN|M_ZERO) (sys/dev/drm/include/linux/slab.h:35, sys/dev/drm/include/linux/gfp.h:40). DF M_WAITOK without M_NULLOK never yields NULL β€” failure panics inside the allocator (sys/kern/kern_slaballoc.c:1721-1727). So if (!ctx->scratch) at amdgpu_atomfirmware.c:107 is dead code on DF; the NULL+4 write chain (real on Linux) cannot execute here.
  2. The atom-interpreter FB paths are guarded by scratch_size_bytes (sys/dev/drm/amd/amdgpu/atom.c:273-278,529-533), so no interpreter path derefs the buffer unguarded.
  3. On the atomfw (Vega10+) path the unguarded dp/i2c consumers are not wired in this tree (no dce12 classic CRTC file exists; Vega10 classic falls back to dce_virtual which creates no DP/hw-I2C connectors; DC uses its own AUX). The reachable instance is the legacy twin on dce8/10/11 ASICs.
  4. Residual DF exposure: crafted VBIOS used_by_driver_in_kb = 0xFFFF forces a 64 MB M_WAITOK allocation at attach β€” succeeds on a healthy system, panics via kmem_slab_alloc only under kernel-map exhaustion, or blocks indefinitely under memory pressure. Defense-in-depth fix warranted, no demonstrated unprivileged trigger.

Reproduction

Not applicable: the DF verification guest has no AMD GPU (amdgpu never attaches), and the defective branch is provably dead on DF (point 1). A Linux reproduction would be: classic ASIC (e.g. polaris), fail the kzalloc (fault injection / memory pressure), hotplug a DP monitor β†’ panic at amdgpu_atombios_copy_swap writing to (NULL+4).

Fix validation

fix_status: not_testable β€” fix.diff clamps the untrusted size to the driver's own 20 KB default and propagates the error from both branches of amdgpu_atombios_init(). No kernel-build validation was performed (Info finding, no guest amdgpu device; the change is error-plumbing only and cannot be exercised on the guest).

Cross-references

  • DF-2027 (pass 1, same file): all table parsers deref unvalidated bios + data_offset β€” the offset OOB class. This finding is the ignored-error / untrusted-size class, distinct and not a re-report.
  • DF-4884/DF-4885: amdgpu_bios double-free + off-by-one (context).
  • Upstream: file byte-identical to Linux v5.0; master still ignores the return (amdgpu_atombios.c:1935 as of 2026-09-25).

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 validation not applicable: Info-grade finding with no reproducible impact on the verification guest (no AMD GPU; DF allocator semantics make the NULL branch dead). fix.diff is error-propagation + untrusted-size clamping authored from line-accurate source reading, never applied to sys/.

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

Confirmed kernel references

Detail

Evidence (decisive lines)

findings/poc/DF-4980/VERDICT.md (line-proven chain) + README.md; decisive lines: amdgpu_atomfirmware.c:99,106-108; amdgpu_atombios.c:2046,2049; atombios_dp.c:74-76; atombios_i2c.c:54,104; gfp.h:40; slab.h:35; kern_slaballoc.c:1721-1727

PoC changes

n/a β€” analysis-only evidence pack; no PoC built (guest has no AMD GPU; the defective branch is provably dead on DF because GFP_KERNEL == M_WAITOK without M_NULLOK)

Verified recommended fix

Clamp the untrusted usage_bytes to the driver's own 20 KB default and check the return value of both allocate_fb_scratch variants in amdgpu_atombios_init()

Verdict

amdgpu_atomfirmware_allocate_fb_scratch() sizes kzalloc(GFP_KERNEL) from the untrusted VBIOS field used_by_driver_in_kb (up to 64 MB, unclamped) and its -ENOMEM return is ignored by the only caller (amdgpu_atombios.c:2046; legacy twin :2049), leaving consumers that dereference atom_context->scratch without any NULL guard (atombios_dp.c:74-76 write to scratch+1, atombios_i2c.c:54/104). On Linux-semantics allocators that failure is a NULL-page write panic; on DragonFly GFP_KERNEL maps to M_WAITOK without M_NULLOK (gfp.h:40) so kzalloc can never return NULL and the !ctx->scratch branch (amdgpu_atomfirmware.c:107) is dead code β€” failure instead surfaces as the allocator's own panic under kernel-map exhaustion with a VBIOS-influenced 64 MB request. Info/defense-in-depth: no demonstrated unprivileged impact on DF; unfixed upstream (master amdgpu_atombios.c:1935 still ignores the return).