[PATCH] nfc: port100: don't wait forever for a sync command to complete
From: Palla Raghunath
Date: Sat Oct 03 2026 - 19:00:08 EST
port100_send_cmd_sync() sends a command and then sleeps in
wait_for_completion() until the device answers. There is no timeout,
so if the device never sends anything back, the caller sleeps forever.
syzbot hit this in probe, with a fake RC-S380 that enumerates fine and
then never answers GET_COMMAND_TYPE:
INFO: task kworker/0:1:10 blocked for more than 143 seconds.
Workqueue: usb_hub_wq hub_event
wait_for_completion+0x2ca/0x5e0
port100_send_cmd_sync drivers/nfc/port100.c:933 [inline]
port100_get_command_type_mask+0x1df/0x3a0 drivers/nfc/port100.c:1018
port100_probe+0x694/0xba0 drivers/nfc/port100.c:1549
usb_probe_interface+0x788/0xe50 drivers/usb/core/driver.c:399
Probe runs from hub_event() with the USB device lock held, so it's not
only this device that gets stuck. The hub worker is stuck too, and so
is anything else that needs that lock. In the syzbot case the process
behind the fake device is itself waiting for that lock in
usbdev_open(), so it can never answer, and the two wait on each other
for good.
Use wait_for_completion_timeout() instead. If it times out, kill
in_urb. The command then finishes with an error the normal way,
through cmd_complete_work. That path still writes to arg, which lives
on our stack, so wait for it before returning.
I tested this with the syzbot reproducer on next-20261002. Without the
patch, 5 hub workers and the 5 reproducer processes hang almost right
away. With it, probe gives up after the timeout ("Could not get
supported command types"), and the reproducer kept going for 5 minutes
(177 iterations) with no hung tasks and no KASAN reports.
Fixes: 0347a6ab300a ("NFC: port100: Commands mechanism implementation")
Reported-by: syzbot+3decf7252f8521fa80ff@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=3decf7252f8521fa80ff
Signed-off-by: Palla Raghunath <raghunathpalla.0209@xxxxxxxxx>
---
drivers/nfc/port100.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c
index e769a30b8b5c..2ca593f6c9fd 100644
--- a/drivers/nfc/port100.c
+++ b/drivers/nfc/port100.c
@@ -86,6 +86,9 @@ static u8 ack_frame[PORT100_FRAME_ACK_SIZE] = {
#define PORT100_CMD_STATUS_OK 0x00
#define PORT100_CMD_STATUS_TIMEOUT 0x80
+/* Timeout in ms for port100_send_cmd_sync() */
+#define PORT100_SYNC_CMD_TIMEOUT 5000
+
#define PORT100_MDAA_TGT_HAS_BEEN_ACTIVATED_MASK 0x01
#define PORT100_MDAA_TGT_WAS_ACTIVATED_MASK 0x02
@@ -930,7 +933,16 @@ static struct sk_buff *port100_send_cmd_sync(struct port100 *dev, u8 cmd_code,
return ERR_PTR(rc);
}
- wait_for_completion(&arg.done);
+ if (!wait_for_completion_timeout(&arg.done,
+ msecs_to_jiffies(PORT100_SYNC_CMD_TIMEOUT))) {
+ /*
+ * No answer from the device. Kill in_urb so the command
+ * completes with an error, and wait for that, since the
+ * completion handler still writes to arg.
+ */
+ usb_kill_urb(dev->in_urb);
+ wait_for_completion(&arg.done);
+ }
return arg.resp;
}
--
2.34.1