[PATCH RFC v2] nbd: fix hung task and integer overflow in timeout configuration
"syzbot" <[email protected]> Thu, 23 Jul 2026 16:16:38 +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. Because the BLK_MQ_F_BLOCKING async-dispatch path leaves the
submitter in an uninterruptible I/O-completion wait, a dead connection
timeout larger than 120 seconds triggers the hung task detector, leading to
a kernel panic:
INFO: task kworker/1:1H:99 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 non-fatal signals from
prematurely interrupting the wait. While commit ff57dc94faec is not the
root cause of the unbounded-wait DoS, keeping the wait safe against
non-fatal signals is still necessary.
To fix this, cap the dead connection timeout to a maximum of 100 seconds
(100 * HZ). Bounding the timeout to 100 seconds is what actually prevents
the hung task panic, as it is below the default 120-second threshold of the
hung task detector. Additionally, replace wait_event_timeout() with
wait_event_killable_timeout() as reapability hardening, allowing the
waiting task to be terminated by fatal signals. Also, call wake_up() in
sock_shutdown() to ensure any waiting tasks are woken up when the socket is
shut down.
Finally, prevent integer overflow when parsing the timeout by capping the
input value to U32_MAX before converting it to jiffies.
Fixes: 560bc4b39952 ("nbd: handle dead connections")
Assisted-by: Gemini:gemini-3.5-flash 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=a75e9299-d014-47a0-abf8-8bbea2f535a5
To: "Jens Axboe" <[email protected]>
To: "Josef Bacik" <[email protected]>
To: <[email protected]>
To: <[email protected]>
Cc: <[email protected]>
---
v2:
- Capped dead_conn_timeout to 100 seconds (100 * HZ) instead of MAX_SCHEDULE_TIMEOUT to prevent the hung task detector from triggering.
- Replaced wait_event_idle_timeout with wait_event_killable_timeout to allow the waiting task to be terminated by fatal signals.
- Added wake_up(&config->conn_wait) in sock_shutdown() to ensure waiting tasks are woken up during socket shutdown.
v1:
https://lore.kernel.org/all/[email protected]/T/
---
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index 8f10762e9..9a212da7c 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -412,6 +412,7 @@ static void sock_shutdown(struct nbd_device *nbd)
mutex_unlock(&nsock->tx_lock);
}
dev_warn(disk_to_dev(nbd->disk), "shutting down sockets\n");
+ wake_up(&config->conn_wait);
}
static u32 req_to_nbd_cmd_type(struct request *req)
@@ -1101,14 +1102,17 @@ static int find_fallback(struct nbd_device *nbd, int index)
static int wait_for_reconnect(struct nbd_device *nbd)
{
struct nbd_config *config = nbd->config;
+ long ret;
+
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))
+ ret = wait_event_killable_timeout(config->conn_wait,
+ test_bit(NBD_RT_DISCONNECTED,
+ &config->runtime_flags) ||
+ atomic_read(&config->live_connections) > 0,
+ config->dead_conn_timeout);
+ if (ret <= 0)
return 0;
return !test_bit(NBD_RT_DISCONNECTED, &config->runtime_flags);
@@ -2194,9 +2198,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, 100 * HZ);
}
if (info->attrs[NBD_ATTR_SERVER_FLAGS])
config->flags =
@@ -2415,9 +2421,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, 100 * HZ);
}
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].