Bug 206359 - Linux Kernel 5.4.7 - vc_do_resize use-after-free
Summary: Linux Kernel 5.4.7 - vc_do_resize use-after-free
Status: NEW
Alias: None
Product: Drivers
Classification: Unclassified
Component: Console/Framebuffers (show other bugs)
Hardware: All Linux
: P1 normal
Assignee: James Simmons
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2020-01-30 16:50 UTC by Tristan Madani
Modified: 2020-02-06 11:59 UTC (History)
1 user (show)

See Also:
Kernel Version: 5.4.7
Tree: Mainline
Regression: No


Attachments

Description Tristan Madani 2020-01-30 16:50:22 UTC
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.
Comment 1 Jiri Slaby 2020-02-06 10:36:44 UTC
Oh well...
/* FIXME: all this needs locking */
/* Variables for selection control. */
...
static char *sel_buffer;

Do we have a reproducer?
Comment 2 Jiri Slaby 2020-02-06 11:56:57 UTC
(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.
Comment 3 Jiri Slaby 2020-02-06 11:59:30 UTC
(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?

Note You need to log in before you can comment on or make changes to this bug.