lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20260116022638.994778-1-tuhaowen@uniontech.com>
Date: Fri, 16 Jan 2026 10:26:38 +0800
From: tuhaowen <tuhaowen@...ontech.com>
To: "Rafael J . Wysocki" <rafael@...nel.org>
Cc: Pavel Machek <pavel@...nel.org>,
	Len Brown <lenb@...nel.org>,
	linux-pm@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	tuhaowen@...ontech.com
Subject: [PATCH] PM: sleep: Fix race condition in suspend statistics updates

The suspend_stats structure tracks suspend/resume failure information
and is protected by suspend_stats_lock. However, while
dpm_save_failed_dev() correctly uses this lock, dpm_save_failed_step()
and dpm_save_errno() modify the same structure without any locking
protection.

This can cause races between writers (suspend/resume code updating
stats) and readers (userspace reading from /sys/power/suspend_stats/
or debugfs), leading to:

- Lost updates to counters (success, fail, step_failures)
- Corrupted circular buffer indices (last_failed_step,
  last_failed_errno)
- Inconsistent data when reading statistics from sysfs/debugfs

Fix this by adding mutex_lock/unlock protection to both
dpm_save_failed_step() and dpm_save_errno(). These functions are
always called in process context, so mutex can be used safely.

Signed-off-by: tuhaowen <tuhaowen@...ontech.com>
---
 kernel/power/main.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/kernel/power/main.c b/kernel/power/main.c
index bb7dd73e18fc..d8b053d85dc0 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -503,16 +503,23 @@ void dpm_save_failed_dev(const char *name)
 
 void dpm_save_failed_step(enum suspend_stat_step step)
 {
+	mutex_lock(&suspend_stats_lock);
+
 	suspend_stats.step_failures[step-1]++;
 	suspend_stats.failed_steps[suspend_stats.last_failed_step] = step;
 	suspend_stats.last_failed_step++;
 	suspend_stats.last_failed_step %= REC_FAILED_NUM;
+
+	mutex_unlock(&suspend_stats_lock);
 }
 
 void dpm_save_errno(int err)
 {
+	mutex_lock(&suspend_stats_lock);
+
 	if (!err) {
 		suspend_stats.success++;
+		mutex_unlock(&suspend_stats_lock);
 		return;
 	}
 
@@ -521,6 +528,8 @@ void dpm_save_errno(int err)
 	suspend_stats.errno[suspend_stats.last_failed_errno] = err;
 	suspend_stats.last_failed_errno++;
 	suspend_stats.last_failed_errno %= REC_FAILED_NUM;
+
+	mutex_unlock(&suspend_stats_lock);
 }
 
 void pm_report_hw_sleep_time(u64 t)
-- 
2.20.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