Skip to content

Commit 032aaf3

Browse files
committed
Merge tag 'tty-4.11-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
Pull tty fix from Greg KH: "Here is a single tty core revert for a patch that was reported to cause problems. The original issue is one that we have lived with for decades, so trying to scramble to fix the fix in time for 4.11-final does not make sense due to the fragility of the tty ldisc layer. Just reverting it makes sense for now" * tag 'tty-4.11-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: Revert "tty: don't panic on OOM in tty_set_ldisc()"
2 parents 4853886 + a8983d0 commit 032aaf3

File tree

1 file changed

+69
-16
lines changed

1 file changed

+69
-16
lines changed

drivers/tty/tty_ldisc.c

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,41 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
491491
tty_ldisc_debug(tty, "%p: closed\n", ld);
492492
}
493493

494+
/**
495+
* tty_ldisc_restore - helper for tty ldisc change
496+
* @tty: tty to recover
497+
* @old: previous ldisc
498+
*
499+
* Restore the previous line discipline or N_TTY when a line discipline
500+
* change fails due to an open error
501+
*/
502+
503+
static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
504+
{
505+
struct tty_ldisc *new_ldisc;
506+
int r;
507+
508+
/* There is an outstanding reference here so this is safe */
509+
old = tty_ldisc_get(tty, old->ops->num);
510+
WARN_ON(IS_ERR(old));
511+
tty->ldisc = old;
512+
tty_set_termios_ldisc(tty, old->ops->num);
513+
if (tty_ldisc_open(tty, old) < 0) {
514+
tty_ldisc_put(old);
515+
/* This driver is always present */
516+
new_ldisc = tty_ldisc_get(tty, N_TTY);
517+
if (IS_ERR(new_ldisc))
518+
panic("n_tty: get");
519+
tty->ldisc = new_ldisc;
520+
tty_set_termios_ldisc(tty, N_TTY);
521+
r = tty_ldisc_open(tty, new_ldisc);
522+
if (r < 0)
523+
panic("Couldn't open N_TTY ldisc for "
524+
"%s --- error %d.",
525+
tty_name(tty), r);
526+
}
527+
}
528+
494529
/**
495530
* tty_set_ldisc - set line discipline
496531
* @tty: the terminal to set
@@ -504,7 +539,12 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
504539

505540
int tty_set_ldisc(struct tty_struct *tty, int disc)
506541
{
507-
int retval, old_disc;
542+
int retval;
543+
struct tty_ldisc *old_ldisc, *new_ldisc;
544+
545+
new_ldisc = tty_ldisc_get(tty, disc);
546+
if (IS_ERR(new_ldisc))
547+
return PTR_ERR(new_ldisc);
508548

509549
tty_lock(tty);
510550
retval = tty_ldisc_lock(tty, 5 * HZ);
@@ -517,8 +557,7 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
517557
}
518558

519559
/* Check the no-op case */
520-
old_disc = tty->ldisc->ops->num;
521-
if (old_disc == disc)
560+
if (tty->ldisc->ops->num == disc)
522561
goto out;
523562

524563
if (test_bit(TTY_HUPPED, &tty->flags)) {
@@ -527,32 +566,42 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
527566
goto out;
528567
}
529568

530-
retval = tty_ldisc_reinit(tty, disc);
569+
old_ldisc = tty->ldisc;
570+
571+
/* Shutdown the old discipline. */
572+
tty_ldisc_close(tty, old_ldisc);
573+
574+
/* Now set up the new line discipline. */
575+
tty->ldisc = new_ldisc;
576+
tty_set_termios_ldisc(tty, disc);
577+
578+
retval = tty_ldisc_open(tty, new_ldisc);
531579
if (retval < 0) {
532580
/* Back to the old one or N_TTY if we can't */
533-
if (tty_ldisc_reinit(tty, old_disc) < 0) {
534-
pr_err("tty: TIOCSETD failed, reinitializing N_TTY\n");
535-
if (tty_ldisc_reinit(tty, N_TTY) < 0) {
536-
/* At this point we have tty->ldisc == NULL. */
537-
pr_err("tty: reinitializing N_TTY failed\n");
538-
}
539-
}
581+
tty_ldisc_put(new_ldisc);
582+
tty_ldisc_restore(tty, old_ldisc);
540583
}
541584

542-
if (tty->ldisc && tty->ldisc->ops->num != old_disc &&
543-
tty->ops->set_ldisc) {
585+
if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) {
544586
down_read(&tty->termios_rwsem);
545587
tty->ops->set_ldisc(tty);
546588
up_read(&tty->termios_rwsem);
547589
}
548590

591+
/* At this point we hold a reference to the new ldisc and a
592+
reference to the old ldisc, or we hold two references to
593+
the old ldisc (if it was restored as part of error cleanup
594+
above). In either case, releasing a single reference from
595+
the old ldisc is correct. */
596+
new_ldisc = old_ldisc;
549597
out:
550598
tty_ldisc_unlock(tty);
551599

552600
/* Restart the work queue in case no characters kick it off. Safe if
553601
already running */
554602
tty_buffer_restart_work(tty->port);
555603
err:
604+
tty_ldisc_put(new_ldisc); /* drop the extra reference */
556605
tty_unlock(tty);
557606
return retval;
558607
}
@@ -613,8 +662,10 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
613662
int retval;
614663

615664
ld = tty_ldisc_get(tty, disc);
616-
if (IS_ERR(ld))
665+
if (IS_ERR(ld)) {
666+
BUG_ON(disc == N_TTY);
617667
return PTR_ERR(ld);
668+
}
618669

619670
if (tty->ldisc) {
620671
tty_ldisc_close(tty, tty->ldisc);
@@ -626,8 +677,10 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
626677
tty_set_termios_ldisc(tty, disc);
627678
retval = tty_ldisc_open(tty, tty->ldisc);
628679
if (retval) {
629-
tty_ldisc_put(tty->ldisc);
630-
tty->ldisc = NULL;
680+
if (!WARN_ON(disc == N_TTY)) {
681+
tty_ldisc_put(tty->ldisc);
682+
tty->ldisc = NULL;
683+
}
631684
}
632685
return retval;
633686
}

0 commit comments

Comments
 (0)