forked from mirrors/linux
		
	tty: Hold tty_ldisc_lock() during tty_reopen()
tty_ldisc_reinit() doesn't race with neither tty_ldisc_hangup()
nor set_ldisc() nor tty_ldisc_release() as they use tty lock.
But it races with anyone who expects line discipline to be the same
after hoding read semaphore in tty_ldisc_ref().
We've seen the following crash on v4.9.108 stable:
BUG: unable to handle kernel paging request at 0000000000002260
IP: [..] n_tty_receive_buf_common+0x5f/0x86d
Workqueue: events_unbound flush_to_ldisc
Call Trace:
 [..] n_tty_receive_buf2
 [..] tty_ldisc_receive_buf
 [..] flush_to_ldisc
 [..] process_one_work
 [..] worker_thread
 [..] kthread
 [..] ret_from_fork
tty_ldisc_reinit() should be called with ldisc_sem hold for writing,
which will protect any reader against line discipline changes.
Cc: Jiri Slaby <jslaby@suse.com>
Cc: stable@vger.kernel.org # b027e2298b ("tty: fix data race between tty_init_dev and flush of buf")
Reviewed-by: Jiri Slaby <jslaby@suse.cz>
Reported-by: syzbot+3aa9784721dfb90e984d@syzkaller.appspotmail.com
Tested-by: Mark Rutland <mark.rutland@arm.com>
Tested-by: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>
Signed-off-by: Dmitry Safonov <dima@arista.com>
Tested-by: Tycho Andersen <tycho@tycho.ws>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
			
			
This commit is contained in:
		
							parent
							
								
									231f8fd0cc
								
							
						
					
					
						commit
						83d817f410
					
				
					 1 changed files with 7 additions and 2 deletions
				
			
		| 
						 | 
					@ -1268,15 +1268,20 @@ static int tty_reopen(struct tty_struct *tty)
 | 
				
			||||||
	if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
 | 
						if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
 | 
				
			||||||
		return -EBUSY;
 | 
							return -EBUSY;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tty->count++;
 | 
						retval = tty_ldisc_lock(tty, 5 * HZ);
 | 
				
			||||||
 | 
						if (retval)
 | 
				
			||||||
 | 
							return retval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tty->count++;
 | 
				
			||||||
	if (tty->ldisc)
 | 
						if (tty->ldisc)
 | 
				
			||||||
		return 0;
 | 
							goto out_unlock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	retval = tty_ldisc_reinit(tty, tty->termios.c_line);
 | 
						retval = tty_ldisc_reinit(tty, tty->termios.c_line);
 | 
				
			||||||
	if (retval)
 | 
						if (retval)
 | 
				
			||||||
		tty->count--;
 | 
							tty->count--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					out_unlock:
 | 
				
			||||||
 | 
						tty_ldisc_unlock(tty);
 | 
				
			||||||
	return retval;
 | 
						return retval;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue