From 42458f41d08f0873299e830464c1232a6839297d Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 7 Nov 2013 13:59:46 -0500 Subject: n_tty: Ensure reader restarts worker for next reader A departing reader must restart a flush_to_ldisc() worker _before_ the next reader enters the read loop; this is to avoid the new reader concluding no more i/o is available and prematurely exiting, when the old reader simply hasn't re-started the worker yet. Cc: stable # 3.12 Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index 7cdd1eb9406c..a2b70c97391f 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2243,6 +2243,9 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, if (time) timeout = time; } + n_tty_set_room(tty); + up_read(&tty->termios_rwsem); + mutex_unlock(&ldata->atomic_read_lock); remove_wait_queue(&tty->read_wait, &wait); @@ -2253,8 +2256,6 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, if (b - buf) retval = b - buf; - n_tty_set_room(tty); - up_read(&tty->termios_rwsem); return retval; } -- cgit v1.2.3-59-g8ed1b From 6f2225363c205e186c1465c2c7c84f17c1635504 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 8 Nov 2013 09:42:18 -0500 Subject: n_tty: Fix echo overrun tail computation Commit cbfd0340ae1993378fd47179db949e050e16e697, 'n_tty: Process echoes in blocks', introduced an error when consuming the echo buffer tail to prevent buffer overrun, where the incorrect operation code byte is checked to determine how far to advance the tail to the next echo byte. Check the correct byte for the echo operation code byte. Cc: # 3.12.x : c476f65 tty: incorrect test of echo_buf() result for ECHO_OP_START Cc: # 3.12.x Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index a2b70c97391f..d8a779d3a611 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -768,7 +768,7 @@ static size_t __process_echoes(struct tty_struct *tty) * data at the tail to prevent a subsequent overrun */ while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) { if (echo_buf(ldata, tail) == ECHO_OP_START) { - if (echo_buf(ldata, tail) == ECHO_OP_ERASE_TAB) + if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB) tail += 3; else tail += 2; -- cgit v1.2.3-59-g8ed1b From c77569d2f3ef7844ee4ac7005a57da6898b302a8 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Fri, 22 Nov 2013 07:16:25 -0500 Subject: n_tty: Fix 4096-byte canonical reads Although the maximum allowable canonical line is specified to be 255 bytes (MAX_CANON), the practical limit has actually been the size of the line discipline read buffer (N_TTY_BUF_SIZE == 4096). Commit 32f13521ca68bc624ff6effc77f308a52b038bf0, n_tty: Line copy to user buffer in canonical mode, limited the line copy to 4095 bytes. With a completely full line discipline read buffer and a userspace buffer > 4095, _no_ data was copied, and the read() syscall returned 0, indicating EOF. Fix the interval arithmetic to compute the correct number of bytes to copy to userspace in the range [1..4096]. Cc: # 3.12.x Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index d8a779d3a611..c222a561c5ac 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -1998,7 +1998,10 @@ static int canon_copy_from_read_buf(struct tty_struct *tty, found = 1; size = N_TTY_BUF_SIZE - tail; - n = (found + eol + size) & (N_TTY_BUF_SIZE - 1); + n = eol - tail; + if (n > 4096) + n += 4096; + n += found; c = n; if (found && read_buf(ldata, eol) == __DISABLED_CHAR) { -- cgit v1.2.3-59-g8ed1b From f3014127ada32f51ce0baf0bee4c6324a601ef59 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 7 Nov 2013 09:58:14 -0800 Subject: tty/serial/8250: fix typo in help text Commit 9326b047e4fd4a8da72e59d913214a1803e9709c includes a typo of "8350_core" instead of "8250_core", so correct it. Fixes kernel bugzilla #60724: https://bugzilla.kernel.org/show_bug.cgi?id=60724 Reported-by: Christoph Biedl Signed-off-by: Randy Dunlap Cc: Jiri Slaby Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/8250/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index f3b306efaa59..23329918f229 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -41,7 +41,7 @@ config SERIAL_8250_DEPRECATED_OPTIONS accept kernel parameters in both forms like 8250_core.nr_uarts=4 and 8250.nr_uarts=4. We now renamed the module back to 8250, but if anybody noticed in 3.7 and changed their userspace we still have to - keep the 8350_core.* options around until they revert the changes + keep the 8250_core.* options around until they revert the changes they already did. If 8250 is built as a module, this adds 8250_core alias instead. -- cgit v1.2.3-59-g8ed1b From c284ee2cf12b55fa8496b2d098bf0938688f1c1c Mon Sep 17 00:00:00 2001 From: Chao Bi Date: Thu, 17 Oct 2013 15:08:27 +0800 Subject: n_gsm: race between ld close and gsmtty open ttyA has ld associated to n_gsm, when ttyA is closing, it triggers to release gsmttyB's ld data dlci[B], then race would happen if gsmttyB is opening in parallel. Here are race cases we found recently in test: CASE #1 ==================================================================== releasing dlci[B] race with gsmtty_install(gsmttyB), then panic in gsmtty_open(gsmttyB), as below: tty_release(ttyA) tty_open(gsmttyB) | | ----- gsmtty_install(gsmttyB) | | ----- gsm_dlci_alloc(gsmttyB) => alloc dlci[B] tty_ldisc_release(ttyA) ----- | | gsm_dlci_release(dlci[B]) ----- | | gsm_dlci_free(dlci[B]) ----- | | ----- gsmtty_open(gsmttyB) gsmtty_open() { struct gsm_dlci *dlci = tty->driver_data; => here it uses dlci[B] ... } In gsmtty_open(gsmttyA), it uses dlci[B] which was release, so hit a panic. ===================================================================== CASE #2 ===================================================================== releasing dlci[0] race with gsmtty_install(gsmttyB), then panic in gsmtty_open(), as below: tty_release(ttyA) tty_open(gsmttyB) | | ----- gsmtty_install(gsmttyB) | | ----- gsm_dlci_alloc(gsmttyB) => alloc dlci[B] | | ----- gsmtty_open(gsmttyB) fail | | ----- tty_release(gsmttyB) | | ----- gsmtty_close(gsmttyB) | | ----- gsmtty_detach_dlci(dlci[B]) | | ----- dlci_put(dlci[B]) | | tty_ldisc_release(ttyA) ----- | | gsm_dlci_release(dlci[0]) ----- | | gsm_dlci_free(dlci[0]) ----- | | ----- dlci_put(dlci[0]) In gsmtty_detach_dlci(dlci[B]), it tries to use dlci[0] which was released, then hit panic. ===================================================================== IMHO, n_gsm tty operations would refer released ldisc, as long as gsm_dlci_release() has chance to release ldisc data when some gsmtty operations are not completed.. This patch is try to avoid it by: 1) in n_gsm driver, use a global gsm spin lock to avoid gsm_dlci_release() run in parallel with gsmtty_install(); 2) Increase dlci's ref count in gsmtty_install() instead of in gsmtty_open(), the purpose is to prevent gsm_dlci_release() releasing dlci after gsmtty_install() allocats dlci but before gsmtty_open increases dlci's ref count; 3) Decrease dlci's ref count in gsmtty_remove(), which is a tty framework api, and this is the opposite process of step 2). Signed-off-by: Chao Bi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index c0f76da55304..069bfd654485 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2054,9 +2054,11 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) dlci->state == DLCI_CLOSED); } /* Free up any link layer users */ + spin_lock(&gsm->lock); for (i = 0; i < NUM_DLCI; i++) if (gsm->dlci[i]) gsm_dlci_release(gsm->dlci[i]); + spin_unlock(&gsm->lock); /* Now wipe the queues */ list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list) kfree(txq); @@ -2909,23 +2911,33 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty) This is ok from a locking perspective as we don't have to worry about this if DLCI0 is lost */ - if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) + spin_lock(&gsm->lock); + if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) { + spin_unlock(&gsm->lock); return -EL2NSYNC; + } dlci = gsm->dlci[line]; if (dlci == NULL) { alloc = true; dlci = gsm_dlci_alloc(gsm, line); } - if (dlci == NULL) + if (dlci == NULL) { + spin_unlock(&gsm->lock); return -ENOMEM; + } ret = tty_port_install(&dlci->port, driver, tty); if (ret) { if (alloc) dlci_put(dlci); + spin_unlock(&gsm->lock); return ret; } + dlci_get(dlci); + dlci_get(gsm->dlci[0]); + mux_get(gsm); tty->driver_data = dlci; + spin_unlock(&gsm->lock); return 0; } @@ -2936,9 +2948,6 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) struct tty_port *port = &dlci->port; port->count++; - dlci_get(dlci); - dlci_get(dlci->gsm->dlci[0]); - mux_get(dlci->gsm); tty_port_tty_set(port, tty); dlci->modem_rx = 0; @@ -2965,7 +2974,7 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&dlci->mutex); gsm = dlci->gsm; if (tty_port_close_start(&dlci->port, tty, filp) == 0) - goto out; + return; gsm_dlci_begin_close(dlci); if (test_bit(ASYNCB_INITIALIZED, &dlci->port.flags)) { if (C_HUPCL(tty)) @@ -2973,10 +2982,7 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp) } tty_port_close_end(&dlci->port, tty); tty_port_tty_set(&dlci->port, NULL); -out: - dlci_put(dlci); - dlci_put(gsm->dlci[0]); - mux_put(gsm); + return; } static void gsmtty_hangup(struct tty_struct *tty) @@ -3153,6 +3159,16 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state) return gsmtty_modem_update(dlci, encode); } +static void gsmtty_remove(struct tty_driver *driver, struct tty_struct *tty) +{ + struct gsm_dlci *dlci = tty->driver_data; + struct gsm_mux *gsm = dlci->gsm; + + dlci_put(dlci); + dlci_put(gsm->dlci[0]); + mux_put(gsm); + driver->ttys[tty->index] = NULL; +} /* Virtual ttys for the demux */ static const struct tty_operations gsmtty_ops = { @@ -3172,6 +3188,7 @@ static const struct tty_operations gsmtty_ops = { .tiocmget = gsmtty_tiocmget, .tiocmset = gsmtty_tiocmset, .break_ctl = gsmtty_break_ctl, + .remove = gsmtty_remove, }; -- cgit v1.2.3-59-g8ed1b From dc1dc2f8a5dd863bf2e79f338fc3ae29e99c683a Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 22 Nov 2013 16:47:26 +0100 Subject: TTY: pmac_zilog, check existence of ports in pmz_console_init() When booting a multi-platform m68k kernel on a non-Mac with "console=ttyS0" on the kernel command line, it crashes with: Unable to handle kernel NULL pointer dereference at virtual address (null) Oops: 00000000 PC: [<0013ad28>] __pmz_startup+0x32/0x2a0 ... Call Trace: [<002c5d3e>] pmz_console_setup+0x64/0xe4 The normal tty driver doesn't crash, because init_pmz() checks pmz_ports_count again after calling pmz_probe(). In the serial console initialization path, pmz_console_init() doesn't do this, causing the driver to crash later. Add a check for pmz_ports_count to fix this. Signed-off-by: Geert Uytterhoeven Cc: Finn Thain Cc: Benjamin Herrenschmidt Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serial/pmac_zilog.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c index 481b781b26e3..e9d420ff3931 100644 --- a/drivers/tty/serial/pmac_zilog.c +++ b/drivers/tty/serial/pmac_zilog.c @@ -2052,6 +2052,9 @@ static int __init pmz_console_init(void) /* Probe ports */ pmz_probe(); + if (pmz_ports_count == 0) + return -ENODEV; + /* TODO: Autoprobe console based on OF */ /* pmz_console.index = i; */ register_console(&pmz_console); -- cgit v1.2.3-59-g8ed1b From 3dcf344bef2e9b9910817b210783d79c383bc849 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Fri, 22 Nov 2013 16:47:27 +0100 Subject: TTY: amiserial, add missing platform check When booting a multi-platform m68k kernel on a non-Amiga with "console=ttyS0" on the kernel command line, it crashes with: Unable to handle kernel access at virtual address 81dff01c Oops: 00000000 PC: [<001e09a8>] serial_console_write+0xc/0x70 Add the missing platform check to amiserial_console_init() to fix this. Signed-off-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/amiserial.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/tty/amiserial.c b/drivers/tty/amiserial.c index 2b86f8e0fb58..71630a2af42c 100644 --- a/drivers/tty/amiserial.c +++ b/drivers/tty/amiserial.c @@ -1855,6 +1855,9 @@ static struct console sercons = { */ static int __init amiserial_console_init(void) { + if (!MACH_IS_AMIGA) + return -ENODEV; + register_console(&sercons); return 0; } -- cgit v1.2.3-59-g8ed1b From d4855e1fc03c2bb32dd64badf51cec5a2a26ab2a Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 19 Nov 2013 08:46:27 -0500 Subject: tty: Reset hupped state on open A common security idiom is to hangup the current tty (via vhangup()) after forking but before execing a root shell. This hangs up any existing opens which other processes may have and ensures subsequent opens have the necessary permissions to open the root shell tty/pty. Reset the TTY_HUPPED state after the driver has successfully returned the opened tty (perform the reset while the tty is locked to avoid racing with concurrent hangups). Reported-by: Heorhi Valakhanovich Signed-off-by: Peter Hurley Cc: stable # 3.12 Tested-by: Heorhi Valakhanovich Signed-off-by: Greg Kroah-Hartman --- drivers/tty/tty_io.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 3a1a01af9a80..c74a00ad7add 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2086,6 +2086,7 @@ retry_open: filp->f_op = &tty_fops; goto retry_open; } + clear_bit(TTY_HUPPED, &tty->flags); tty_unlock(tty); -- cgit v1.2.3-59-g8ed1b From aebf045382edb32bd763d8edea02a559e7dc41e9 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Thu, 7 Nov 2013 14:01:57 -0500 Subject: n_tty: Protect minimum_to_wake reset for concurrent readers With multiple, concurrent readers (each waiting to acquire the atomic_read_lock mutex), a departing reader may mistakenly reset minimum_to_wake after a new reader has already set a new value. Protect the minimum_to_wake reset with the atomic_read_lock critical section. Signed-off-by: Peter Hurley Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_tty.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index c222a561c5ac..0f74945af624 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -2249,12 +2249,12 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file, n_tty_set_room(tty); up_read(&tty->termios_rwsem); - mutex_unlock(&ldata->atomic_read_lock); remove_wait_queue(&tty->read_wait, &wait); - if (!waitqueue_active(&tty->read_wait)) ldata->minimum_to_wake = minimum; + mutex_unlock(&ldata->atomic_read_lock); + __set_current_state(TASK_RUNNING); if (b - buf) retval = b - buf; -- cgit v1.2.3-59-g8ed1b From c42b4e6501cdaabee8cc292ae1ef0f66bf4825c1 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 25 Nov 2013 18:30:21 -0800 Subject: Revert "n_gsm: race between ld close and gsmtty open" This reverts commit c284ee2cf12b55fa8496b2d098bf0938688f1c1c. Turns out the locking was incorrect. Reported-by: Fengguang Wu Cc: Chao Bi Signed-off-by: Greg Kroah-Hartman --- drivers/tty/n_gsm.c | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c index 069bfd654485..c0f76da55304 100644 --- a/drivers/tty/n_gsm.c +++ b/drivers/tty/n_gsm.c @@ -2054,11 +2054,9 @@ void gsm_cleanup_mux(struct gsm_mux *gsm) dlci->state == DLCI_CLOSED); } /* Free up any link layer users */ - spin_lock(&gsm->lock); for (i = 0; i < NUM_DLCI; i++) if (gsm->dlci[i]) gsm_dlci_release(gsm->dlci[i]); - spin_unlock(&gsm->lock); /* Now wipe the queues */ list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list) kfree(txq); @@ -2911,33 +2909,23 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty) This is ok from a locking perspective as we don't have to worry about this if DLCI0 is lost */ - spin_lock(&gsm->lock); - if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) { - spin_unlock(&gsm->lock); + if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) return -EL2NSYNC; - } dlci = gsm->dlci[line]; if (dlci == NULL) { alloc = true; dlci = gsm_dlci_alloc(gsm, line); } - if (dlci == NULL) { - spin_unlock(&gsm->lock); + if (dlci == NULL) return -ENOMEM; - } ret = tty_port_install(&dlci->port, driver, tty); if (ret) { if (alloc) dlci_put(dlci); - spin_unlock(&gsm->lock); return ret; } - dlci_get(dlci); - dlci_get(gsm->dlci[0]); - mux_get(gsm); tty->driver_data = dlci; - spin_unlock(&gsm->lock); return 0; } @@ -2948,6 +2936,9 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp) struct tty_port *port = &dlci->port; port->count++; + dlci_get(dlci); + dlci_get(dlci->gsm->dlci[0]); + mux_get(dlci->gsm); tty_port_tty_set(port, tty); dlci->modem_rx = 0; @@ -2974,7 +2965,7 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp) mutex_unlock(&dlci->mutex); gsm = dlci->gsm; if (tty_port_close_start(&dlci->port, tty, filp) == 0) - return; + goto out; gsm_dlci_begin_close(dlci); if (test_bit(ASYNCB_INITIALIZED, &dlci->port.flags)) { if (C_HUPCL(tty)) @@ -2982,7 +2973,10 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp) } tty_port_close_end(&dlci->port, tty); tty_port_tty_set(&dlci->port, NULL); - return; +out: + dlci_put(dlci); + dlci_put(gsm->dlci[0]); + mux_put(gsm); } static void gsmtty_hangup(struct tty_struct *tty) @@ -3159,16 +3153,6 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state) return gsmtty_modem_update(dlci, encode); } -static void gsmtty_remove(struct tty_driver *driver, struct tty_struct *tty) -{ - struct gsm_dlci *dlci = tty->driver_data; - struct gsm_mux *gsm = dlci->gsm; - - dlci_put(dlci); - dlci_put(gsm->dlci[0]); - mux_put(gsm); - driver->ttys[tty->index] = NULL; -} /* Virtual ttys for the demux */ static const struct tty_operations gsmtty_ops = { @@ -3188,7 +3172,6 @@ static const struct tty_operations gsmtty_ops = { .tiocmget = gsmtty_tiocmget, .tiocmset = gsmtty_tiocmset, .break_ctl = gsmtty_break_ctl, - .remove = gsmtty_remove, }; -- cgit v1.2.3-59-g8ed1b