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

USB: option: fix usage of urb->status abuse

Might fix bug 8561

On Mon, 4 Jun 2007, Paulo Pereira wrote:

> The patch that you send is not resolving the problem... :(
> I stil have Kernel panic after 45/60 min of work with Ktorrent/Amule...
>
> The Drump is:
>
> Call Trace:
> [<c055fb36>] usb_hcd_submit+0xb1/0x763
> [<f9276488>] ipt_do_table+0x2c7/0x2ef [ip_tables]
> [<f929a6d7>] nf_ct_deliver_cached_events+0x41/0x96 [nf_conntrak]
> [<f9288254>] ipv4_confirm+0x36/0c3b [nf_conntrack_ipv4]
> [<c05ce7c2>] tcp_v4_rcv+0x827/0x899
> [<c05afcc0>] nf_hook_slow+0x4d/0xb5
> [<c042826f>] irq_enter+0x19/0x23
> [<c042826f>] irq_enter+0x19/0x23
> [<c040794c>] do_IRQ+0xbd/0xd1
> [<f90893c9>] option_write+0xa7/0xef [option]

Okay, from this it looks like there's a problem in the option.c serial
driver. Glancing at the code, it's obvious why: The thing totally
abuses the USB API.

Try applying this patch; it should help.

From: Alan Stern <stern@rowland.harvard.edu>
Cc: Paulo Pereira <pfmp.404@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
59c2afa0 4365831d

+17 -3
+17 -3
drivers/usb/serial/option.c
··· 38 38 #include <linux/tty.h> 39 39 #include <linux/tty_flip.h> 40 40 #include <linux/module.h> 41 + #include <linux/bitops.h> 41 42 #include <linux/usb.h> 42 43 #include <linux/usb/serial.h> 43 44 ··· 241 240 /* Output endpoints and buffer for this port */ 242 241 struct urb *out_urbs[N_OUT_URB]; 243 242 char out_buffer[N_OUT_URB][OUT_BUFLEN]; 243 + unsigned long out_busy; /* Bit vector of URBs in use */ 244 244 245 245 /* Settings for the port */ 246 246 int rts_state; /* Handshaking pins (outputs) */ ··· 372 370 todo = OUT_BUFLEN; 373 371 374 372 this_urb = portdata->out_urbs[i]; 375 - if (this_urb->status == -EINPROGRESS) { 373 + if (test_and_set_bit(i, &portdata->out_busy)) { 376 374 if (time_before(jiffies, 377 375 portdata->tx_start_time[i] + 10 * HZ)) 378 376 continue; ··· 396 394 dbg("usb_submit_urb %p (write bulk) failed " 397 395 "(%d, has %d)", this_urb, 398 396 err, this_urb->status); 397 + clear_bit(i, &portdata->out_busy); 399 398 continue; 400 399 } 401 400 portdata->tx_start_time[i] = jiffies; ··· 449 446 static void option_outdat_callback(struct urb *urb) 450 447 { 451 448 struct usb_serial_port *port; 449 + struct option_port_private *portdata; 450 + int i; 452 451 453 452 dbg("%s", __FUNCTION__); 454 453 455 454 port = (struct usb_serial_port *) urb->context; 456 455 457 456 usb_serial_port_softint(port); 457 + 458 + portdata = usb_get_serial_port_data(port); 459 + for (i = 0; i < N_OUT_URB; ++i) { 460 + if (portdata->out_urbs[i] == urb) { 461 + smp_mb__before_clear_bit(); 462 + clear_bit(i, &portdata->out_busy); 463 + break; 464 + } 465 + } 458 466 } 459 467 460 468 static void option_instat_callback(struct urb *urb) ··· 532 518 533 519 for (i=0; i < N_OUT_URB; i++) { 534 520 this_urb = portdata->out_urbs[i]; 535 - if (this_urb && this_urb->status != -EINPROGRESS) 521 + if (this_urb && !test_bit(i, &portdata->out_busy)) 536 522 data_len += OUT_BUFLEN; 537 523 } 538 524 ··· 551 537 552 538 for (i=0; i < N_OUT_URB; i++) { 553 539 this_urb = portdata->out_urbs[i]; 554 - if (this_urb && this_urb->status == -EINPROGRESS) 540 + if (this_urb && test_bit(i, &portdata->out_busy)) 555 541 data_len += this_urb->transfer_buffer_length; 556 542 } 557 543 dbg("%s: %d", __FUNCTION__, data_len);