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 <[email protected]>
---
 drivers/gpu/drm/amd/pm/amdgpu_dpm.c     | 14 +++++++++++---
 drivers/gpu/drm/amd/pm/amdgpu_pm.c      | 13 +++----------
 drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h |  3 ++-
 3 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c 
b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
index c09c60ad3f38..df863a451f26 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_dpm.c
@@ -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;
diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c 
b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
index f5a5d72b4108..d473976ddae5 100644
--- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c
+++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c
@@ -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;
diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h 
b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
index c7ea29385682..a1d1c9f5b1d4 100644
--- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
+++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h
@@ -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,
-- 
2.47.3

Reply via email to