NULL pointer dereference / use-after-close in kernel space (memory safety issue)

HIGH
torvalds/linux
Commit: c69dbbf02127
Affected: Pre-7.0-rc6 (i.e., 7.0-rc5 and earlier) in the AMD XDNA driver; fixed in 7.0-rc6.
2026-07-17 16:29 UTC

Description

The commit fixes a potential NULL pointer dereference in the AMD XDNA GEM/BO handling path. After a BO handle is closed, abo->client may be cleared to NULL while the underlying GEM object can still be referenced by the kernel. Code paths that execute after the BO close could dereference abo->client (e.g., abo->client->xdna), leading to a NULL pointer dereference and possible kernel OOPS. The patch eliminates dereferencing abo->client after close by obtaining the device context via the object's gobj dev (to_xdna_dev(to_gobj(abo)->dev)) and by guarding access patterns in relevant helpers (e.g., amdxdna_gem_vmap, amdxdna_dev_offset calculations, and dma address handling). It also adds protective comments and guards in the HMM registration path to avoid using abo->client when the resource is already detached, relying on mem.dma_addr or UVA paths instead.

Proof of Concept

PoC (conceptual repro in a controlled lab): Prerequisites: - A kernel build containing the vulnerable AMD XDNA driver (pre-patch) and a user-space driver/test harness capable of interacting with the DRM/XDNA interface. - A test machine with a compatible AMD GPU and a crash-safe test environment. Steps to reproduce (race/use-after-close scenario): 1) Open the XDNA DRM device and allocate a GEM BO (abo) that will be used for an execution request. Keep the abo handle alive for the duration of command submission. 2) Submit an execution request that references the BO (code path that uses abo->client->xdna to obtain device context). This queues work that will be executed by the kernel/NPU. 3) While the work is still pending, close or release the BO handle from user-space, which clears abo->client but lets internal kernel references to the GEM object remain. 4) Wait for the kernel to process the queued exec (or allow it to complete). Because abo->client has been cleared, the kernel could dereference a NULL abo->client in the post-close path, leading to a NULL pointer dereference and a kernel OOPS/crash. 5) Observe the system crash/dump; analyze with crash/kdump to confirm NULL dereference in the AMD XDNA driver code path. Notes: - This relies on a window where a single command submission is in-flight while the BO handle is closed, exploiting the use-after-close condition described in the commit. - Modern kernels with the patch applied should not dereference abo->client after close; the PoC should fail on patched kernels. If you want a minimal code sketch (pseudo-C, non-functional): #include <stdio.h> #include <fcntl.h> #include <unistd.h> int main() { int fd = open("/dev/xdna", O_RDWR); if (fd < 0) return 1; int abo = create_amdxdna_bo(fd, /*size*/ 4096); if (abo < 0) return 1; // Queue an execution that uses the BO submit_amdxdna_exec(fd, abo, /*cmd_list*/ NULL); // Immediately drop/close the BO handle close_amdxdna_bo(fd, abo); // Optional: wait for completion or crash occurs on the driver side sleep(1); close(fd); return 0; } Note: The function names are indicative; the exact IOCTLs/kernel entry points depend on the DRM/XDNA interface. The PoC aims to trigger a path where abo->client is NULL after close and still dereferenced by a post-close code path. In a patched kernel, this should not crash.

Commit Details

Author: Lizhi Hou

Date: 2026-07-07 20:15 UTC

Message:

accel/amdxdna: Fix potential NULL pointer dereference of abo->client Closing a BO handle clears abo->client, while the underlying GEM object may remain alive due to internal kernel references. As a result, code executed after the BO handle is closed may dereference a NULL abo->client pointer. Remove accesses to abo->client from code paths that may execute after the BO handle has been closed. Fixes: d76856beb4a4 ("accel/amdxdna: Refactor GEM BO handling and add helper APIs for address retrieval") Reviewed-by: Max Zhen <max.zhen@amd.com> Signed-off-by: Lizhi Hou <lizhi.hou@amd.com> Link: https://patch.msgid.link/20260707201556.562191-1-lizhi.hou@amd.com

Triage Assessment

Vulnerability Type: Memory safety (NULL pointer dereference)

Confidence: HIGH

Reasoning:

The commit fixes potential NULL pointer dereferences of abo->client after a BO handle is closed, which could lead to crashes or kernel instability. It replaces dereferences with safe access patterns and guards against use-after-close scenarios, addressing a memory-safety vulnerability.

Verification Assessment

Vulnerability Type: NULL pointer dereference / use-after-close in kernel space (memory safety issue)

Confidence: HIGH

