Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

USB: fix omninet write vs. close race

omninet kills all URBs in close. However write() returns as soon as
the URB has been submitted. Killing the last URB means a race that
can lose that date written in the last call to write().
As a fix this is moved to shutdown().

Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Oliver Neukum and committed by
Greg Kroah-Hartman
5ec1862e 2f007de2

+23 -19
+23 -19
drivers/usb/serial/omninet.c
··· 69 69 static int omninet_write (struct usb_serial_port *port, const unsigned char *buf, int count); 70 70 static int omninet_write_room (struct usb_serial_port *port); 71 71 static void omninet_shutdown (struct usb_serial *serial); 72 + static int omninet_attach (struct usb_serial *serial); 72 73 73 74 static struct usb_device_id id_table [] = { 74 75 { USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) }, ··· 100 99 .num_bulk_in = 1, 101 100 .num_bulk_out = 2, 102 101 .num_ports = 1, 102 + .attach = omninet_attach, 103 103 .open = omninet_open, 104 104 .close = omninet_close, 105 105 .write = omninet_write, ··· 147 145 __u8 od_outseq; // Sequence number for bulk_out URBs 148 146 }; 149 147 150 - static int omninet_open (struct usb_serial_port *port, struct file *filp) 148 + static int omninet_attach (struct usb_serial *serial) 151 149 { 152 - struct usb_serial *serial = port->serial; 153 - struct usb_serial_port *wport; 154 - struct omninet_data *od; 155 - int result = 0; 156 - 157 - dbg("%s - port %d", __FUNCTION__, port->number); 150 + struct omninet_data *od; 151 + struct usb_serial_port *port = serial->port[0]; 158 152 159 153 od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL ); 160 154 if( !od ) { 161 155 err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct omninet_data)); 162 156 return -ENOMEM; 163 157 } 164 - 165 158 usb_set_serial_port_data(port, od); 159 + return 0; 160 + } 161 + 162 + static int omninet_open (struct usb_serial_port *port, struct file *filp) 163 + { 164 + struct usb_serial *serial = port->serial; 165 + struct usb_serial_port *wport; 166 + struct omninet_data *od = usb_get_serial_port_data(port); 167 + int result = 0; 168 + 169 + dbg("%s - port %d", __FUNCTION__, port->number); 170 + 171 + od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL ); 166 172 wport = serial->port[1]; 167 173 wport->tty = port->tty; 168 174 ··· 182 172 result = usb_submit_urb(port->read_urb, GFP_KERNEL); 183 173 if (result) { 184 174 err("%s - failed submitting read urb, error %d", __FUNCTION__, result); 185 - /* open failed - all allocations must be freed */ 186 - kfree(od); 187 - usb_set_serial_port_data(port, NULL); 188 175 } 189 176 190 177 return result; ··· 189 182 190 183 static void omninet_close (struct usb_serial_port *port, struct file * filp) 191 184 { 192 - struct usb_serial *serial = port->serial; 193 - struct usb_serial_port *wport; 194 - 195 185 dbg("%s - port %d", __FUNCTION__, port->number); 196 - 197 - wport = serial->port[1]; 198 - usb_kill_urb(wport->write_urb); 199 186 usb_kill_urb(port->read_urb); 200 - 201 - kfree(usb_get_serial_port_data(port)); 202 187 } 203 188 204 189 ··· 329 330 330 331 static void omninet_shutdown (struct usb_serial *serial) 331 332 { 333 + struct usb_serial_port *wport = serial->port[1]; 334 + struct usb_serial_port *port = serial->port[0]; 332 335 dbg ("%s", __FUNCTION__); 336 + 337 + usb_kill_urb(wport->write_urb); 338 + kfree(usb_get_serial_port_data(port)); 333 339 } 334 340 335 341