diff options
| author | Francois Dugast <francois.dugast@intel.com> | 2026-05-22 11:25:28 +0200 |
|---|---|---|
| committer | Francois Dugast <francois.dugast@intel.com> | 2026-05-29 13:44:00 +0200 |
| commit | 0251963afd22d1365d07bf81135ef0c694f6c3b5 (patch) | |
| tree | 7cd0866e8d54d751dcdb6f56832b14a675aecc00 /drivers | |
| parent | 1502b42819c2eee9658009ff0b7c9249078d7366 (diff) | |
| download | linux-next-0251963afd22d1365d07bf81135ef0c694f6c3b5.tar.gz linux-next-0251963afd22d1365d07bf81135ef0c694f6c3b5.zip | |
gpu/buddy: Fix use-after-free in split_block() call sites
When split_block() fails it returns before calling mark_split(), leaving
the block in the FREE state and still linked in the rbtree. The four
err_undo paths then call __gpu_buddy_free() without first removing the
block from the tree, which leads to two distinct bugs:
- If the buddy is also free, __gpu_buddy_free() merges the two siblings
by calling gpu_block_free(mm, block) while block->rb is still linked
in the tree. Any subsequent rbtree traversal will follow the now-
dangling pointer, causing a use-after-free.
- In alloc_from_freetree(), where there is no buddy guard,
__gpu_buddy_free() always reaches mark_free() -> rbtree_insert() with
block still in the tree, corrupting the rbtree.
The same pattern is already used correctly in __force_merge(): call
rbtree_remove() to unlink the block before handing it to
__gpu_buddy_free(). Apply the same fix to all four err_undo sites.
Reported-by: Sashiko <sashiko-bot@kernel.org>
Assisted-by: GitHub Copilot:claude-sonnet-4.6
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
Link: https://lore.kernel.org/r/20260522092600.32818-2-francois.dugast@intel.com
Signed-off-by: Francois Dugast <francois.dugast@intel.com>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/gpu/buddy.c | 16 |
1 files changed, 12 insertions, 4 deletions
diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c index eb1457376307..dac2027bb64a 100644 --- a/drivers/gpu/buddy.c +++ b/drivers/gpu/buddy.c @@ -737,8 +737,10 @@ err_undo: buddy = __get_buddy(block); if (buddy && (gpu_buddy_block_is_free(block) && - gpu_buddy_block_is_free(buddy))) + gpu_buddy_block_is_free(buddy))) { + rbtree_remove(mm, block); __gpu_buddy_free(mm, block, false); + } return ERR_PTR(err); } @@ -847,8 +849,10 @@ alloc_from_freetree(struct gpu_buddy *mm, return block; err_undo: - if (tmp != order) + if (tmp != order) { + rbtree_remove(mm, block); __gpu_buddy_free(mm, block, false); + } return ERR_PTR(err); } @@ -968,8 +972,10 @@ err_undo: buddy = __get_buddy(block); if (buddy && (gpu_buddy_block_is_free(block) && - gpu_buddy_block_is_free(buddy))) + gpu_buddy_block_is_free(buddy))) { + rbtree_remove(mm, block); __gpu_buddy_free(mm, block, false); + } return ERR_PTR(err); } @@ -1054,8 +1060,10 @@ err_undo: buddy = __get_buddy(block); if (buddy && (gpu_buddy_block_is_free(block) && - gpu_buddy_block_is_free(buddy))) + gpu_buddy_block_is_free(buddy))) { + rbtree_remove(mm, block); __gpu_buddy_free(mm, block, false); + } err_free: if (err == -ENOSPC && total_allocated_on_err) { |
