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

USB: cdc-acm.c: fix recursive lock in acm_start_wb error path

Fixes an obvious bug in cdc-acm by avoiding a recursive lock on
acm_start_wb()'s error path. Should apply towards 2.6.27 stable and
2.6.28.

=============================================
[ INFO: possible recursive locking detected ]
2.6.27-2-pae #109
---------------------------------------------
python/31449 is trying to acquire lock:
(&acm->write_lock){++..}, at: [<f89a0348>] acm_start_wb+0x5c/0x7b [cdc_acm]

but task is already holding lock:
(&acm->write_lock){++..}, at: [<f89a04fb>] acm_tty_write+0xe1/0x167 [cdc_acm]

other info that might help us debug this:
2 locks held by python/31449:
#0: (&tty->atomic_write_lock){--..}, at: [<c0260fae>] tty_write_lock+0x14/0x3b
#1: (&acm->write_lock){++..}, at: [<f89a04fb>] acm_tty_write+0xe1/0x167 [cdc_acm]

stack backtrace:
Pid: 31449, comm: python Not tainted 2.6.27-2-pae #109
[<c030f42f>] ? printk+0xf/0x18
[<c0149f33>] __lock_acquire+0xc7b/0x1316
[<c014a63e>] lock_acquire+0x70/0x97
[<f89a0348>] ? acm_start_wb+0x5c/0x7b [cdc_acm]
[<c0312109>] _spin_lock_irqsave+0x37/0x47
[<f89a0348>] ? acm_start_wb+0x5c/0x7b [cdc_acm]
[<f89a0348>] acm_start_wb+0x5c/0x7b [cdc_acm]
[<f89a055d>] acm_tty_write+0x143/0x167 [cdc_acm]
[<c0262a98>] write_chan+0x1cd/0x297
[<c012527e>] ? default_wake_function+0x0/0xd
[<c026111e>] tty_write+0x149/0x1b9
[<c02628cb>] ? write_chan+0x0/0x297
[<c01912c5>] ? rw_verify_area+0x76/0x98
[<c0260fd5>] ? tty_write+0x0/0x1b9
[<c01919ba>] vfs_write+0x8c/0x136
[<c0191afd>] sys_write+0x3b/0x60
[<c0103beb>] sysenter_do_call+0x12/0x3f
=======================

Signed-off-by: Brandon Philips <bphilips@suse.de>
Cc: Oliver Neukum <oliver@neukum.org>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Brandon Philips and committed by
Greg Kroah-Hartman
ad0b65ef 881e3c98

+4 -5
+4 -5
drivers/usb/class/cdc-acm.c
··· 158 158 } 159 159 160 160 /* 161 - * Finish write. 161 + * Finish write. Caller must hold acm->write_lock 162 162 */ 163 163 static void acm_write_done(struct acm *acm, struct acm_wb *wb) 164 164 { 165 - unsigned long flags; 166 - 167 - spin_lock_irqsave(&acm->write_lock, flags); 168 165 wb->use = 0; 169 166 acm->transmitting--; 170 - spin_unlock_irqrestore(&acm->write_lock, flags); 171 167 } 172 168 173 169 /* ··· 478 482 { 479 483 struct acm_wb *wb = urb->context; 480 484 struct acm *acm = wb->instance; 485 + unsigned long flags; 481 486 482 487 if (verbose || urb->status 483 488 || (urb->actual_length != urb->transfer_buffer_length)) ··· 487 490 urb->transfer_buffer_length, 488 491 urb->status); 489 492 493 + spin_lock_irqsave(&acm->write_lock, flags); 490 494 acm_write_done(acm, wb); 495 + spin_unlock_irqrestore(&acm->write_lock, flags); 491 496 if (ACM_READY(acm)) 492 497 schedule_work(&acm->work); 493 498 else