Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/amdkfd: Fix use-after-free of HMM range in svm_range_validate_and_map()

The function svm_range_validate_and_map() was freeing `range` when
amdgpu_hmm_range_get_pages() failed. But later, the code still used the
same `range` pointer and freed it again. This could cause a
use-after-free and double-free issue.

The fix sets `range = NULL` right after it is freed and checks for
`range` before using or freeing it again.

v2: Removed duplicate !r check in the condition for clarity.

v3: In amdgpu_hmm_range_get_pages(), when hmm_range_fault() fails, we
kvfree(pfns) but leave the pointer in hmm_range->hmm_pfns still pointing
to freed memory. The caller (or amdgpu_hmm_range_free(range)) may try to
free range->hmm_range.hmm_pfns again, causing a double free, Setting
hmm_range->hmm_pfns = NULL immediately after kvfree(pfns) prevents both
double free. (Philip)

In svm_range_validate_and_map(), When r == 0, it means success → range
is not NULL. When r != 0, it means failure → already made range = NULL.
So checking both (!r && range) is unnecessary because the moment r == 0,
we automatically know range exists and is safe to use. (Philip)

Fixes: 737da5363cc0 ("drm/amdgpu: update the functions to use amdgpu version of hmm")
Reported by: Dan Carpenter <dan.carpenter@linaro.org>
Cc: Philip Yang <Philip.Yang@amd.com>
Cc: Sunil Khatri <sunil.khatri@amd.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Alex Deucher <alexander.deucher@amd.com>
Signed-off-by: Srinivasan Shanmugam <srinivasan.shanmugam@amd.com>
Reviewed-by: Philip Yang<Philip.Yang@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>

authored by

Srinivasan Shanmugam and committed by
Alex Deucher
dfc74e37 1017e393

+5 -2
+1
drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c
··· 221 221 222 222 out_free_pfns: 223 223 kvfree(pfns); 224 + hmm_range->hmm_pfns = NULL; 224 225 out_free_range: 225 226 if (r == -EBUSY) 226 227 r = -EAGAIN;
+4 -2
drivers/gpu/drm/amd/amdkfd/kfd_svm.c
··· 1746 1746 WRITE_ONCE(p->svms.faulting_task, NULL); 1747 1747 if (r) { 1748 1748 amdgpu_hmm_range_free(range); 1749 + range = NULL; 1749 1750 pr_debug("failed %d to get svm range pages\n", r); 1750 1751 } 1751 1752 } else { ··· 1764 1763 svm_range_lock(prange); 1765 1764 1766 1765 /* Free backing memory of hmm_range if it was initialized 1767 - * Overrride return value to TRY AGAIN only if prior returns 1766 + * Override return value to TRY AGAIN only if prior returns 1768 1767 * were successful 1769 1768 */ 1770 1769 if (range && !amdgpu_hmm_range_valid(range) && !r) { ··· 1772 1771 r = -EAGAIN; 1773 1772 } 1774 1773 /* Free the hmm range */ 1775 - amdgpu_hmm_range_free(range); 1774 + if (range) 1775 + amdgpu_hmm_range_free(range); 1776 1776 1777 1777 1778 1778 if (!r && !list_empty(&prange->child_list)) {