drm/amd/pm: fix pptable use-after-free

amdgpu_dpm_get_pp_table() returns a pointer to a driver-owned power table
after dropping adev->pm.mutex. The sysfs path then copies from that pointer.
A concurrent pp_table write can replace and free the allocation during the
copy, causing a use-after-free.

Change the DPM interface to copy into caller-provided storage while the mutex
is held. Keep the size-only query for attribute discovery without exposing
the driver-owned pointer.

Fixes: 1684d3ba48 ("drm/amd/amdgpu: change pptable output format from ASCII to binary")
Signed-off-by: Yang Wang <kevinyang.wang@amd.com>
Reviewed-by: Kenneth Feng <kenneth.feng@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
This commit is contained in:
Yang Wang
2026-07-24 07:41:29 +08:00
committed by Alex Deucher
parent 47a71f452c
commit f6eed7acfd
3 changed files with 16 additions and 14 deletions

View File

@@ -1183,12 +1183,14 @@ int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev,
return ret;
}
int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table)
int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table,
size_t size)
{
const struct amd_pm_funcs *pp_funcs = adev->powerplay.pp_funcs;
char *pptable = NULL;
int ret = 0;
if (!table)
if ((!table && size) || (table && !size))
return -EINVAL;
if (amdgpu_sriov_vf(adev) || !pp_funcs->get_pp_table || adev->scpm_enabled)
@@ -1196,7 +1198,13 @@ int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table)
mutex_lock(&adev->pm.mutex);
ret = pp_funcs->get_pp_table(adev->powerplay.pp_handle,
table);
&pptable);
if (ret > 0 && !pptable) {
ret = -EINVAL;
} else if (ret > 0 && table) {
ret = min_t(size_t, ret, size);
memcpy(table, pptable, ret);
}
mutex_unlock(&adev->pm.mutex);
return ret;

View File

@@ -595,25 +595,19 @@ static ssize_t amdgpu_get_pp_table(struct device *dev,
{
struct drm_device *ddev = dev_get_drvdata(dev);
struct amdgpu_device *adev = drm_to_adev(ddev);
char *table = NULL;
int size, ret;
ret = amdgpu_pm_get_access_if_active(adev);
if (ret)
return ret;
size = amdgpu_dpm_get_pp_table(adev, &table);
size = amdgpu_dpm_get_pp_table(adev, buf, PAGE_SIZE - 1);
amdgpu_pm_put_access(adev);
if (size <= 0)
return size;
if (size >= PAGE_SIZE)
size = PAGE_SIZE - 1;
memcpy(buf, table, size);
return size;
}
@@ -2733,10 +2727,9 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_
*states = ATTR_STATE_UNSUPPORTED;
} else if (DEVICE_ATTR_IS(pp_table)) {
int ret;
char *tmp = NULL;
ret = amdgpu_dpm_get_pp_table(adev, &tmp);
if (ret == -EOPNOTSUPP || !tmp)
ret = amdgpu_dpm_get_pp_table(adev, NULL, 0);
if (ret <= 0)
*states = ATTR_STATE_UNSUPPORTED;
else
*states = ATTR_STATE_SUPPORTED;

View File

@@ -487,7 +487,8 @@ int amdgpu_dpm_get_pp_num_states(struct amdgpu_device *adev,
int amdgpu_dpm_dispatch_task(struct amdgpu_device *adev,
enum amd_pp_task task_id,
enum amd_pm_state_type *user_state);
int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char **table);
int amdgpu_dpm_get_pp_table(struct amdgpu_device *adev, char *table,
size_t size);
int amdgpu_dpm_set_fine_grain_clk_vol(struct amdgpu_device *adev,
uint32_t type,
long *input,