drm/radeon: Avoid power table parsing memory leaks
authorKees Cook <keescook@chromium.org>
Mon, 3 May 2021 05:06:08 +0000 (22:06 -0700)
committerAlex Deucher <alexander.deucher@amd.com>
Mon, 10 May 2021 22:06:45 +0000 (18:06 -0400)
Avoid leaving a hanging pre-allocated clock_info if last mode is
invalid, and avoid heap corruption if no valid modes are found.

Bug: https://bugzilla.kernel.org/show_bug.cgi?id=211537
Fixes: 6991b8f2a319 ("drm/radeon/kms: fix segfault in pm rework")
Signed-off-by: Kees Cook <keescook@chromium.org>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/radeon/radeon_atombios.c

index f9f4efa..28c4413 100644 (file)
@@ -2120,11 +2120,14 @@ static int radeon_atombios_parse_power_table_1_3(struct radeon_device *rdev)
                return state_index;
        /* last mode is usually default, array is low to high */
        for (i = 0; i < num_modes; i++) {
-               rdev->pm.power_state[state_index].clock_info =
-                       kcalloc(1, sizeof(struct radeon_pm_clock_info),
-                               GFP_KERNEL);
+               /* avoid memory leaks from invalid modes or unknown frev. */
+               if (!rdev->pm.power_state[state_index].clock_info) {
+                       rdev->pm.power_state[state_index].clock_info =
+                               kzalloc(sizeof(struct radeon_pm_clock_info),
+                                       GFP_KERNEL);
+               }
                if (!rdev->pm.power_state[state_index].clock_info)
-                       return state_index;
+                       goto out;
                rdev->pm.power_state[state_index].num_clock_modes = 1;
                rdev->pm.power_state[state_index].clock_info[0].voltage.type = VOLTAGE_NONE;
                switch (frev) {
@@ -2243,8 +2246,15 @@ static int radeon_atombios_parse_power_table_1_3(struct radeon_device *rdev)
                        break;
                }
        }
+out:
+       /* free any unused clock_info allocation. */
+       if (state_index && state_index < num_modes) {
+               kfree(rdev->pm.power_state[state_index].clock_info);
+               rdev->pm.power_state[state_index].clock_info = NULL;
+       }
+
        /* last mode is usually default */
-       if (rdev->pm.default_power_state_index == -1) {
+       if (state_index && rdev->pm.default_power_state_index == -1) {
                rdev->pm.power_state[state_index - 1].type =
                        POWER_STATE_TYPE_DEFAULT;
                rdev->pm.default_power_state_index = state_index - 1;