Writing to /sys/power/state can suspend a system indefinitely if there are
no wakeup sources configured (e.g., no RTC alarm or power button). This can
lead to an unrecoverable state where the system remains suspended forever.
Additionally, during suspend, tasks frozen while holding locks can trigger
false positive lockdep warnings, such as:
WARNING: kernel/freezer.c:139 at __set_task_frozen+0x260/0x340
kernel/freezer.c:139
Call Trace:
task_call_func+0x1aa/0x260 kernel/sched/core.c:4495
__freeze_task kernel/freezer.c:150 [inline]
freeze_task+0x216/0x390 kernel/freezer.c:169
try_to_freeze_tasks+0x190/0x620 kernel/power/process.c:54
freeze_processes+0xce/0x1e0 kernel/power/process.c:137
suspend_freeze_processes kernel/power/power.h:281 [inline]
suspend_prepare kernel/power/suspend.c:387 [inline]
enter_state kernel/power/suspend.c:609 [inline]
pm_suspend+0x2f1/0x760 kernel/power/suspend.c:644
state_store+0x206/0x290 kernel/power/main.c:819
kernfs_fop_write_iter+0x3a5/0x540 fs/kernfs/file.c:345
vfs_write+0x61e/0xbb0 fs/read_write.c:687
ksys_write+0x156/0x270 fs/read_write.c:739
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
To prevent this, require CAP_SYS_BOOT and check if any wakeup sources are
registered in the system before allowing suspend via state_store() and
autosleep_store(). If the list of wakeup sources is empty, return -EPERM.
Furthermore, fix the false positive lockdep warnings in FUSE and SunRPC by
changing TASK_FREEZABLE to TASK_FREEZABLE_UNSAFE in fuse_get_req() and
__rpc_execute(). This explicitly tells lockdep that sleeping with locks
held in these specific locations is safe and expected during system freeze.
Assisted-by: Gemini:gemini-3.5-flash Gemini:gemini-3.1-pro-preview syzbot
Reported-by: syzbot+209eccd8f507de9f7f1b@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=209eccd8f507de9f7f1b
Link: https://syzkaller.appspot.com/ai_job?id=b9059933-51e0-4b0e-81e5-3baa7c7ce899
To: "Anna Schumaker"
To: "Chuck Lever"
To: "David S. Miller"
To: "Eric Dumazet"
To:
To: "Jeff Layton"
To: "Jakub Kicinski"
To:
To:
To: "Miklos Szeredi"
To:
To: "Paolo Abeni"
To: "Rafael J. Wysocki"
To: "Trond Myklebust"
Cc: "Dai Ngo"
Cc: "Simon Horman"
Cc: "Len Brown"
Cc:
Cc: "NeilBrown"
Cc: "Olga Kornievskaia"
Cc: "Pavel Machek"
Cc: "Tom Talpey"
---
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 5763a7cd3..9e8dc96d2 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -116,7 +116,7 @@ static struct fuse_req *fuse_get_req(struct fuse_chan *fch, bool for_background)
err = -EINTR;
if (wait_event_state_exclusive(fch->blocked_waitq,
!fuse_block_alloc(fch, for_background),
- (TASK_KILLABLE | TASK_FREEZABLE)))
+ (TASK_KILLABLE | TASK_FREEZABLE_UNSAFE)))
goto out;
}
diff --git a/kernel/power/main.c b/kernel/power/main.c
index 5429e9f19..780afda11 100644
--- a/kernel/power/main.c
+++ b/kernel/power/main.c
@@ -20,6 +20,7 @@
#include
#include
#include
+#include
#include "power.h"
@@ -802,6 +803,22 @@ static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
suspend_state_t state;
int error;
+#ifdef CONFIG_PM_SLEEP
+ int idx;
+ struct wakeup_source *ws;
+#endif
+
+ if (!capable(CAP_SYS_BOOT))
+ return -EPERM;
+
+#ifdef CONFIG_PM_SLEEP
+ idx = wakeup_sources_read_lock();
+ ws = wakeup_sources_walk_start();
+ wakeup_sources_read_unlock(idx);
+ if (!ws)
+ return -EPERM;
+#endif
+
error = pm_autosleep_lock();
if (error)
return error;
@@ -929,6 +946,24 @@ static ssize_t autosleep_store(struct kobject *kobj,
suspend_state_t state = decode_state(buf, n);
int error;
+#ifdef CONFIG_PM_SLEEP
+ int idx;
+ struct wakeup_source *ws;
+#endif
+
+ if (!capable(CAP_SYS_BOOT))
+ return -EPERM;
+
+#ifdef CONFIG_PM_SLEEP
+ if (state != PM_SUSPEND_ON) {
+ idx = wakeup_sources_read_lock();
+ ws = wakeup_sources_walk_start();
+ wakeup_sources_read_unlock(idx);
+ if (!ws)
+ return -EPERM;
+ }
+#endif
+
if (state == PM_SUSPEND_ON
&& strcmp(buf, "off") && strcmp(buf, "off\n"))
return -EINVAL;
diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c
index 016f16ca5..494947d50 100644
--- a/net/sunrpc/sched.c
+++ b/net/sunrpc/sched.c
@@ -984,7 +984,7 @@ static void __rpc_execute(struct rpc_task *task)
trace_rpc_task_sync_sleep(task, task->tk_action);
status = out_of_line_wait_on_bit(&task->tk_runstate,
RPC_TASK_QUEUED, rpc_wait_bit_killable,
- TASK_KILLABLE|TASK_FREEZABLE);
+ TASK_KILLABLE | TASK_FREEZABLE_UNSAFE);
if (status < 0) {
/*
* When a sync task receives a signal, it exits with
base-commit: 075b74841bd0065a3bda3440873c747938e69b68
--
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).
See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
The person who has signed off on the patch is responsible for
addressing comments.
syzbot engineers can be reached at syzkaller@googlegroups.com.