mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 01:48:58 -04:00
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:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user