[PATCH RFC] nbd: fix hung task and integer overflow in timeout configuration

"syzbot" <[email protected]> Wed, 15 Jul 2026 15:32:04 +0000 (UTC)
Newsgroups dev.linux.lists.syzbot
Message-ID <[email protected]>
An unprivileged user can configure an NBD device with an arbitrarily large
dead connection timeout via the NBD_ATTR_DEAD_CONN_TIMEOUT netlink
attribute. When a request is queued and the connection is dead,
nbd_handle_cmd() calls wait_for_reconnect(), which blocks the current
thread using wait_event_timeout(). Because this wait is in the
TASK_UNINTERRUPTIBLE state, a timeout larger than 120 seconds triggers the
hung task detector, leading to a kernel panic:

INFO: task kworker/1:1H:667 blocked for more than 143 seconds.
Workqueue: kblockd blk_mq_run_work_fn
Call Trace:
 <TASK>
 __schedule+0x17e7/0x5630
 schedule+0x164/0x2b0
 schedule_timeout+0x152/0x2c0
 wait_for_reconnect drivers/block/nbd.c:1107 [inline]
 nbd_handle_cmd drivers/block/nbd.c:1149 [inline]
 nbd_queue_rq+0x797/0xfb0 drivers/block/nbd.c:1207
 blk_mq_dispatch_rq_list+0x499/0x1990
 __blk_mq_sched_dispatch_requests+0xd36/0x1580
 blk_mq_sched_dispatch_requests+0xd7/0x190
 blk_mq_run_work_fn+0x16c/0x300
 process_one_work kernel/workqueue.c:3322 [inline]
 process_scheduled_works+0xa8e/0x14e0
 worker_thread+0x92d/0xe10
 kthread+0x388/0x470
 ret_from_fork+0x514/0xb70
 ret_from_fork_asm+0x1a/0x30
 </TASK>

Historically, wait_for_reconnect() used wait_event_interruptible_timeout(),
but commit ff57dc94faec ("nbd: wait uninterruptible for the dead timeout")
changed it to wait_event_timeout() to prevent signals from prematurely
interrupting the wait. However, using TASK_UNINTERRUPTIBLE for arbitrarily
long waits is an anti-pattern that trips the hung task detector.

Fix this by replacing wait_event_timeout() with wait_event_idle_timeout().
This puts the task into the TASK_IDLE state (TASK_UNINTERRUPTIBLE |
TASK_NOLOAD), which ignores signals as intended but is explicitly ignored
by the hung task detector.

Additionally, add defense-in-depth by capping the parsed timeout to U32_MAX
and the resulting jiffies to MAX_SCHEDULE_TIMEOUT. This prevents integer
overflow when the u64 timeout is implicitly cast to a signed long by
schedule_timeout(), which could otherwise result in a negative timeout and
cause the wait to return immediately.

Fixes: ff57dc94faec ("nbd: wait uninterruptible for the dead timeout")
Assisted-by: Gemini:gemini-3.1-pro-preview syzbot
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=30c16035531e3248dcbc
Link: https://syzkaller.appspot.com/ai_job?id=f07c3611-0a61-4d67-9f4d-033b1cb82e1e
To: "Jens Axboe" <[email protected]>
To: "Josef Bacik" <[email protected]>
To: <[email protected]>
To: <[email protected]>
To: "Josef Bacik" <[email protected]>
Cc: <[email protected]>

---
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 8f10762e9..874a916f8 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -1104,11 +1104,11 @@ static int wait_for_reconnect(struct nbd_device *nbd)
 	if (!config->dead_conn_timeout)
 		return 0;
 
-	if (!wait_event_timeout(config->conn_wait,
-				test_bit(NBD_RT_DISCONNECTED,
-					 &config->runtime_flags) ||
-				atomic_read(&config->live_connections) > 0,
-				config->dead_conn_timeout))
+	if (!wait_event_idle_timeout(
+		    config->conn_wait,
+		    test_bit(NBD_RT_DISCONNECTED, &config->runtime_flags) ||
+			    atomic_read(&config->live_connections) > 0,
+		    config->dead_conn_timeout))
 		return 0;
 
 	return !test_bit(NBD_RT_DISCONNECTED, &config->runtime_flags);
@@ -2194,9 +2194,11 @@ static int nbd_genl_connect(struct sk_buff *skb, struct genl_info *info)
 		nbd_set_cmd_timeout(nbd,
 				    nla_get_u64(info->attrs[NBD_ATTR_TIMEOUT]));
 	if (info->attrs[NBD_ATTR_DEAD_CONN_TIMEOUT]) {
-		config->dead_conn_timeout =
+		u64 timeout =
 			nla_get_u64(info->attrs[NBD_ATTR_DEAD_CONN_TIMEOUT]);
-		config->dead_conn_timeout *= HZ;
+		timeout = min_t(u64, timeout, U32_MAX);
+		config->dead_conn_timeout =
+			min_t(u64, timeout * HZ, MAX_SCHEDULE_TIMEOUT);
 	}
 	if (info->attrs[NBD_ATTR_SERVER_FLAGS])
 		config->flags =
@@ -2415,9 +2417,11 @@ static int nbd_genl_reconfigure(struct sk_buff *skb, struct genl_info *info)
 		nbd_set_cmd_timeout(nbd,
 				    nla_get_u64(info->attrs[NBD_ATTR_TIMEOUT]));
 	if (info->attrs[NBD_ATTR_DEAD_CONN_TIMEOUT]) {
-		config->dead_conn_timeout =
+		u64 timeout =
 			nla_get_u64(info->attrs[NBD_ATTR_DEAD_CONN_TIMEOUT]);
-		config->dead_conn_timeout *= HZ;
+		timeout = min_t(u64, timeout, U32_MAX);
+		config->dead_conn_timeout =
+			min_t(u64, timeout * HZ, MAX_SCHEDULE_TIMEOUT);
 	}
 	if (info->attrs[NBD_ATTR_CLIENT_FLAGS]) {
 		u64 flags = nla_get_u64(info->attrs[NBD_ATTR_CLIENT_FLAGS]);


base-commit: a13c140cc289c0b7b3770bce5b3ad42ab35074aa
-- 
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.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at [email protected].