DF-4430 / verdict.json
{ "finding_id": "DF-4430", "status": "untested", "attempts": 0, "reproduced": 0, "confidence": "likely", "impact": "PRIME-imported (ttm_bo_type_sg) BOs on radeon: populate() ignores ttm->sg (slave branch #ifdef DUMBBELL_WIP, radeon_ttm.c:753-764) and allocates fresh pool pages that get bound into the GART \u2014 the GPU view of an imported dma-buf diverges from the exporters memory (silent data corruption); unpopulate()s live slave early-return (radeon_ttm.c:808,818-819) then never frees those pages or their ttm_mem_global accounting, so repeated import/destroy cycles leak wired kernel memory until GPU allocations fail (DoS). Sibling amdgpu_ttm.c:1268-1330 implements the slave path live. Reachability: cross-driver dma-buf import with radeon as importer (multi-GPU); same-driver self-import unaffected.", "verdict": "source-verified divergence/leak; untested on the audit guest (no AMD GPU, no second DRM device \u2014 env.txt). DF-4018/4019 precedent. Low: rare multi-GPU config, unprivileged within it.", "exploit_chain": [ "process with both card nodes open exports a dma-buf from card0 (DRM_IOCTL_PRIME_HANDLE_TO_FD)", "imports it on the radeon card (DRM_IOCTL_PRIME_FD_TO_HANDLE -> radeon_gem_prime_import_sg_table -> ttm_bo_type_sg)", "first validate/move populates the tt: radeon_ttm.c:779 ttm_pool_populate allocates fresh pages ignoring ttm->sg -> GART binds wrong pages (importer reads zeros; GPU writes never reach exporter)", "GEM_CLOSE destroys the BO: ttm_tt_destroy -> radeon_ttm_tt_unpopulate -> slave early-return -> pages+accounting leak; loop N times for unbounded wired-memory growth" ], "evidence": "README.md (chain, path:line), VERDICT.md, poc_prime_import.c (runnable on multi-GPU DFly box), fix.diff (un-ifdefs the slave populate branch, amdgpu-style), env.txt (guest hardware absence)", "kernel_refs": [ "sys/dev/drm/radeon/radeon_ttm.c:753", "sys/dev/drm/radeon/radeon_ttm.c:779", "sys/dev/drm/radeon/radeon_ttm.c:808", "sys/dev/drm/radeon/radeon_prime.c:73", "sys/dev/drm/ttm/ttm_tt.c:71", "sys/dev/drm/ttm/ttm_tt.c:215", "sys/dev/drm/ttm/ttm_page_alloc.c:890", "sys/dev/drm/amd/amdgpu/amdgpu_ttm.c:1268" ], "poc_changes": "authored fresh (no seed sketch). Compile-verified on the audit guest with -I uapi kernel include paths and -D__user= (build.log). Runtime blocked: guest has neither radeon hardware nor a second DRM device.", "build_cmd": "cc -O2 -o poc_prime_import poc_prime_import.c", "run_cmd": "DRM_EXPORT=/dev/dri/card0 DRM_IMPORT=/dev/dri/card1 ./poc_prime_import (needs radeon as importer)", "code_hash": "fc964efbcd8f6de62fc3c5f18c281baf01599247261c0e5f8a62ad9e3e4d94c8 (poc_prime_import.c)", "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 x86_64 (no /dev/dri \u2014 radeon never attaches)", "runtime_sec": 0, "guest_dirty": 0, "notes": "Hardware-gated (no AMD GPU in audit guest; additionally needs a 2nd DRM device as exporter). Sibling amdgpu driver implements the path live, confirming unfinished-port divergence rather than upstream behavior.", "recommended_fix": "Un-ifdef the slave branch of radeon_ttm_tt_populate (fill ttm->pages/dma_address from ttm->sg via drm_prime_sg_to_page_addr_arrays, as amdgpu_ttm.c:1268+ does); the existing slave early-return in unpopulate then becomes correct.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff un-ifdefs the slave populate branch; cannot compile/load on the audit guest (no radeon hardware, no multi-GPU), so fix validation is not_testable here.", "fix_evidence": "fix.diff; VERDICT.md Fix validation section" } |