| From bc983fffbaa0a75a41289a9bbee277e06e15e83c Mon Sep 17 00:00:00 2001 |
| From: Zhenzhong Duan <zhenzhong.duan@gmail.com> |
| Date: Mon, 13 Jan 2020 11:48:42 +0800 |
| Subject: [PATCH] ttyprintk: fix a potential deadlock in interrupt context |
| issue |
| |
| commit 9a655c77ff8fc65699a3f98e237db563b37c439b upstream. |
| |
| tpk_write()/tpk_close() could be interrupted when holding a mutex, then |
| in timer handler tpk_write() may be called again trying to acquire same |
| mutex, lead to deadlock. |
| |
| Google syzbot reported this issue with CONFIG_DEBUG_ATOMIC_SLEEP |
| enabled: |
| |
| BUG: sleeping function called from invalid context at |
| kernel/locking/mutex.c:938 |
| in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 0, name: swapper/1 |
| 1 lock held by swapper/1/0: |
| ... |
| Call Trace: |
| <IRQ> |
| dump_stack+0x197/0x210 |
| ___might_sleep.cold+0x1fb/0x23e |
| __might_sleep+0x95/0x190 |
| __mutex_lock+0xc5/0x13c0 |
| mutex_lock_nested+0x16/0x20 |
| tpk_write+0x5d/0x340 |
| resync_tnc+0x1b6/0x320 |
| call_timer_fn+0x1ac/0x780 |
| run_timer_softirq+0x6c3/0x1790 |
| __do_softirq+0x262/0x98c |
| irq_exit+0x19b/0x1e0 |
| smp_apic_timer_interrupt+0x1a3/0x610 |
| apic_timer_interrupt+0xf/0x20 |
| </IRQ> |
| |
| See link https://syzkaller.appspot.com/bug?extid=2eeef62ee31f9460ad65 for |
| more details. |
| |
| Fix it by using spinlock in process context instead of mutex and having |
| interrupt disabled in critical section. |
| |
| Reported-by: syzbot+2eeef62ee31f9460ad65@syzkaller.appspotmail.com |
| Signed-off-by: Zhenzhong Duan <zhenzhong.duan@gmail.com> |
| Cc: Arnd Bergmann <arnd@arndb.de> |
| Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Link: https://lore.kernel.org/r/20200113034842.435-1-zhenzhong.duan@gmail.com |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/drivers/char/ttyprintk.c b/drivers/char/ttyprintk.c |
| index 4f24e46ebe7c..56db949a7b70 100644 |
| --- a/drivers/char/ttyprintk.c |
| +++ b/drivers/char/ttyprintk.c |
| @@ -15,10 +15,11 @@ |
| #include <linux/serial.h> |
| #include <linux/tty.h> |
| #include <linux/module.h> |
| +#include <linux/spinlock.h> |
| |
| struct ttyprintk_port { |
| struct tty_port port; |
| - struct mutex port_write_mutex; |
| + spinlock_t spinlock; |
| }; |
| |
| static struct ttyprintk_port tpk_port; |
| @@ -99,11 +100,12 @@ static int tpk_open(struct tty_struct *tty, struct file *filp) |
| static void tpk_close(struct tty_struct *tty, struct file *filp) |
| { |
| struct ttyprintk_port *tpkp = tty->driver_data; |
| + unsigned long flags; |
| |
| - mutex_lock(&tpkp->port_write_mutex); |
| + spin_lock_irqsave(&tpkp->spinlock, flags); |
| /* flush tpk_printk buffer */ |
| tpk_printk(NULL, 0); |
| - mutex_unlock(&tpkp->port_write_mutex); |
| + spin_unlock_irqrestore(&tpkp->spinlock, flags); |
| |
| tty_port_close(&tpkp->port, tty, filp); |
| } |
| @@ -115,13 +117,14 @@ static int tpk_write(struct tty_struct *tty, |
| const unsigned char *buf, int count) |
| { |
| struct ttyprintk_port *tpkp = tty->driver_data; |
| + unsigned long flags; |
| int ret; |
| |
| |
| /* exclusive use of tpk_printk within this tty */ |
| - mutex_lock(&tpkp->port_write_mutex); |
| + spin_lock_irqsave(&tpkp->spinlock, flags); |
| ret = tpk_printk(buf, count); |
| - mutex_unlock(&tpkp->port_write_mutex); |
| + spin_unlock_irqrestore(&tpkp->spinlock, flags); |
| |
| return ret; |
| } |
| @@ -171,7 +174,7 @@ static int __init ttyprintk_init(void) |
| { |
| int ret = -ENOMEM; |
| |
| - mutex_init(&tpk_port.port_write_mutex); |
| + spin_lock_init(&tpk_port.spinlock); |
| |
| ttyprintk_driver = tty_alloc_driver(1, |
| TTY_DRIVER_RESET_TERMIOS | |
| -- |
| 2.7.4 |
| |