mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	Revert "tty: don't panic on OOM in tty_set_ldisc()"
This reverts commit5362544bebas it is reported to cause a reproducable crash. Fixes:5362544beb("tty: don't panic on OOM in tty_set_ldisc()") Reported-by: Vegard Nossum <vegard.nossum@gmail.com> Cc: Dmitry Vyukov <dvyukov@google.com> Cc: <syzkaller@googlegroups.com> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Cc: Jiri Slaby <jslaby@suse.com> Cc: Peter Hurley <peter@hurleysoftware.com> Cc: One Thousand Gnomes <gnomes@lxorguk.ukuu.org.uk> Cc: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org
This commit is contained in:
		
							parent
							
								
									a71c9a1c77
								
							
						
					
					
						commit
						a8983d01f9
					
				
					 1 changed files with 69 additions and 16 deletions
				
			
		| 
						 | 
				
			
			@ -491,6 +491,41 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
 | 
			
		|||
	tty_ldisc_debug(tty, "%p: closed\n", ld);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *	tty_ldisc_restore	-	helper for tty ldisc change
 | 
			
		||||
 *	@tty: tty to recover
 | 
			
		||||
 *	@old: previous ldisc
 | 
			
		||||
 *
 | 
			
		||||
 *	Restore the previous line discipline or N_TTY when a line discipline
 | 
			
		||||
 *	change fails due to an open error
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
 | 
			
		||||
{
 | 
			
		||||
	struct tty_ldisc *new_ldisc;
 | 
			
		||||
	int r;
 | 
			
		||||
 | 
			
		||||
	/* There is an outstanding reference here so this is safe */
 | 
			
		||||
	old = tty_ldisc_get(tty, old->ops->num);
 | 
			
		||||
	WARN_ON(IS_ERR(old));
 | 
			
		||||
	tty->ldisc = old;
 | 
			
		||||
	tty_set_termios_ldisc(tty, old->ops->num);
 | 
			
		||||
	if (tty_ldisc_open(tty, old) < 0) {
 | 
			
		||||
		tty_ldisc_put(old);
 | 
			
		||||
		/* This driver is always present */
 | 
			
		||||
		new_ldisc = tty_ldisc_get(tty, N_TTY);
 | 
			
		||||
		if (IS_ERR(new_ldisc))
 | 
			
		||||
			panic("n_tty: get");
 | 
			
		||||
		tty->ldisc = new_ldisc;
 | 
			
		||||
		tty_set_termios_ldisc(tty, N_TTY);
 | 
			
		||||
		r = tty_ldisc_open(tty, new_ldisc);
 | 
			
		||||
		if (r < 0)
 | 
			
		||||
			panic("Couldn't open N_TTY ldisc for "
 | 
			
		||||
			      "%s --- error %d.",
 | 
			
		||||
			      tty_name(tty), r);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 *	tty_set_ldisc		-	set line discipline
 | 
			
		||||
 *	@tty: the terminal to set
 | 
			
		||||
| 
						 | 
				
			
			@ -504,7 +539,12 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
 | 
			
		|||
 | 
			
		||||
int tty_set_ldisc(struct tty_struct *tty, int disc)
 | 
			
		||||
{
 | 
			
		||||
	int retval, old_disc;
 | 
			
		||||
	int retval;
 | 
			
		||||
	struct tty_ldisc *old_ldisc, *new_ldisc;
 | 
			
		||||
 | 
			
		||||
	new_ldisc = tty_ldisc_get(tty, disc);
 | 
			
		||||
	if (IS_ERR(new_ldisc))
 | 
			
		||||
		return PTR_ERR(new_ldisc);
 | 
			
		||||
 | 
			
		||||
	tty_lock(tty);
 | 
			
		||||
	retval = tty_ldisc_lock(tty, 5 * HZ);
 | 
			
		||||
| 
						 | 
				
			
			@ -517,8 +557,7 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	/* Check the no-op case */
 | 
			
		||||
	old_disc = tty->ldisc->ops->num;
 | 
			
		||||
	if (old_disc == disc)
 | 
			
		||||
	if (tty->ldisc->ops->num == disc)
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	if (test_bit(TTY_HUPPED, &tty->flags)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -527,25 +566,34 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
 | 
			
		|||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	retval = tty_ldisc_reinit(tty, disc);
 | 
			
		||||
	old_ldisc = tty->ldisc;
 | 
			
		||||
 | 
			
		||||
	/* Shutdown the old discipline. */
 | 
			
		||||
	tty_ldisc_close(tty, old_ldisc);
 | 
			
		||||
 | 
			
		||||
	/* Now set up the new line discipline. */
 | 
			
		||||
	tty->ldisc = new_ldisc;
 | 
			
		||||
	tty_set_termios_ldisc(tty, disc);
 | 
			
		||||
 | 
			
		||||
	retval = tty_ldisc_open(tty, new_ldisc);
 | 
			
		||||
	if (retval < 0) {
 | 
			
		||||
		/* Back to the old one or N_TTY if we can't */
 | 
			
		||||
		if (tty_ldisc_reinit(tty, old_disc) < 0) {
 | 
			
		||||
			pr_err("tty: TIOCSETD failed, reinitializing N_TTY\n");
 | 
			
		||||
			if (tty_ldisc_reinit(tty, N_TTY) < 0) {
 | 
			
		||||
				/* At this point we have tty->ldisc == NULL. */
 | 
			
		||||
				pr_err("tty: reinitializing N_TTY failed\n");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		tty_ldisc_put(new_ldisc);
 | 
			
		||||
		tty_ldisc_restore(tty, old_ldisc);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (tty->ldisc && tty->ldisc->ops->num != old_disc &&
 | 
			
		||||
	    tty->ops->set_ldisc) {
 | 
			
		||||
	if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) {
 | 
			
		||||
		down_read(&tty->termios_rwsem);
 | 
			
		||||
		tty->ops->set_ldisc(tty);
 | 
			
		||||
		up_read(&tty->termios_rwsem);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* At this point we hold a reference to the new ldisc and a
 | 
			
		||||
	   reference to the old ldisc, or we hold two references to
 | 
			
		||||
	   the old ldisc (if it was restored as part of error cleanup
 | 
			
		||||
	   above). In either case, releasing a single reference from
 | 
			
		||||
	   the old ldisc is correct. */
 | 
			
		||||
	new_ldisc = old_ldisc;
 | 
			
		||||
out:
 | 
			
		||||
	tty_ldisc_unlock(tty);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -553,6 +601,7 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
 | 
			
		|||
	   already running */
 | 
			
		||||
	tty_buffer_restart_work(tty->port);
 | 
			
		||||
err:
 | 
			
		||||
	tty_ldisc_put(new_ldisc);	/* drop the extra reference */
 | 
			
		||||
	tty_unlock(tty);
 | 
			
		||||
	return retval;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -613,8 +662,10 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
 | 
			
		|||
	int retval;
 | 
			
		||||
 | 
			
		||||
	ld = tty_ldisc_get(tty, disc);
 | 
			
		||||
	if (IS_ERR(ld))
 | 
			
		||||
	if (IS_ERR(ld)) {
 | 
			
		||||
		BUG_ON(disc == N_TTY);
 | 
			
		||||
		return PTR_ERR(ld);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (tty->ldisc) {
 | 
			
		||||
		tty_ldisc_close(tty, tty->ldisc);
 | 
			
		||||
| 
						 | 
				
			
			@ -626,8 +677,10 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
 | 
			
		|||
	tty_set_termios_ldisc(tty, disc);
 | 
			
		||||
	retval = tty_ldisc_open(tty, tty->ldisc);
 | 
			
		||||
	if (retval) {
 | 
			
		||||
		tty_ldisc_put(tty->ldisc);
 | 
			
		||||
		tty->ldisc = NULL;
 | 
			
		||||
		if (!WARN_ON(disc == N_TTY)) {
 | 
			
		||||
			tty_ldisc_put(tty->ldisc);
 | 
			
		||||
			tty->ldisc = NULL;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return retval;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue