⬢ DragonFlyBSD Kernel Audit
DF-4162 / fix.diff
← back to finding ↓ download raw
--- a/sys/dev/drm/radeon/radeon_pm.c
+++ b/sys/dev/drm/radeon/radeon_pm.c
@@ -1255,6 +1255,10 @@
 
 static void radeon_pm_suspend_dpm(struct radeon_device *rdev)
 {
+	/* dpm init failed and state was scrubbed: nothing to disable */
+	if (!rdev->pm.dpm.boot_ps)
+		return;
+
 	mutex_lock(&rdev->pm.mutex);
 	/* disable dpm */
 	radeon_dpm_disable(rdev);
@@ -1315,6 +1319,10 @@
 {
 	int ret;
 
+	/* dpm init failed and state was scrubbed: cannot re-enable */
+	if (!rdev->pm.dpm.boot_ps)
+		return;
+
 	/* asic init will reset to the boot state */
 	mutex_lock(&rdev->pm.mutex);
 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
@@ -1445,7 +1453,18 @@
 
 	INIT_WORK(&rdev->pm.dpm.thermal.work, radeon_dpm_thermal_work_handler);
 	mutex_lock(&rdev->pm.mutex);
-	radeon_dpm_init(rdev);
+	ret = radeon_dpm_init(rdev);
+	if (ret)
+		goto dpm_init_failed;
+	if (!rdev->pm.dpm.boot_ps) {
+		/*
+		 * The VBIOS power table has no BOOT-classified state
+		 * (or ucStateEntrySize<=1): every per-asic dpm
+		 * enable/disable/fini path derefs boot_ps.
+		 */
+		ret = -EINVAL;
+		goto dpm_init_failed;
+	}
 	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = rdev->pm.dpm.boot_ps;
 	if (radeon_dpm == 1)
 		radeon_dpm_print_power_states(rdev);
@@ -1466,6 +1485,22 @@
 
 	return 0;
 
+dpm_init_failed:
+	/*
+	 * dpm.init failed or there is no BOOT-classified state.  The
+	 * per-asic parse error paths may already have kfree'd
+	 * rdev->pm.dpm.ps without NULLing it (see the DF-4117/DF-4125/
+	 * DF-4144 twins): scrub the dangling pointers so radeon_dpm_fini()
+	 * at unload cannot double free, and so the suspend/resume/fini
+	 * paths skip the dpm paths entirely (the asic *_init files still
+	 * ignore radeon_pm_init's return, so pm_method stays DPM).
+	 */
+	rdev->pm.dpm.ps = NULL;
+	rdev->pm.dpm.num_ps = 0;
+	rdev->pm.dpm.boot_ps = NULL;
+	rdev->pm.dpm.current_ps = rdev->pm.dpm.requested_ps = NULL;
+	mutex_unlock(&rdev->pm.mutex);
+	goto dpm_failed;
 dpm_failed:
 	rdev->pm.dpm_enabled = false;
 	if ((rdev->family >= CHIP_BARTS) &&
@@ -1689,7 +1724,7 @@
 
 static void radeon_pm_fini_dpm(struct radeon_device *rdev)
 {
-	if (rdev->pm.num_power_states > 1) {
+	if (rdev->pm.num_power_states > 1 && rdev->pm.dpm.boot_ps) {
 		mutex_lock(&rdev->pm.mutex);
 		radeon_dpm_disable(rdev);
 		mutex_unlock(&rdev->pm.mutex);