Patch "tty: n_tty: do not look ahead for EOL character past the end of the buffer" has been added to the 5.15-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    tty: n_tty: do not look ahead for EOL character past the end of the buffer

to the 5.15-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     tty-n_tty-do-not-look-ahead-for-eol-character-past-t.patch
and it can be found in the queue-5.15 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 89d7ab6d588819c15092e1ac731822cfc115c1ad
Author: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
Date:   Tue Feb 15 15:28:00 2022 -0800

    tty: n_tty: do not look ahead for EOL character past the end of the buffer
    
    [ Upstream commit 3593030761630e09200072a4bd06468892c27be3 ]
    
    Daniel Gibson reports that the n_tty code gets line termination wrong in
    very specific cases:
    
     "If you feed a line with exactly 64 chars + terminating newline, and
      directly afterwards (without reading) another line into a pseudo
      terminal, the the first read() on the other side will return the 64
      char line *without* terminating newline, and the next read() will
      return the missing terminating newline AND the complete next line (if
      it fits in the buffer)"
    
    and bisected the behavior to commit 3b830a9c34d5 ("tty: convert
    tty_ldisc_ops 'read()' function to take a kernel pointer").
    
    Now, digging deeper, it turns out that the behavior isn't exactly new:
    what changed in commit 3b830a9c34d5 was that the tty line discipline
    .read() function is now passed an intermediate kernel buffer rather than
    the final user space buffer.
    
    And that intermediate kernel buffer is 64 bytes in size - thus that
    special case with exactly 64 bytes plus terminating newline.
    
    The same problem did exist before, but historically the boundary was not
    the 64-byte chunk, but the user-supplied buffer size, which is obviously
    generally bigger (and potentially bigger than N_TTY_BUF_SIZE, which
    would hide the issue entirely).
    
    The reason is that the n_tty canon_copy_from_read_buf() code would look
    ahead for the EOL character one byte further than it would actually
    copy.  It would then decide that it had found the terminator, and unmark
    it as an EOL character - which in turn explains why the next read
    wouldn't then be terminated by it.
    
    Now, the reason it did all this in the first place is related to some
    historical and pretty obscure EOF behavior, see commit ac8f3bf8832a
    ("n_tty: Fix poll() after buffer-limited eof push read") and commit
    40d5e0905a03 ("n_tty: Fix EOF push handling").
    
    And the reason for the EOL confusion is that we treat EOF as a special
    EOL condition, with the EOL character being NUL (aka "__DISABLED_CHAR"
    in the kernel sources).
    
    So that EOF look-ahead also affects the normal EOL handling.
    
    This patch just removes the look-ahead that causes problems, because EOL
    is much more critical than the historical "EOF in the middle of a line
    that coincides with the end of the buffer" handling ever was.
    
    Now, it is possible that we should indeed re-introduce the "look at next
    character to see if it's a EOF" behavior, but if so, that should be done
    not at the kernel buffer chunk boundary in canon_copy_from_read_buf(),
    but at a higher level, when we run out of the user buffer.
    
    In particular, the place to do that would be at the top of
    'n_tty_read()', where we check if it's a continuation of a previously
    started read, and there is no more buffer space left, we could decide to
    just eat the __DISABLED_CHAR at that point.
    
    But that would be a separate patch, because I suspect nobody actually
    cares, and I'd like to get a report about it before bothering.
    
    Fixes: 3b830a9c34d5 ("tty: convert tty_ldisc_ops 'read()' function to take a kernel pointer")
    Fixes: ac8f3bf8832a ("n_tty: Fix  poll() after buffer-limited eof push read")
    Fixes: 40d5e0905a03 ("n_tty: Fix EOF push handling")
    Link: https://bugzilla.kernel.org/show_bug.cgi?id=215611
    Reported-and-tested-by: Daniel Gibson <metalcaedes@xxxxxxxxx>
    Cc: Peter Hurley <peter@xxxxxxxxxxxxxxxxxx>
    Cc: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>
    Cc: Jiri Slaby <jirislaby@xxxxxxxxxx>
    Signed-off-by: Linus Torvalds <torvalds@xxxxxxxxxxxxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c
index 451e02cd06377..de5b45de50402 100644
--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -1963,7 +1963,7 @@ static bool canon_copy_from_read_buf(struct tty_struct *tty,
 		return false;
 
 	canon_head = smp_load_acquire(&ldata->canon_head);
-	n = min(*nr + 1, canon_head - ldata->read_tail);
+	n = min(*nr, canon_head - ldata->read_tail);
 
 	tail = ldata->read_tail & (N_TTY_BUF_SIZE - 1);
 	size = min_t(size_t, tail + n, N_TTY_BUF_SIZE);
@@ -1985,10 +1985,8 @@ static bool canon_copy_from_read_buf(struct tty_struct *tty,
 		n += N_TTY_BUF_SIZE;
 	c = n + found;
 
-	if (!found || read_buf(ldata, eol) != __DISABLED_CHAR) {
-		c = min(*nr, c);
+	if (!found || read_buf(ldata, eol) != __DISABLED_CHAR)
 		n = c;
-	}
 
 	n_tty_trace("%s: eol:%zu found:%d n:%zu c:%zu tail:%zu more:%zu\n",
 		    __func__, eol, found, n, c, tail, more);



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux