diff options
author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2018-05-25 20:53:13 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-06-28 08:30:16 -0400 |
commit | 3d63b7e4ae0dc5e02d28ddd2fa1f945defc68d81 (patch) | |
tree | 4c72cbec570647876d8060aa4cb85cc4f3dfb796 | |
parent | 7daf201d7fe8334e2d2364d4e8ed3394ec9af819 (diff) |
n_tty: Fix stall at n_tty_receive_char_special().
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>
-rw-r--r-- | drivers/tty/n_tty.c | 13 |
1 files changed, 8 insertions, 5 deletions
diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index cbe98bc2b998..b279f8730e04 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c | |||
@@ -124,6 +124,8 @@ struct n_tty_data { | |||
124 | struct mutex output_lock; | 124 | struct mutex output_lock; |
125 | }; | 125 | }; |
126 | 126 | ||
127 | #define MASK(x) ((x) & (N_TTY_BUF_SIZE - 1)) | ||
128 | |||
127 | static inline size_t read_cnt(struct n_tty_data *ldata) | 129 | static inline size_t read_cnt(struct n_tty_data *ldata) |
128 | { | 130 | { |
129 | return ldata->read_head - ldata->read_tail; | 131 | return ldata->read_head - ldata->read_tail; |
@@ -978,14 +980,15 @@ static void eraser(unsigned char c, struct tty_struct *tty) | |||
978 | } | 980 | } |
979 | 981 | ||
980 | seen_alnums = 0; | 982 | seen_alnums = 0; |
981 | while (ldata->read_head != ldata->canon_head) { | 983 | while (MASK(ldata->read_head) != MASK(ldata->canon_head)) { |
982 | head = ldata->read_head; | 984 | head = ldata->read_head; |
983 | 985 | ||
984 | /* erase a single possibly multibyte character */ | 986 | /* erase a single possibly multibyte character */ |
985 | do { | 987 | do { |
986 | head--; | 988 | head--; |
987 | c = read_buf(ldata, head); | 989 | c = read_buf(ldata, head); |
988 | } while (is_continuation(c, tty) && head != ldata->canon_head); | 990 | } while (is_continuation(c, tty) && |
991 | MASK(head) != MASK(ldata->canon_head)); | ||
989 | 992 | ||
990 | /* do not partially erase */ | 993 | /* do not partially erase */ |
991 | if (is_continuation(c, tty)) | 994 | if (is_continuation(c, tty)) |
@@ -1027,7 +1030,7 @@ static void eraser(unsigned char c, struct tty_struct *tty) | |||
1027 | * This info is used to go back the correct | 1030 | * This info is used to go back the correct |
1028 | * number of columns. | 1031 | * number of columns. |
1029 | */ | 1032 | */ |
1030 | while (tail != ldata->canon_head) { | 1033 | while (MASK(tail) != MASK(ldata->canon_head)) { |
1031 | tail--; | 1034 | tail--; |
1032 | c = read_buf(ldata, tail); | 1035 | c = read_buf(ldata, tail); |
1033 | if (c == '\t') { | 1036 | if (c == '\t') { |
@@ -1302,7 +1305,7 @@ n_tty_receive_char_special(struct tty_struct *tty, unsigned char c) | |||
1302 | finish_erasing(ldata); | 1305 | finish_erasing(ldata); |
1303 | echo_char(c, tty); | 1306 | echo_char(c, tty); |
1304 | echo_char_raw('\n', ldata); | 1307 | echo_char_raw('\n', ldata); |
1305 | while (tail != ldata->read_head) { | 1308 | while (MASK(tail) != MASK(ldata->read_head)) { |
1306 | echo_char(read_buf(ldata, tail), tty); | 1309 | echo_char(read_buf(ldata, tail), tty); |
1307 | tail++; | 1310 | tail++; |
1308 | } | 1311 | } |
@@ -2411,7 +2414,7 @@ static unsigned long inq_canon(struct n_tty_data *ldata) | |||
2411 | tail = ldata->read_tail; | 2414 | tail = ldata->read_tail; |
2412 | nr = head - tail; | 2415 | nr = head - tail; |
2413 | /* Skip EOF-chars.. */ | 2416 | /* Skip EOF-chars.. */ |
2414 | while (head != tail) { | 2417 | while (MASK(head) != MASK(tail)) { |
2415 | if (test_bit(tail & (N_TTY_BUF_SIZE - 1), ldata->read_flags) && | 2418 | if (test_bit(tail & (N_TTY_BUF_SIZE - 1), ldata->read_flags) && |
2416 | read_buf(ldata, tail) == __DISABLED_CHAR) | 2419 | read_buf(ldata, tail) == __DISABLED_CHAR) |
2417 | nr--; | 2420 | nr--; |