CVE-2026-46224
Description
In the Linux kernel, the following vulnerability has been resolved:
drm/xe: Fix bo leak in xe_dma_buf_init_obj() on allocation failure
When drm_gpuvm_resv_object_alloc() fails, the pre-allocated storage bo is not freed. Add xe_bo_free(storage) before returning the error.
xe_dma_buf_init_obj() calls xe_bo_init_locked(), which frees the bo on error. Therefore, xe_dma_buf_init_obj() must also free the bo on its own error paths. Otherwise, since xe_gem_prime_import() cannot distinguish whether the failure originated from xe_dma_buf_init_obj() or from xe_bo_init_locked(), it cannot safely decide whether the bo should be freed.
Add comments documenting the ownership semantics: on success, ownership of storage is transferred to the returned drm_gem_object; on failure, storage is freed before returning.
v2: Add comments to explain the free logic.
(cherry picked from commit 78a6c5f899f22338bbf48b44fb8950409c5a69b9)
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
A memory leak in Linux kernel's drm/xe driver in xe_dma_buf_init_obj() when drm_gpuvm_resv_object_alloc() fails, leaving a pre-allocated bo unfreed.
Vulnerability
In the Linux kernel's drm/xe driver, the function xe_dma_buf_init_obj() allocates a storage bo and then calls drm_gpuvm_resv_object_alloc(). If that allocation fails, the pre-allocated storage bo is not freed, resulting in a memory leak. The vulnerability exists in kernel versions prior to the fix commit 78a6c5f899f2. The function xe_bo_init_locked() frees the bo on error, but xe_dma_buf_init_obj() did not handle its own error path correctly, leading to the leak.
Exploitation
An attacker must be able to trigger a failure in drm_gpuvm_resv_object_alloc() during the import of a DMA-buf object. This could be achieved by exhausting memory or other system resources, causing the allocation to fail. No special privileges are required beyond the ability to initiate a DMA-buf import operation, which may be available to unprivileged users depending on system configuration.
Impact
The vulnerability causes a memory leak of a bo object. Repeated exploitation could lead to resource exhaustion, resulting in denial of service (system instability or unavailability). There is no impact on confidentiality or integrity.
Mitigation
The issue is fixed in Linux kernel commit 78a6c5f899f2 [1]. Users should apply the patch or update to a kernel version that includes this commit. No workaround is documented.
AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected products
1Patches
7f9ad21b90162drm/xe: Fix bo leak in xe_dma_buf_init_obj() on allocation failure
1 file changed · +11 −2
drivers/gpu/drm/xe/xe_dma_buf.c+11 −2 modifieddiff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c index 2650c5abb36542..f9fe7ca32f6178 100644 --- a/drivers/gpu/drm/xe/xe_dma_buf.c +++ b/drivers/gpu/drm/xe/xe_dma_buf.c @@ -227,6 +227,13 @@ struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj, int flags) return buf; } +/* + * Takes ownership of @storage: on success it is transferred to the returned + * drm_gem_object; on failure it is freed before returning the error. + * This matches the contract of xe_bo_init_locked() which frees @storage on + * its error paths, so callers need not (and must not) free @storage after + * this call. + */ static struct drm_gem_object * xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, struct dma_buf *dma_buf) @@ -240,8 +247,10 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, int ret = 0; dummy_obj = drm_gpuvm_resv_object_alloc(&xe->drm); - if (!dummy_obj) + if (!dummy_obj) { + xe_bo_free(storage); return ERR_PTR(-ENOMEM); + } dummy_obj->resv = resv; xe_validation_guard(&ctx, &xe->val, &exec, (struct xe_val_flags) {}, ret) { @@ -250,6 +259,7 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, if (ret) break; + /* xe_bo_init_locked() frees storage on error */ bo = xe_bo_init_locked(xe, storage, NULL, resv, NULL, dma_buf->size, 0, /* Will require 1way or 2way for vm_bind */ ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec); -- cgit 1.3-korg
8fa8c2a22585drm/xe: Fix bo leak in xe_dma_buf_init_obj() on allocation failure
1 file changed · +11 −2
drivers/gpu/drm/xe/xe_dma_buf.c+11 −2 modifieddiff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c index 7c74a31d448602..43d1e01c801264 100644 --- a/drivers/gpu/drm/xe/xe_dma_buf.c +++ b/drivers/gpu/drm/xe/xe_dma_buf.c @@ -238,6 +238,13 @@ struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj, int flags) return buf; } +/* + * Takes ownership of @storage: on success it is transferred to the returned + * drm_gem_object; on failure it is freed before returning the error. + * This matches the contract of xe_bo_init_locked() which frees @storage on + * its error paths, so callers need not (and must not) free @storage after + * this call. + */ static struct drm_gem_object * xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, struct dma_buf *dma_buf) @@ -251,8 +258,10 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, int ret = 0; dummy_obj = drm_gpuvm_resv_object_alloc(&xe->drm); - if (!dummy_obj) + if (!dummy_obj) { + xe_bo_free(storage); return ERR_PTR(-ENOMEM); + } dummy_obj->resv = resv; xe_validation_guard(&ctx, &xe->val, &exec, (struct xe_val_flags) {}, ret) { @@ -261,6 +270,7 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, if (ret) break; + /* xe_bo_init_locked() frees storage on error */ bo = xe_bo_init_locked(xe, storage, NULL, resv, NULL, dma_buf->size, 0, /* Will require 1way or 2way for vm_bind */ ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec); -- cgit 1.3-korg
93a528f67ce5drm/xe: Fix bo leak in xe_dma_buf_init_obj() on allocation failure
1 file changed · +11 −2
drivers/gpu/drm/xe/xe_dma_buf.c+11 −2 modifieddiff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c index 7f9602b3363db1..c0937c090d33c3 100644 --- a/drivers/gpu/drm/xe/xe_dma_buf.c +++ b/drivers/gpu/drm/xe/xe_dma_buf.c @@ -258,6 +258,13 @@ out_unlock: return ERR_PTR(ret); } +/* + * Takes ownership of @storage: on success it is transferred to the returned + * drm_gem_object; on failure it is freed before returning the error. + * This matches the contract of xe_bo_init_locked() which frees @storage on + * its error paths, so callers need not (and must not) free @storage after + * this call. + */ static struct drm_gem_object * xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, struct dma_buf *dma_buf) @@ -271,8 +278,10 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, int ret = 0; dummy_obj = drm_gpuvm_resv_object_alloc(&xe->drm); - if (!dummy_obj) + if (!dummy_obj) { + xe_bo_free(storage); return ERR_PTR(-ENOMEM); + } dummy_obj->resv = resv; xe_validation_guard(&ctx, &xe->val, &exec, (struct xe_val_flags) {}, ret) { @@ -281,6 +290,7 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, if (ret) break; + /* xe_bo_init_locked() frees storage on error */ bo = xe_bo_init_locked(xe, storage, NULL, resv, NULL, dma_buf->size, 0, /* Will require 1way or 2way for vm_bind */ ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec); -- cgit 1.3-korg
78a6c5f899f2drm/xe: Fix bo leak in xe_dma_buf_init_obj() on allocation failure
1 file changed · +11 −1
drivers/gpu/drm/xe/xe_dma_buf.c+11 −1 modified@@ -258,6 +258,13 @@ struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj, int flags) return ERR_PTR(ret); } +/* + * Takes ownership of @storage: on success it is transferred to the returned + * drm_gem_object; on failure it is freed before returning the error. + * This matches the contract of xe_bo_init_locked() which frees @storage on + * its error paths, so callers need not (and must not) free @storage after + * this call. + */ static struct drm_gem_object * xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, struct dma_buf *dma_buf) @@ -271,8 +278,10 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, int ret = 0; dummy_obj = drm_gpuvm_resv_object_alloc(&xe->drm); - if (!dummy_obj) + if (!dummy_obj) { + xe_bo_free(storage); return ERR_PTR(-ENOMEM); + } dummy_obj->resv = resv; xe_validation_guard(&ctx, &xe->val, &exec, (struct xe_val_flags) {}, ret) { @@ -281,6 +290,7 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, if (ret) break; + /* xe_bo_init_locked() frees storage on error */ bo = xe_bo_init_locked(xe, storage, NULL, resv, NULL, dma_buf->size, 0, /* Will require 1way or 2way for vm_bind */ ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec);
8fa8c2a22585drm/xe: Fix bo leak in xe_dma_buf_init_obj() on allocation failure
1 file changed · +11 −2
drivers/gpu/drm/xe/xe_dma_buf.c+11 −2 modifieddiff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c index 7c74a31d448602..43d1e01c801264 100644 --- a/drivers/gpu/drm/xe/xe_dma_buf.c +++ b/drivers/gpu/drm/xe/xe_dma_buf.c @@ -238,6 +238,13 @@ struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj, int flags) return buf; } +/* + * Takes ownership of @storage: on success it is transferred to the returned + * drm_gem_object; on failure it is freed before returning the error. + * This matches the contract of xe_bo_init_locked() which frees @storage on + * its error paths, so callers need not (and must not) free @storage after + * this call. + */ static struct drm_gem_object * xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, struct dma_buf *dma_buf) @@ -251,8 +258,10 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, int ret = 0; dummy_obj = drm_gpuvm_resv_object_alloc(&xe->drm); - if (!dummy_obj) + if (!dummy_obj) { + xe_bo_free(storage); return ERR_PTR(-ENOMEM); + } dummy_obj->resv = resv; xe_validation_guard(&ctx, &xe->val, &exec, (struct xe_val_flags) {}, ret) { @@ -261,6 +270,7 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, if (ret) break; + /* xe_bo_init_locked() frees storage on error */ bo = xe_bo_init_locked(xe, storage, NULL, resv, NULL, dma_buf->size, 0, /* Will require 1way or 2way for vm_bind */ ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec); -- cgit 1.3-korg
93a528f67ce5drm/xe: Fix bo leak in xe_dma_buf_init_obj() on allocation failure
1 file changed · +11 −2
drivers/gpu/drm/xe/xe_dma_buf.c+11 −2 modifieddiff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c index 7f9602b3363db1..c0937c090d33c3 100644 --- a/drivers/gpu/drm/xe/xe_dma_buf.c +++ b/drivers/gpu/drm/xe/xe_dma_buf.c @@ -258,6 +258,13 @@ out_unlock: return ERR_PTR(ret); } +/* + * Takes ownership of @storage: on success it is transferred to the returned + * drm_gem_object; on failure it is freed before returning the error. + * This matches the contract of xe_bo_init_locked() which frees @storage on + * its error paths, so callers need not (and must not) free @storage after + * this call. + */ static struct drm_gem_object * xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, struct dma_buf *dma_buf) @@ -271,8 +278,10 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, int ret = 0; dummy_obj = drm_gpuvm_resv_object_alloc(&xe->drm); - if (!dummy_obj) + if (!dummy_obj) { + xe_bo_free(storage); return ERR_PTR(-ENOMEM); + } dummy_obj->resv = resv; xe_validation_guard(&ctx, &xe->val, &exec, (struct xe_val_flags) {}, ret) { @@ -281,6 +290,7 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, if (ret) break; + /* xe_bo_init_locked() frees storage on error */ bo = xe_bo_init_locked(xe, storage, NULL, resv, NULL, dma_buf->size, 0, /* Will require 1way or 2way for vm_bind */ ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec); -- cgit 1.3-korg
f9ad21b90162drm/xe: Fix bo leak in xe_dma_buf_init_obj() on allocation failure
1 file changed · +11 −2
drivers/gpu/drm/xe/xe_dma_buf.c+11 −2 modifieddiff --git a/drivers/gpu/drm/xe/xe_dma_buf.c b/drivers/gpu/drm/xe/xe_dma_buf.c index 2650c5abb36542..f9fe7ca32f6178 100644 --- a/drivers/gpu/drm/xe/xe_dma_buf.c +++ b/drivers/gpu/drm/xe/xe_dma_buf.c @@ -227,6 +227,13 @@ struct dma_buf *xe_gem_prime_export(struct drm_gem_object *obj, int flags) return buf; } +/* + * Takes ownership of @storage: on success it is transferred to the returned + * drm_gem_object; on failure it is freed before returning the error. + * This matches the contract of xe_bo_init_locked() which frees @storage on + * its error paths, so callers need not (and must not) free @storage after + * this call. + */ static struct drm_gem_object * xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, struct dma_buf *dma_buf) @@ -240,8 +247,10 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, int ret = 0; dummy_obj = drm_gpuvm_resv_object_alloc(&xe->drm); - if (!dummy_obj) + if (!dummy_obj) { + xe_bo_free(storage); return ERR_PTR(-ENOMEM); + } dummy_obj->resv = resv; xe_validation_guard(&ctx, &xe->val, &exec, (struct xe_val_flags) {}, ret) { @@ -250,6 +259,7 @@ xe_dma_buf_init_obj(struct drm_device *dev, struct xe_bo *storage, if (ret) break; + /* xe_bo_init_locked() frees storage on error */ bo = xe_bo_init_locked(xe, storage, NULL, resv, NULL, dma_buf->size, 0, /* Will require 1way or 2way for vm_bind */ ttm_bo_type_sg, XE_BO_FLAG_SYSTEM, &exec); -- cgit 1.3-korg
Vulnerability mechanics
Root cause
"Missing xe_bo_free(storage) call in xe_dma_buf_init_obj() when drm_gpuvm_resv_object_alloc() fails, causing a memory leak of the pre-allocated buffer object."
Attack vector
An attacker triggers the bug by causing `drm_gpuvm_resv_object_alloc()` to fail inside `xe_dma_buf_init_obj()` during a DMA-buf import operation. This can be achieved by exhausting kernel memory or by inducing an allocation failure in the DRM GPU-VM reservation object allocator. When the allocation fails, the function returns `-ENOMEM` without freeing the previously allocated `storage` bo, resulting in a memory leak. Repeated exploitation can exhaust system memory, leading to denial of service.
Affected code
The vulnerability is in `xe_dma_buf_init_obj()` in `drivers/gpu/drm/xe/xe_dma_buf.c`. When `drm_gpuvm_resv_object_alloc()` fails, the pre-allocated `storage` bo was not freed, causing a memory leak. The caller `xe_gem_prime_import()` cannot distinguish whether the failure came from `xe_dma_buf_init_obj()` or from `xe_bo_init_locked()`, so it cannot safely free the bo itself.
What the fix does
The patch adds `xe_bo_free(storage)` before the `return ERR_PTR(-ENOMEM)` when `drm_gpuvm_resv_object_alloc()` fails, ensuring the pre-allocated buffer object is freed on that error path. It also adds a comment block documenting the ownership semantics: on success ownership of `storage` is transferred to the returned `drm_gem_object`; on failure `storage` is freed before returning. A second inline comment clarifies that `xe_bo_init_locked()` itself frees `storage` on its own error paths, so callers must not double-free.
Preconditions
- inputThe attacker must be able to trigger a DMA-buf import operation that reaches xe_dma_buf_init_obj()
- inputThe attacker must cause drm_gpuvm_resv_object_alloc() to fail, e.g. by exhausting kernel memory
Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
3News mentions
0No linked articles in our index yet.