summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorJohan Hovold <johan@kernel.org>2021-09-29 11:09:36 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2021-10-13 10:10:50 +0200
commit5586e0f68fea90a63f013e3bb0e0f35756489854 (patch)
treefab8d0bf176765afd777f096257e87ed32c53d3c /drivers
parent75c1aa7ed1ed4caf5d9dde0a7a8f142140afbce9 (diff)
downloadlinux-5586e0f68fea90a63f013e3bb0e0f35756489854.tar.gz
linux-5586e0f68fea90a63f013e3bb0e0f35756489854.tar.bz2
linux-5586e0f68fea90a63f013e3bb0e0f35756489854.zip
USB: cdc-acm: fix racy tty buffer accesses
commit 65a205e6113506e69a503b61d97efec43fc10fd7 upstream. A recent change that started reporting break events to the line discipline caused the tty-buffer insertions to no longer be serialised by inserting events also from the completion handler for the interrupt endpoint. Completion calls for distinct endpoints are not guaranteed to be serialised. For example, in case a host-controller driver uses bottom-half completion, the interrupt and bulk-in completion handlers can end up running in parallel on two CPUs (high-and low-prio tasklets, respectively) thereby breaking the tty layer's single producer assumption. Fix this by holding the read lock also when inserting characters from the bulk endpoint. Fixes: 08dff274edda ("cdc-acm: fix BREAK rx code path adding necessary calls") Cc: stable@vger.kernel.org Acked-by: Oliver Neukum <oneukum@suse.com> Signed-off-by: Johan Hovold <johan@kernel.org> Link: https://lore.kernel.org/r/20210929090937.7410-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/class/cdc-acm.c5
1 files changed, 5 insertions, 0 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 6959231d63b3..5f44877137de 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -474,11 +474,16 @@ static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
static void acm_process_read_urb(struct acm *acm, struct urb *urb)
{
+ unsigned long flags;
+
if (!urb->actual_length)
return;
+ spin_lock_irqsave(&acm->read_lock, flags);
tty_insert_flip_string(&acm->port, urb->transfer_buffer,
urb->actual_length);
+ spin_unlock_irqrestore(&acm->read_lock, flags);
+
tty_flip_buffer_push(&acm->port);
}