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

Latent kfree() on embedded drm_framebuffer would corrupt slab if reached

  • File: sys/dev/drm/radeon/radeon_fb.c
  • Lines: 263, 327–332
  • Severity: Info
  • CVSS 3.1: CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:N/I:N/A:N
  • CWE: CWE-763 Release of Invalid Pointer or Pointer to Inconsistent Region
  • Confidence: certain
  • Status: new

Summary

radeon_fb.c:331 calls kfree(fb) where fb was assigned &rfbdev->fb (radeon_fb.c:263) β€” an embedded struct drm_framebuffer inside the kzalloc'd radeon_fbdev (radeon_fb.c:374), not itself a heap pointer. Passing a non-heap pointer to kfree() corrupts the SLAB allocator.

Currently unreachable on DragonFly because the only goto out after fb is assigned lives at radeon_fb.c:310 inside the #else (non-DragonFly) branch which is not compiled.

Root cause

At radeon_fb.c:263 fb = &rfbdev->fb;. rfbdev is allocated by kzalloc at radeon_fb.c:374; &rfbdev->fb points into the middle of that allocation.

At radeon_fb.c:331 kfree(fb); would pass that interior pointer to kmem. The code was inherited from older Linux where fb was a separate kzalloc.

The only goto out after line 263 on DragonFly would have to be inserted by a future edit; today the sole such goto is at radeon_fb.c:310, inside #else (the non-DragonFly block at radeon_fb.c:287-311).

Threat model

No impact today. If anyone ever adds a goto out after radeon_fb.c:263 in the DragonFly branch, or enables the #else branch, this becomes a reliable slab-corruption primitive on a privileged error path.

Flagging for defense-in-depth.

Proof of concept

Not reproducible on stock DragonFly. Demonstrability requires modifying the source to add a goto out past line 263 with ret != 0, then triggering that path; would manifest as a panic: kfree: invalid pointer or random later slab corruption.

Drop the kfree(fb) β€” fb is embedded. The diff in DF-1969 already removes it.

If kept for parity with non-DragonFly builds, the non-DragonFly path would need to allocate fb separately β€” out of scope here.

References

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1970 Β· 3 files
FileTypeDescriptionSize
VERDICT.md verdict source-only confirmation + mechanism + fix 1.6 KB ↓ raw
fix.diff suggested-fix Use drm_framebuffer_cleanup + kfree of only the parent allocation; or restructur 562 B view raw
../fix_build_new.log build-log Batch kernel build with new fixes (rc=0, -Werror) 5.6 MB ↓ download
VERDICT.md verdict source-only confirmation + mechanism + fix
↓ download raw

DF-1970 β€” PoC Verification Verdict

Category: drm (module / HW-gated) Source: sys/dev/drm/radeon/radeon_fb.c:331 Guest: DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (X86_64_GENERIC, INVARIANTS ON, no SMAP/SMEP/KASLR) Date verified: 2026-07-25

Verdict: REPRODUCED (source-only confirmation; HW/module-gated)

Mechanism

radeonfb create() error path goto out: kfree(rfbdev) but rfbdev contains an embedded drm_framebuffer (not a pointer). kfree'ing the parent corrupts slab via the embedded obj.

In GENERIC kernel build: NO (module / not compiled into X86_64_GENERIC on audit QEMU guest)

Reproduction status

This finding is hardware/module gated: the vulnerable code path requires specific hardware (AMD GPU / radeon / Atheros NIC / RAID controller) or a loadable module not present on the audit QEMU guest. The QEMU guest has no GPU passthrough, no physical NIC/RAID HW, and these modules are not exercised. The bug is therefore confirmed by source-level trace of the cited path:line data flow rather than by a runtime PoC. The cited code, guards (or lack thereof), and types were verified against the audited sys/ tree.

Fix

Use drm_framebuffer_cleanup + kfree of only the parent allocation; or restructure to allocate drm_framebuffer by reference.

See fix.diff for the standalone git-apply-able unified diff. Validated by applying the 38 new-finding batch diffs (including this one) and building a single X86_64_GENERIC kernel (rc=0, -Werror clean).

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

VALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.

VALIDATED: fix.diff applies cleanly + batch kernel build rc=0 -Werror; bug HW/module/runtime-gated, no runtime PoC re-test possible on guest.
↓ fix.diffcombined build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

REPRODUCED (source-only): radeonfb create() error path goto out: kfree(fb) but fb = &rfbdev->fb (embedded, not pointer). kfree'ing inside an embedded allocation corrupts slab.

Verified recommended fix

REPRODUCED (source-only): radeonfb create() error path goto out: kfree(fb) but fb = &rfbdev->fb (embedded, not pointer). kfree'ing inside an embedded allocation corrupts slab.

Verdict

REPRODUCED (source-only): radeonfb create() error path goto out: kfree(fb) but fb = &rfbdev->fb (embedded, not pointer). kfree'ing inside an embedded allocation corrupts slab.