Cross-device PRIME import panics unconditionally: dma_buf_attach() stub returns NULL (not ERR_PTR), IS_ERR misses it, and the fail_detach unwind calls dma_buf_detach() which is hardcoded panic()
Summary
The attach stub returns NULL so the IS_ERR guard passes; map_attachment(NULL) returns -EINVAL; the unwind calls the detach stub which is panic(). Any foreign dma-buf imported through radeon's generic helper or amdgpu's fallthrough panics the kernel - unpriv cross-GPU export/import on hybrid systems is an instant panic (DoS). VERIFIED via KLD replay with shipped symbols: deterministic panic with syscall trace. i915's private import dodges the panic but creates a zombie object (noted). ERR_PTR(-ENOSYS) + non-panicking detach fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4516 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.9 KB | β raw | |
| VERDICT.md | β | 3.6 KB | β raw | |
| df4515mod.c | β | 7.0 KB | view raw | |
| Makefile | β | 231 B | β download | |
| build.sh | β | 127 B | view raw | |
| run.sh | β | 178 B | view raw | |
| run.log | β | 865 B | view raw | |
| panic.txt | β | 578 B | view raw | |
| fix_run.log | β | 528 B | view raw | |
| fix.diff | β | 794 B | view raw | |
| env.txt | β | 611 B | view raw | |
| verdict.json | β | 3.4 KB | view raw |
DF-4516 β cross-device PRIME import panics the kernel via dma_buf_detach stub
What
drm_gem_prime_import_dev() (sys/dev/drm/drm_prime.c:709-762) attaches with
dma_buf_attach() (drm_prime.c:733). On this port dma_buf_attach() is a stub that
returns NULL, not ERR_PTR (sys/dev/drm/include/linux/dma-buf.h:96-101), so the
IS_ERR(attach) guard at drm_prime.c:734 does not fire. The code proceeds, the subsequent
dma_buf_map_attachment(NULL, β¦) returns -EINVAL (linux_dma-buf.c:196-197), and the
error unwind fail_detach: calls dma_buf_detach() (drm_prime.c:758) β which is
hardcoded to panic("dma_buf_attach is not implemented") (dma-buf.h:125-130).
Any foreign dma-buf (i.e. exported by another DRM device) imported through a driver
that uses the generic import helper β radeon (radeon_drv.c:699) and amdgpu
(amdgpu_prime.c:445-458 falls through to drm_gem_prime_import) β instantly panics the
kernel. i915 has its own copy (i915_gem_dmabuf.c:305-321) that swallows the NULL attach
and instead creates a zombie object with import_attach == NULL.
Files (shared trigger with DF-4515)
df4515mod.cβsysctl hw.df4515mod.fire=6runs exactly drm_prime.c:733β739β758run.log,panic.txtβ baseline:panic: dma_buf_attach is not implementedfix_run.logβ after fix.diff:dma_buf_attach -> ERR_PTR(-ENOSYS), IS_ERR=1, no panicfix.diffβ stub returnsERR_PTR(-ENOSYS); detach stops panicking
Reproduce (as root in the guest)
cd /usr/src/sys/dev/drm/drm && make obj && make cd /root/df4515 && make kldload /usr/obj/usr/src/sys/dev/drm/drm/drm.ko kldload df4515mod.ko sysctl hw.df4515mod.fire=6 # EXPECT: panic: dma_buf_attach is not implemented
Note: the real entry (DRM_IOCTL_PRIME_FD_TO_HANDLE with a foreign prime fd) requires two DRM devices; the QEMU guest has none, so the PoC drives the identical call sequence through the shipped drm.ko symbols.
DF-4516 VERDICT β REPRODUCED (guaranteed kernel panic on cross-device PRIME import)
Bottom line
Importing a dma-buf exported by another DRM device through the generic PRIME import
helper executes panic() unconditionally on this port. The chain, every step traced in
source and executed on the guest against the shipped drm.ko:
DRM_IOCTL_PRIME_FD_TO_HANDLE(unprivileged with /dev/dri/cardN open β video group) βdrm_gem_prime_fd_to_handle(drm_prime.c:792)- β driver
gem_prime_importβ genericdrm_gem_prime_import_dev(drm_prime.c:709) - β
attach = dma_buf_attach(dma_buf, attach_dev)(drm_prime.c:733) β the port's stub returns NULL (dma-buf.h:96-101);IS_ERR(NULL)is false, so the guard at :734 passes - β
dma_buf_map_attachment(NULL, DMA_BIDIRECTIONAL)(drm_prime.c:739) returnsERR_PTR(-EINVAL)(linux_dma-buf.c:196-197) - β
goto fail_detachβdma_buf_detach(dma_buf, attach)(drm_prime.c:758) βpanic("dma_buf_attach is not implemented")(dma-buf.h:129)
Guest proof (no GPU on the guest β entry-level precondition documented)
drm.ko built from the guest's /usr/src and loaded; KLD executes steps 3-5 with the real
module symbols. Observed (run.log, panic.txt):
dma_buf_attach: stub df4515mod: [6] dma_buf_attach -> 0 IS_ERR=0 <- NULL, IS_ERR blind (drm_prime.c:734) dma_buf_map_attachment: stub df4515mod: [6] dma_buf_map_attachment -> -22 <- -EINVAL (linux_dma-buf.c:196) df4515mod: [6] goto fail_detach -> dma_buf_detach (drm_prime.c:758): expect panic panic: dma_buf_attach is not implemented sysctl_fire() <- sysctl_root() <- userland_sysctl() <- sys___sysctl() <- syscall2()
Deterministic; reproduced first try.
Reachability
- radeon:
.gem_prime_import = drm_gem_prime_import(radeon_drv.c:699) β direct. - amdgpu:
amdgpu_gem_prime_importfalls through todrm_gem_prime_importfor any non-self dmabuf (amdgpu_prime.c:445-458). - Trigger requires two DRM devices (export on A, import into B) β e.g. any hybrid i915+radeon/amdgpu laptop or dual-GPU box, or a foreign exporter appearing later. Single-GPU self-import takes the early-return path (drm_prime.c:718-728) and does not reach the stub.
- i915's own import (i915_gem_dmabuf.c:284-334) dodges the panic (
#if 0around its fail_detach) but instead succeeds withimport_attach == NULLand no dma_buf reference β a zombie object whose first page-fetch fails; separate defect, same root.
Fix validation
fix.diff (dma-buf.h): dma_buf_attach returns ERR_PTR(-ENOSYS) (caught by the
existing IS_ERR at drm_prime.c:734 β clean -ENOSYS ioctl failure) and dma_buf_detach
no longer panics. Applied to guest /usr/src, rebuilt, re-run (fix_run.log):
dma_buf_attach: not implemented df4515mod: [6] dma_buf_attach -> 0xffffffffffffffb2 IS_ERR=1 <- caught, no panic
Guest kept running. fix_status = fixed.
References
sys/dev/drm/drm_prime.c:709-762 (:733, :734, :739, :742-743, :758) sys/dev/drm/include/linux/dma-buf.h:96-101 (NULL stub), :125-130 (panic stub) sys/dev/drm/linux_dma-buf.c:189-207 (map_attachment EINVAL on NULL attach) sys/dev/drm/radeon/radeon_drv.c:699, sys/dev/drm/amd/amdgpu/amdgpu_prime.c:445-458, sys/dev/drm/amd/amdgpu/amdgpu_drv.c:1328 (generic import wiring)
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
fixedfix.diff applied to guest /usr/src (dma-buf.h), drm.ko + module rebuilt, identical PoC step 6 re-run: dma_buf_attach returned ERR_PTR(-ENOSYS) with IS_ERR=1 (0xffffffffffffffb2), the sequence broke out cleanly before map/detach, no panic, guest stayed up. Baseline panic gone.
findings/poc/DF-4516/fix_run.log vs run.log + panic.txt
Confirmed kernel references
Detail
Exploit chain
Requires two DRM devices (foreign dma-buf): export a GEM object on GPU A via PRIME_HANDLE_TO_FD, pass the fd to a process with GPU B's card open (radeon or amdgpu), issue DRM_IOCTL_PRIME_FD_TO_HANDLE -> dma_buf_attach NULL -> map -EINVAL -> fail_detach -> panic. Pure DoS (panic), no escalation path.
Evidence (decisive lines)
run.log ('dma_buf_attach -> 0 IS_ERR=0', 'map_attachment -> -22', 'goto fail_detach'), panic.txt ('panic: dma_buf_attach is not implemented' with syscall trace), fix_run.log ('0xffffffffffffffb2 IS_ERR=1', no panic)
PoC changes
Shared trigger module with DF-4515; step 6 replays drm_prime.c:733->739->758. The real entry needs two GPUs (guest has none) - documented in VERDICT.md.
Verified recommended fix
dma_buf_attach() stub must return ERR_PTR(-ENOSYS) instead of NULL, and dma_buf_detach() must not panic(); drm_prime.c:734's existing IS_ERR check then yields a clean ioctl failure.
Verdict
drm_prime.c's import helper attaches via dma_buf_attach() (drm_prime.c:733) which on this port is a stub returning NULL instead of ERR_PTR (dma-buf.h:96-101); IS_ERR(NULL) does not catch it, dma_buf_map_attachment(NULL) fails with -EINVAL, and the fail_detach unwind (drm_prime.c:758) calls dma_buf_detach() which is hardcoded panic('dma_buf_attach is not implemented'). Any unprivileged video-group user importing a foreign dma-buf on radeon/amdgpu (generic import helper) panics the kernel. Proven on the guest with the shipped drm.ko: the exact drm_prime.c:733-758 sequence panics deterministically; fix.diff (ERR_PTR(-ENOSYS) + no-panic detach) makes the same sequence fail cleanly with IS_ERR catching the error.
No comments yet.