| From 3d63b7e4ae0dc5e02d28ddd2fa1f945defc68d81 Mon Sep 17 00:00:00 2001 |
| From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
| Date: Sat, 26 May 2018 09:53:13 +0900 |
| Subject: n_tty: Fix stall at n_tty_receive_char_special(). |
| |
| From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
| |
| commit 3d63b7e4ae0dc5e02d28ddd2fa1f945defc68d81 upstream. |
| |
| syzbot is reporting stalls at n_tty_receive_char_special() [1]. This is |
| because comparison is not working as expected since ldata->read_head can |
| change at any moment. Mitigate this by explicitly masking with buffer size |
| when checking condition for "while" loops. |
| |
| [1] https://syzkaller.appspot.com/bug?id=3d7481a346958d9469bebbeb0537d5f056bdd6e8 |
| |
| Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
| Reported-by: syzbot <syzbot+18df353d7540aa6b5467@syzkaller.appspotmail.com> |
| Fixes: bc5a5e3f45d04784 ("n_tty: Don't wrap input buffer indices at buffer size") |
| Cc: stable <stable@vger.kernel.org> |
| Cc: Peter Hurley <peter@hurleysoftware.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/tty/n_tty.c | 13 ++++++++----- |
| 1 file changed, 8 insertions(+), 5 deletions(-) |
| |
| --- a/drivers/tty/n_tty.c |
| +++ b/drivers/tty/n_tty.c |
| @@ -128,6 +128,8 @@ struct n_tty_data { |
| struct mutex output_lock; |
| }; |
| |
| +#define MASK(x) ((x) & (N_TTY_BUF_SIZE - 1)) |
| + |
| static inline size_t read_cnt(struct n_tty_data *ldata) |
| { |
| return ldata->read_head - ldata->read_tail; |
| @@ -1006,14 +1008,15 @@ static void eraser(unsigned char c, stru |
| } |
| |
| seen_alnums = 0; |
| - while (ldata->read_head != ldata->canon_head) { |
| + while (MASK(ldata->read_head) != MASK(ldata->canon_head)) { |
| head = ldata->read_head; |
| |
| /* erase a single possibly multibyte character */ |
| do { |
| head--; |
| c = read_buf(ldata, head); |
| - } while (is_continuation(c, tty) && head != ldata->canon_head); |
| + } while (is_continuation(c, tty) && |
| + MASK(head) != MASK(ldata->canon_head)); |
| |
| /* do not partially erase */ |
| if (is_continuation(c, tty)) |
| @@ -1055,7 +1058,7 @@ static void eraser(unsigned char c, stru |
| * This info is used to go back the correct |
| * number of columns. |
| */ |
| - while (tail != ldata->canon_head) { |
| + while (MASK(tail) != MASK(ldata->canon_head)) { |
| tail--; |
| c = read_buf(ldata, tail); |
| if (c == '\t') { |
| @@ -1332,7 +1335,7 @@ n_tty_receive_char_special(struct tty_st |
| finish_erasing(ldata); |
| echo_char(c, tty); |
| echo_char_raw('\n', ldata); |
| - while (tail != ldata->read_head) { |
| + while (MASK(tail) != MASK(ldata->read_head)) { |
| echo_char(read_buf(ldata, tail), tty); |
| tail++; |
| } |
| @@ -2479,7 +2482,7 @@ static unsigned long inq_canon(struct n_ |
| tail = ldata->read_tail; |
| nr = head - tail; |
| /* Skip EOF-chars.. */ |
| - while (head != tail) { |
| + while (MASK(head) != MASK(tail)) { |
| if (test_bit(tail & (N_TTY_BUF_SIZE - 1), ldata->read_flags) && |
| read_buf(ldata, tail) == __DISABLED_CHAR) |
| nr--; |