| From f8d1653daec02315e06d30246cff4af72e76e54e Mon Sep 17 00:00:00 2001 |
| From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
| Date: Wed, 29 Jul 2020 23:57:01 +0900 |
| Subject: vt: defer kfree() of vc_screenbuf in vc_do_resize() |
| |
| From: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
| |
| commit f8d1653daec02315e06d30246cff4af72e76e54e upstream. |
| |
| syzbot is reporting UAF bug in set_origin() from vc_do_resize() [1], for |
| vc_do_resize() calls kfree(vc->vc_screenbuf) before calling set_origin(). |
| |
| Unfortunately, in set_origin(), vc->vc_sw->con_set_origin() might access |
| vc->vc_pos when scroll is involved in order to manipulate cursor, but |
| vc->vc_pos refers already released vc->vc_screenbuf until vc->vc_pos gets |
| updated based on the result of vc->vc_sw->con_set_origin(). |
| |
| Preserving old buffer and tolerating outdated vc members until set_origin() |
| completes would be easier than preventing vc->vc_sw->con_set_origin() from |
| accessing outdated vc members. |
| |
| [1] https://syzkaller.appspot.com/bug?id=6649da2081e2ebdc65c0642c214b27fe91099db3 |
| |
| Reported-by: syzbot <syzbot+9116ecc1978ca3a12f43@syzkaller.appspotmail.com> |
| Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
| Cc: stable <stable@vger.kernel.org> |
| Link: https://lore.kernel.org/r/1596034621-4714-1-git-send-email-penguin-kernel@I-love.SAKURA.ne.jp |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/tty/vt/vt.c | 5 +++-- |
| 1 file changed, 3 insertions(+), 2 deletions(-) |
| |
| --- a/drivers/tty/vt/vt.c |
| +++ b/drivers/tty/vt/vt.c |
| @@ -868,7 +868,7 @@ static int vc_do_resize(struct tty_struc |
| unsigned int old_rows, old_row_size; |
| unsigned int new_cols, new_rows, new_row_size, new_screen_size; |
| unsigned int user; |
| - unsigned short *newscreen; |
| + unsigned short *oldscreen, *newscreen; |
| |
| WARN_CONSOLE_UNLOCKED(); |
| |
| @@ -950,10 +950,11 @@ static int vc_do_resize(struct tty_struc |
| if (new_scr_end > new_origin) |
| scr_memsetw((void *)new_origin, vc->vc_video_erase_char, |
| new_scr_end - new_origin); |
| - kfree(vc->vc_screenbuf); |
| + oldscreen = vc->vc_screenbuf; |
| vc->vc_screenbuf = newscreen; |
| vc->vc_screenbuf_size = new_screen_size; |
| set_origin(vc); |
| + kfree(oldscreen); |
| |
| /* do part of a reset_terminal() */ |
| vc->vc_top = 0; |