Skip to content

Commit ac8f3bf

Browse files
peterhurleygregkh
authored andcommitted
n_tty: Fix poll() after buffer-limited eof push read
commit 40d5e09 ("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: 40d5e09 ("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>
1 parent 7be047e commit ac8f3bf

File tree

1 file changed

+9
-13
lines changed

1 file changed

+9
-13
lines changed

drivers/tty/n_tty.c

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2054,13 +2054,13 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
20542054
size_t eol;
20552055
size_t tail;
20562056
int ret, found = 0;
2057-
bool eof_push = 0;
20582057

20592058
/* N.B. avoid overrun if nr == 0 */
2060-
n = min(*nr, smp_load_acquire(&ldata->canon_head) - ldata->read_tail);
2061-
if (!n)
2059+
if (!*nr)
20622060
return 0;
20632061

2062+
n = min(*nr + 1, smp_load_acquire(&ldata->canon_head) - ldata->read_tail);
2063+
20642064
tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
20652065
size = min_t(size_t, tail + n, N_TTY_BUF_SIZE);
20662066

@@ -2081,12 +2081,11 @@ static int canon_copy_from_read_buf(struct tty_struct *tty,
20812081
n = eol - tail;
20822082
if (n > N_TTY_BUF_SIZE)
20832083
n += N_TTY_BUF_SIZE;
2084-
n += found;
2085-
c = n;
2084+
c = n + found;
20862085

2087-
if (found && !ldata->push && read_buf(ldata, eol) == __DISABLED_CHAR) {
2088-
n--;
2089-
eof_push = !n && ldata->read_tail != ldata->line_start;
2086+
if (!found || read_buf(ldata, eol) != __DISABLED_CHAR) {
2087+
c = min(*nr, c);
2088+
n = c;
20902089
}
20912090

20922091
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,
21162115
ldata->push = 0;
21172116
tty_audit_push(tty);
21182117
}
2119-
return eof_push ? -EAGAIN : 0;
2118+
return 0;
21202119
}
21212120

21222121
extern 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,
22732272

22742273
if (ldata->icanon && !L_EXTPROC(tty)) {
22752274
retval = canon_copy_from_read_buf(tty, &b, &nr);
2276-
if (retval == -EAGAIN) {
2277-
retval = 0;
2278-
continue;
2279-
} else if (retval)
2275+
if (retval)
22802276
break;
22812277
} else {
22822278
int uncopied;

0 commit comments

Comments
 (0)