]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
drm/amd/pm: fix pptable use-after-free
authorYang Wang <kevinyang.wang@amd.com>
Thu, 23 Jul 2026 23:41:29 +0000 (07:41 +0800)
committerAlex Deucher <alexander.deucher@amd.com>
Tue, 28 Jul 2026 23:59:59 +0000 (19:59 -0400)
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: 1684d3ba4885 ("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>
(cherry picked from commit f6eed7acfd30099ef7baeb6ba45bb59daad80631)
Cc: stable@vger.kernel.org
drivers/gpu/drm/amd/pm/amdgpu_dpm.c
drivers/gpu/drm/amd/pm/amdgpu_pm.c
drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h

index f76ba67535519cfe407d9e29755b367ad150695c..c787e772b3cc2a8c5e767bb95e00b18c61d24ddf 100644 (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;
index 97da01aff76c7e69a69e04e3074c94b190af393d..d94ea54c33c39bac159924ebe4eb89a9af480f9d 100644 (file)
@@ -564,25 +564,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;
 }
 
@@ -2726,10 +2720,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;
index aa3f427819a097dd0e3c6875ad8cd8ee27203a09..e95cd22a31cfdb395a0a47f264a418f0085ac98f 100644 (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,