aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Hurley <peter@hurleysoftware.com>2015-11-27 13:59:20 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-12-13 00:42:31 -0500
commitac8f3bf8832a405cc6e4dccb1d26d5cb2994d234 (patch)
tree7f67622cb1a6b647b5bca67a8eda9c79b18e534e
parent7be047e035dc4fb1536f1694cbb932f881533ab2 (diff)
n_tty: Fix poll() after buffer-limited eof push read
commit 40d5e0905a03 ("n_tty: Fix EOF push handling") fixed EOF push for reads. However, that approach still allows a condition mismatch between poll() and read(), where poll() returns POLLIN but read() blocks. This state can happen when a previous read() returned because the user buffer was full and the next character was an EOF not at the beginning of the line. While the next read() will properly identify the condition and advance the read buffer tail without improperly indicating an EOF file condition (ie., read() will not mistakenly return 0), poll() will mistakenly indicate POLLIN. Although a possible solution would be to peek at the input buffer in n_tty_poll(), the better solution in this patch is to eat the EOF during the previous read() (ie., fix the problem by eliminating the condition). The current canon line buffer copy limits the scan for next end-of-line to the smaller of either, a. the remaining user buffer size b. completed lines in the input buffer When the remaining user buffer size is exactly one less than the end-of-line marked by EOF push, the EOF is not scanned nor skipped but left for subsequent reads. In the example below, the scan index 'eol' has stopped at the EOF because it is past the scan limit of 5 (not because it has found the next set bit in read_flags) user buffer [*nr = 5] _ _ _ _ _ read_flags 0 0 0 0 0 1 input buffer h e l l o [EOF] ^ ^ / / tail eol result: found = 0, tail += 5, *nr += 5 Instead, allow the scan to peek ahead 1 byte (while still limiting the scan to completed lines in the input buffer). For the example above, result: found = 1, tail += 6, *nr += 5 Because the scan limit is now bumped +1 byte, when the scan is completed, the tail advance and the user buffer copy limit is re-clamped to *nr when EOF is _not_ found. Fixes: 40d5e0905a03 ("n_tty: Fix EOF push handling") Cc: <stable@vger.kernel.org> # 3.12+ Signed-off-by: Peter Hurley <peter@hurleysoftware.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/tty/n_tty.c22
1 files changed, 9 insertions, 13 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index ed776149261e..e49c2bce551d 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -2054,13 +2054,13 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
2054 size_t eol; 2054 size_t eol;
2055 size_t tail; 2055 size_t tail;
2056 int ret, found = 0; 2056 int ret, found = 0;
2057 bool eof_push = 0;
2058 2057
2059 /* N.B. avoid overrun if nr == 0 */ 2058 /* N.B. avoid overrun if nr == 0 */
2060 n = min(*nr, smp_load_acquire(&ldata->canon_head) - ldata->read_tail); 2059 if (!*nr)
2061 if (!n)
2062 return 0; 2060 return 0;
2063 2061
2062 n = min(*nr + 1, smp_load_acquire(&ldata->canon_head) - ldata->read_tail);
2063
2064 tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1); 2064 tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
2065 size = min_t(size_t, tail + n, N_TTY_BUF_SIZE); 2065 size = min_t(size_t, tail + n, N_TTY_BUF_SIZE);
2066 2066
@@ -2081,12 +2081,11 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
2081 n = eol - tail; 2081 n = eol - tail;
2082 if (n > N_TTY_BUF_SIZE) 2082 if (n > N_TTY_BUF_SIZE)
2083 n += N_TTY_BUF_SIZE; 2083 n += N_TTY_BUF_SIZE;
2084 n += found; 2084 c = n + found;
2085 c = n;
2086 2085
2087 if (found && !ldata->push && read_buf(ldata, eol) == __DISABLED_CHAR) { 2086 if (!found || read_buf(ldata, eol) != __DISABLED_CHAR) {
2088 n--; 2087 c = min(*nr, c);
2089 eof_push = !n && ldata->read_tail != ldata->line_start; 2088 n = c;
2090 } 2089 }
2091 2090
2092 n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu size:%zu more:%zu\n", 2091 n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu size:%zu more:%zu\n",
@@ -2116,7 +2115,7 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
2116 ldata->push = 0; 2115 ldata->push = 0;
2117 tty_audit_push(tty); 2116 tty_audit_push(tty);
2118 } 2117 }
2119 return eof_push ? -EAGAIN : 0; 2118 return 0;
2120} 2119}
2121 2120
2122extern ssize_t redirected_tty_write(struct file *, const char __user *, 2121extern ssize_t redirected_tty_write(struct file *, const char __user *,
@@ -2273,10 +2272,7 @@ static ssize_t n_tty_read(struct tty_struct *tty, struct file *file,
2273 2272
2274 if (ldata->icanon && !L_EXTPROC(tty)) { 2273 if (ldata->icanon && !L_EXTPROC(tty)) {
2275 retval = canon_copy_from_read_buf(tty, &b, &nr); 2274 retval = canon_copy_from_read_buf(tty, &b, &nr);
2276 if (retval == -EAGAIN) { 2275 if (retval)
2277 retval = 0;
2278 continue;
2279 } else if (retval)
2280 break; 2276 break;
2281 } else { 2277 } else {
2282 int uncopied; 2278 int uncopied;