Affected Versions: Pre-7.0-rc6 (i.e., 7.0-rc5 and earlier) in the AMD XDNA driver; fixed in 7.0-rc6.

Code Diff

diff --git a/drivers/accel/amdxdna/aie2_message.c b/drivers/accel/amdxdna/aie2_message.c index c4b364801cc045..dfe0fbdf066d2c 100644 --- a/drivers/accel/amdxdna/aie2_message.c +++ b/drivers/accel/amdxdna/aie2_message.c @@ -840,7 +840,7 @@ static struct aie2_exec_msg_ops npu_exec_message_ops = { static int aie2_init_exec_req(void *req, struct amdxdna_gem_obj *cmd_abo, size_t *size, u32 *msg_op) { - struct amdxdna_dev *xdna = cmd_abo->client->xdna; + struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(cmd_abo)->dev); int ret; u32 op; @@ -874,7 +874,7 @@ static int aie2_cmdlist_fill_slot(void *slot, struct amdxdna_gem_obj *cmd_abo, size_t *size, u32 *cmd_op) { - struct amdxdna_dev *xdna = cmd_abo->client->xdna; + struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(cmd_abo)->dev); int ret; u32 op; diff --git a/drivers/accel/amdxdna/amdxdna_gem.c b/drivers/accel/amdxdna/amdxdna_gem.c index 1275f91ca70508..4628a27872656f 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.c +++ b/drivers/accel/amdxdna/amdxdna_gem.c @@ -198,6 +198,7 @@ amdxdna_gem_destroy_obj(struct amdxdna_gem_obj *abo) */ void *amdxdna_gem_vmap(struct amdxdna_gem_obj *abo) { + struct amdxdna_dev *xdna = to_xdna_dev(to_gobj(abo)->dev); struct iosys_map map = IOSYS_MAP_INIT_VADDR(NULL); int ret; @@ -210,7 +211,7 @@ void *amdxdna_gem_vmap(struct amdxdna_gem_obj *abo) if (!abo->mem.kva) { ret = drm_gem_vmap(to_gobj(abo), &map); if (ret) - XDNA_ERR(abo->client->xdna, "Vmap bo failed, ret %d", ret); + XDNA_ERR(xdna, "Vmap bo failed, ret %d", ret); else abo->mem.kva = map.vaddr; } @@ -354,7 +355,13 @@ static int amdxdna_hmm_register(struct amdxdna_gem_obj *abo, unsigned long nr_pages; int ret; - if (!amdxdna_pasid_on(abo->client)) { + /* + * When PASID is off, amdxdna_gem_obj_open() called amdxdna_dma_map_bo() + * and mem.dma_addr is valid; use the DMA address directly and skip HMM. + * Avoid dereferencing abo->client which may be NULL (cleared in close()) + * while internal kernel references are still held. + */ + if (abo->mem.dma_addr != AMDXDNA_INVALID_ADDR) { /* Need to set uva for heap uva validation */ abo->mem.uva = addr; return 0; diff --git a/drivers/accel/amdxdna/amdxdna_gem.h b/drivers/accel/amdxdna/amdxdna_gem.h index a35d2f15d32c79..1e90e32bf3cd79 100644 --- a/drivers/accel/amdxdna/amdxdna_gem.h +++ b/drivers/accel/amdxdna/amdxdna_gem.h @@ -88,12 +88,19 @@ u64 amdxdna_gem_dev_addr(struct amdxdna_gem_obj *abo); static inline u64 amdxdna_dev_bo_offset(struct amdxdna_gem_obj *abo) { - return amdxdna_gem_dev_addr(abo) - abo->client->xdna->dev_info->dev_mem_base; + return amdxdna_gem_dev_addr(abo) - to_xdna_dev(to_gobj(abo)->dev)->dev_info->dev_mem_base; } static inline u64 amdxdna_obj_dma_addr(struct amdxdna_gem_obj *abo) { - return amdxdna_pasid_on(abo->client) ? amdxdna_gem_uva(abo) : abo->mem.dma_addr; + /* + * amdxdna_gem_obj_open() calls amdxdna_dma_map_bo() only when PASID is + * off, leaving mem.dma_addr at AMDXDNA_INVALID_ADDR when PASID is on. + * Avoid dereferencing abo->client, which is cleared to NULL by + * amdxdna_gem_obj_close() while internal kernel references remain. + */ + return (abo->mem.dma_addr != AMDXDNA_INVALID_ADDR) ? + abo->mem.dma_addr : amdxdna_gem_uva(abo); } void amdxdna_umap_put(struct amdxdna_umap *mapp);
← Back to Alerts View on GitHub →