⬢ DragonFlyBSD Kernel Audit
DF-3955 / fix.diff
← back to finding ↓ download raw
diff --git a/sys/dev/drm/radeon/kv_dpm.c b/sys/dev/drm/radeon/kv_dpm.c
index 1111111..2222222 100644
--- a/sys/dev/drm/radeon/kv_dpm.c
+++ b/sys/dev/drm/radeon/kv_dpm.c
@@ -2670,12 +2670,20 @@ static int kv_parse_power_table(struct radeon_device *rdev)
 		power_state = (union pplib_power_state *)power_state_offset;
 		non_clock_array_index = power_state->v2.nonClockInfoIndex;
 		non_clock_info = (struct _ATOM_PPLIB_NONCLOCK_INFO *)
 			&non_clock_info_array->nonClockInfo[non_clock_array_index];
 		if (!rdev->pm.power_state[i].clock_info)
-			return -EINVAL;
+			goto parse_error;
 		ps = kzalloc(sizeof(struct kv_ps), GFP_KERNEL);
 		if (ps == NULL) {
-			kfree(rdev->pm.dpm.ps);
+			/* free the states parsed so far and drop the
+			 * dangling pointer so kv_dpm_fini cannot double
+			 * free it (and does free the ps_priv objects) */
+			for (j = 0; j < i; j++)
+				kfree(rdev->pm.dpm.ps[j].ps_priv);
+			kfree(rdev->pm.dpm.ps);
+			rdev->pm.dpm.ps = NULL;
+			rdev->pm.dpm.num_ps = 0;
 			return -ENOMEM;
 		}
+		/* count states as they complete so error paths can unwind */
+		rdev->pm.dpm.num_ps = i + 1;
 		rdev->pm.dpm.ps[i].ps_priv = ps;
@@ -2698,7 +2706,6 @@ static int kv_parse_power_table(struct radeon_device *rdev)
 		power_state_offset += 2 + power_state->v2.ucNumDPMLevels;
 	}
-	rdev->pm.dpm.num_ps = state_array->ucNumEntries;
+	/* num_ps was updated incrementally above */
 
 	/* fill in the vce power states */
# NOTE: 'j' is already declared in the function (int i, j, k, ...).
# Companion (out of audited scope): honor kv_dpm_init()'s return value at
# radeon_pm.c:1448 so the error is not silently swallowed (see DF-3953).