Commit 1daf74ac authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman

[PATCH] USB: fix racy access to urb->status in cdc acm driver

Hi,

fix access to urb->status by introduction of an explicit flag
for finished data transfer.
  - fix racy access to urb->status
Signed-off-by: default avatarOliver Neukum <oliver@neukum.name>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 3e676420
...@@ -223,12 +223,14 @@ static void acm_write_bulk(struct urb *urb, struct pt_regs *regs) ...@@ -223,12 +223,14 @@ static void acm_write_bulk(struct urb *urb, struct pt_regs *regs)
struct acm *acm = (struct acm *)urb->context; struct acm *acm = (struct acm *)urb->context;
if (!ACM_READY(acm)) if (!ACM_READY(acm))
return; goto out;
if (urb->status) if (urb->status)
dbg("nonzero write bulk status received: %d", urb->status); dbg("nonzero write bulk status received: %d", urb->status);
schedule_work(&acm->work); schedule_work(&acm->work);
out:
acm->ready_for_write = 1;
} }
static void acm_softint(void *private) static void acm_softint(void *private)
...@@ -328,7 +330,7 @@ static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned c ...@@ -328,7 +330,7 @@ static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned c
if (!ACM_READY(acm)) if (!ACM_READY(acm))
return -EINVAL; return -EINVAL;
if (acm->writeurb->status == -EINPROGRESS) if (!acm->ready_for_write)
return 0; return 0;
if (!count) if (!count)
return 0; return 0;
...@@ -344,10 +346,11 @@ static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned c ...@@ -344,10 +346,11 @@ static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned c
acm->writeurb->transfer_buffer_length = count; acm->writeurb->transfer_buffer_length = count;
acm->writeurb->dev = acm->dev; acm->writeurb->dev = acm->dev;
/* GFP_KERNEL probably works if from_user */ acm->ready_for_write = 0;
stat = usb_submit_urb(acm->writeurb, GFP_ATOMIC); stat = usb_submit_urb(acm->writeurb, GFP_NOIO);
if (stat < 0) { if (stat < 0) {
dbg("usb_submit_urb(write bulk) failed"); dbg("usb_submit_urb(write bulk) failed");
acm->ready_for_write = 1;
return stat; return stat;
} }
...@@ -359,7 +362,7 @@ static int acm_tty_write_room(struct tty_struct *tty) ...@@ -359,7 +362,7 @@ static int acm_tty_write_room(struct tty_struct *tty)
struct acm *acm = tty->driver_data; struct acm *acm = tty->driver_data;
if (!ACM_READY(acm)) if (!ACM_READY(acm))
return -EINVAL; return -EINVAL;
return acm->writeurb->status == -EINPROGRESS ? 0 : acm->writesize; return !acm->ready_for_write ? 0 : acm->writesize;
} }
static int acm_tty_chars_in_buffer(struct tty_struct *tty) static int acm_tty_chars_in_buffer(struct tty_struct *tty)
...@@ -367,7 +370,7 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty) ...@@ -367,7 +370,7 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty)
struct acm *acm = tty->driver_data; struct acm *acm = tty->driver_data;
if (!ACM_READY(acm)) if (!ACM_READY(acm))
return -EINVAL; return -EINVAL;
return acm->writeurb->status == -EINPROGRESS ? acm->writeurb->transfer_buffer_length : 0; return !acm->ready_for_write ? acm->writeurb->transfer_buffer_length : 0;
} }
static void acm_tty_throttle(struct tty_struct *tty) static void acm_tty_throttle(struct tty_struct *tty)
...@@ -610,6 +613,7 @@ static int acm_probe (struct usb_interface *intf, ...@@ -610,6 +613,7 @@ static int acm_probe (struct usb_interface *intf,
acm->bh.func = acm_rx_tasklet; acm->bh.func = acm_rx_tasklet;
acm->bh.data = (unsigned long) acm; acm->bh.data = (unsigned long) acm;
INIT_WORK(&acm->work, acm_softint, acm); INIT_WORK(&acm->work, acm_softint, acm);
acm->ready_for_write = 1;
if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) { if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) {
......
...@@ -96,6 +96,7 @@ struct acm { ...@@ -96,6 +96,7 @@ struct acm {
unsigned int minor; /* acm minor number */ unsigned int minor; /* acm minor number */
unsigned char throttle; /* throttled by tty layer */ unsigned char throttle; /* throttled by tty layer */
unsigned char clocal; /* termios CLOCAL */ unsigned char clocal; /* termios CLOCAL */
unsigned char ready_for_write; /* write urb can be used */
}; };
/* "Union Functional Descriptor" from CDC spec 5.2.3.X */ /* "Union Functional Descriptor" from CDC spec 5.2.3.X */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment