[PATCH] USB: cdc-acm: fix racy TIOCMIWAIT implementation

From: Johan Hovold

Date: Mon Sep 07 2026 - 03:05:05 EST


The wakeup condition needs to be checked after adding the task to the
waitqueue and updating the task state to avoid missing a racing modem
status update or disconnect.

Fixes: 5a6a62bdb925 ("cdc-acm: add TIOCMIWAIT")
Cc: stable@xxxxxxxxxxxxxxx # 3.14
Cc: Oliver Neukum <oneukum@xxxxxxx>
Signed-off-by: Johan Hovold <johan@xxxxxxxxxx>
---
drivers/usb/class/cdc-acm.c | 29 +++++++++++++++--------------
1 file changed, 15 insertions(+), 14 deletions(-)

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 7bc5329fa3ed..00c250515e83 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1027,7 +1027,10 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
DECLARE_WAITQUEUE(wait, current);
struct async_icount old, new;

- do {
+ add_wait_queue(&acm->wioctl, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
spin_lock_irq(&acm->read_lock);
old = acm->oldcount;
new = acm->iocount;
@@ -1044,22 +1047,20 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
old.rng != new.rng)
break;

- add_wait_queue(&acm->wioctl, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- remove_wait_queue(&acm->wioctl, &wait);
if (acm->disconnected) {
- if (arg & TIOCM_CD)
- break;
- else
- rv = -ENODEV;
- } else {
- if (signal_pending(current))
- rv = -ERESTARTSYS;
+ rv = -ENODEV;
+ break;
}
- } while (!rv);

-
+ schedule();
+
+ if (signal_pending(current)) {
+ rv = -ERESTARTSYS;
+ break;
+ }
+ }
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&acm->wioctl, &wait);

return rv;
}
--
2.55.0