« Back to the Da Slop Pit Forum

vmalloc-out-of-bounds Write in imageblit

Posted by yuu

posted
updated

Forum: Da Slop Pit Group

A few months ago I saw this bug but wasn't sure if anyone actually did anything with it so here are the notes.

The Crash


KASAN: vmalloc-out-of-bounds Write in imageblit (2)
BUG: unable to handle page fault for address: fffff520008b2208
#PF: supervisor read access in kernel mode
#PF: error_code(0x0000) - not-present page
PGD 23ffed067 P4D 23ffed067 PUD 10db4067 PMD 1470c4067 PTE 0
Oops: 0000 [#1] PREEMPT SMP KASAN
CPU: 0 PID: 3595 Comm: syz-executor362 Not tainted 5.16.0-next-20220120-syzkaller #0
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011
RIP: 0010:fast_imageblit drivers/video/fbdev/core/sysimgblt.c:229 [inline]
RIP: 0010:sys_imageblit+0x656/0x1430 drivers/video/fbdev/core/sysimgblt.c:275
Code: 14 38 48 89 d8 83 e0 07 83 c0 03 38 d0 7c 08 84 d2 0f 85 b6 0c 00 00 8b 44 24 20 23 03 8b 5c 24 18 31 c3 48 89 e8 48 c1 e8 03 <42> 0f b6 14 38 48 89 e8 83 e0 07 83 c0 03 38 d0 7c 08 84 d2 0f 85
RSP: 0018:ffffc90002a1f368 EFLAGS: 00010a02
RAX: 1ffff920008b2208 RBX: 0000000000000000 RCX: 0000000000000007
RDX: 0000000000000000 RSI: ffffffff84257bf0 RDI: 0000000000000003
RBP: ffffc90004591040 R08: 000000000000001f R09: ffffffff84257a74
R10: ffffffff84257be1 R11: 0000000000000020 R12: 0000000000000007
R13: 00000000000003ef R14: ffff888146efc7e0 R15: dffffc0000000000
FS:  0000555555c5d300(0000) GS:ffff8880b9c00000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: fffff520008b2208 CR3: 0000000023b12000 CR4: 00000000003506f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
Call Trace:
 <TASK>
 drm_fb_helper_sys_imageblit drivers/gpu/drm/drm_fb_helper.c:794 [inline]
 drm_fbdev_fb_imageblit+0x15c/0x350 drivers/gpu/drm/drm_fb_helper.c:2288
 bit_putcs_unaligned drivers/video/fbdev/core/bitblit.c:124 [inline]
 bit_putcs+0x6e1/0xd20 drivers/video/fbdev/core/bitblit.c:173
 fbcon_putcs+0x353/0x440 drivers/video/fbdev/core/fbcon.c:1277
 do_update_region+0x399/0x630 drivers/tty/vt/vt.c:676
 invert_screen+0x1d4/0x600 drivers/tty/vt/vt.c:800
 highlight drivers/tty/vt/selection.c:57 [inline]
 clear_selection drivers/tty/vt/selection.c:84 [inline]
 clear_selection+0x55/0x70 drivers/tty/vt/selection.c:80
 vc_do_resize+0xe6e/0x1180 drivers/tty/vt/vt.c:1257
 fbcon_do_set_font+0x47a/0x760 drivers/video/fbdev/core/fbcon.c:1928
 fbcon_set_font+0x817/0xa00 drivers/video/fbdev/core/fbcon.c:2014
 con_font_set drivers/tty/vt/vt.c:4666 [inline]
 con_font_op+0x73a/0xc90 drivers/tty/vt/vt.c:4710
 vt_k_ioctl drivers/tty/vt/vt_ioctl.c:474 [inline]
 vt_ioctl+0x1e26/0x2b10 drivers/tty/vt/vt_ioctl.c:752
 tty_ioctl+0xbbd/0x1660 drivers/tty/tty_io.c:2778
 vfs_ioctl fs/ioctl.c:51 [inline]
 __do_sys_ioctl fs/ioctl.c:874 [inline]
 __se_sys_ioctl fs/ioctl.c:860 [inline]
 __x64_sys_ioctl+0x193/0x200 fs/ioctl.c:860
 do_syscall_x64 arch/x86/entry/common.c:50 [inline]
 do_syscall_64+0x35/0xb0 arch/x86/entry/common.c:80
 entry_SYSCALL_64_after_hwframe+0x44/0xae
RIP: 0033:0x7f3bac0e1349
Code: 28 c3 e8 2a 14 00 00 66 2e 0f 1f 84 00 00 00 00 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 c0 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007ffff160a718 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f3bac0e1349
RDX: 0000000020000000 RSI: 0000000000004b72 RDI: 0000000000000004
RBP: 00007f3bac0a51d0 R08: 000000000000000d R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 00007f3bac0a5260
R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
 </TASK>
Modules linked in:
CR2: fffff520008b2208
---[ end trace 0000000000000000 ]---
RIP: 0010:fast_imageblit drivers/video/fbdev/core/sysimgblt.c:229 [inline]
RIP: 0010:sys_imageblit+0x656/0x1430 drivers/video/fbdev/core/sysimgblt.c:275
Code: 14 38 48 89 d8 83 e0 07 83 c0 03 38 d0 7c 08 84 d2 0f 85 b6 0c 00 00 8b 44 24 20 23 03 8b 5c 24 18 31 c3 48 89 e8 48 c1 e8 03 <42> 0f b6 14 38 48 89 e8 83 e0 07 83 c0 03 38 d0 7c 08 84 d2 0f 85
RSP: 0018:ffffc90002a1f368 EFLAGS: 00010a02
RAX: 1ffff920008b2208 RBX: 0000000000000000 RCX: 0000000000000007
RDX: 0000000000000000 RSI: ffffffff84257bf0 RDI: 0000000000000003
RBP: ffffc90004591040 R08: 000000000000001f R09: ffffffff84257a74
R10: ffffffff84257be1 R11: 0000000000000020 R12: 0000000000000007
R13: 00000000000003ef R14: ffff888146efc7e0 R15: dffffc0000000000
FS:  0000555555c5d300(0000) GS:ffff8880b9c00000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: fffff520008b2208 CR3: 0000000023b12000 CR4: 00000000003506f0
DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
----------------
Code disassembly (best guess):
   0:   14 38                   adc    $0x38,%al
   2:   48 89 d8                mov    %rbx,%rax
   5:   83 e0 07                and    $0x7,%eax
   8:   83 c0 03                add    $0x3,%eax
   b:   38 d0                   cmp    %dl,%al
   d:   7c 08                   jl     0x17
   f:   84 d2                   test   %dl,%dl
  11:   0f 85 b6 0c 00 00       jne    0xccd
  17:   8b 44 24 20             mov    0x20(%rsp),%eax
  1b:   23 03                   and    (%rbx),%eax
  1d:   8b 5c 24 18             mov    0x18(%rsp),%ebx
  21:   31 c3                   xor    %eax,%ebx
  23:   48 89 e8                mov    %rbp,%rax
  26:   48 c1 e8 03             shr    $0x3,%rax
* 2a:   42 0f b6 14 38          movzbl (%rax,%r15,1),%edx <-- trapping instruction
  2f:   48 89 e8                mov    %rbp,%rax
  32:   83 e0 07                and    $0x7,%eax
  35:   83 c0 03                add    $0x3,%eax
  38:   38 d0                   cmp    %dl,%al
  3a:   7c 08                   jl     0x44
  3c:   84 d2                   test   %dl,%dl
  3e:   0f                      .byte 0xf
  3f:   85                      .byte 0x85

Initial Test

I compiled the repro and ran it

root@syzkaller:~# strace ./repro_imgblit
execve("./repro_imgblit", ["./repro_imgblit"], [/* 11 vars */]) = 0
...snip...
mmap(0x1ffff000, 4096, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x1ffff000
mmap(0x20000000, 16777216, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x20000000
mmap(0x21000000, 4096, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x21000000
open("/dev/char/4:1", O_RDWR) = 3
ioctl(3, TIOCLINUX, 0x20000080) = 0
open("/dev/char/4:1", O_RDWR) = 4
ioctl(4, KDFONTOP, 0x20000000) = 0
exit_group(0) = ?
+++ exited with 0 +++

You can run in user mode by adding the unshare namespace trick, otherwise you need CAP_SYS_ADMIN.

Also you can enable execmem per program with chcon -t unconfined_execmem_exec_t ./repro https://etbe.coker.com.au/2007/10/10/execmem-and-se-linux/

What does it do?

Basically it does this

From the docs:

       The action of the following ioctls depends on the first byte in
the struct pointed to by argp, referred to here as the subcode.
These are legal only for the superuser or the owner of the
current terminal.
...snip...
TIOCLINUX, subcode=2
Set selection. argp points to a

struct {
char subcode;
short xs, ys, xe, ye;
short sel_mode;
};

xs and ys are the starting column and row. xe and ye are
the ending column and row. (Upper left corner is
row=column=1.) sel_mode is 0 for character-by-character
selection, 1 for word-by-word selection, or 2 for line-by-
line selection. The indicated screen characters are
highlighted and saved in the static array sel_buffer in
devices/char/console.c.

TIOCLINUX, subcode=3
Paste selection. The characters in the selection buffer
are written to fd.

The repro:

  *(uint8_t*)0x20000080 = 2;  // subcode 2? "Set selection"
*(uint8_t*)0x20000081 = 3; // subcode 3? "Paste selection"
*(uint16_t*)0x20000082 = 0; // short xs;
*(uint16_t*)0x20000084 = 0; // short ys;
*(uint16_t*)0x20000086 = 0; // short xe;
*(uint16_t*)0x20000088 = 0; // short ye;
*(uint16_t*)0x2000008a = 0; // short sel_mode; // 0 = character-by-character selection
syscall(__NR_ioctl, r[0], 0x541c, 0x20000080ul); // TIOCLINUX
  • Opens up /dev/tty1 again -- openat(AT_FDCWD, "/dev/char/4:1", O_RDWR)
  • Sets up a console_font_op struct
  *(uint32_t*)0x20000000 = 0; // unsigned int op;  /* operation code KD_FONT_OP_* */
*(uint32_t*)0x20000004 = 0; // unsigned int flags; /* KD_FONT_FLAG_* */
*(uint32_t*)0x20000008 = 8; // unsigned int width; /* font size */
*(uint32_t*)0x2000000c = 0x20; // unsigned int height; /* font size */
*(uint32_t*)0x20000010 = 0x200; // unsigned int charcount;
*(uint64_t*)0x20000018 = 0x20000600; // unsigned char __user *data; /* font data with height fixed to 32 */

The code it crashes on is parsing this font data. This is the code from fast_imageblit https://elixir.bootlin.com/linux/v5.16/source/drivers/video/fbdev/core/sysimgblt.c#L229

 221	for (i = image->height; i--; ) {
222 dst = dst1;
223 shift = 8;
224 src = s;
225
226 for (j = k; j--; ) {
227 shift -= ppw;
228 end_mask = tab[(*src >> shift) & bit_mask];
*229 *dst++ = (end_mask & eorx) ^ bgx;
230 if (!shift) {
231 shift = 8;
232 src++;
233 }
234 }

The loop is iterating over the height and width of the font.

In the set up for TIOCLINUX, if it's actually subcode 2 "Set selection", then the dimensions of the selection are all 0.

In the console_font_op for KDFONTOP, the font is 8x32, and it's using KD_FONT_OP_SET indicated by the 0 in "op".

The instruction it gets messed up on is movzbl (%rax,%r15,1),%edx which is out of bounds

  23:	48 89 e8             	mov    %rbp,%rax
26: 48 c1 e8 03 shr $0x3,%rax
* 2a: 42 0f b6 14 38 movzbl (%rax,%r15,1),%edx <-- trapping instruction

This is a different looking crash https://lkml.org/lkml/2021/11/19/150, it reports a write of 4 bytes

And this looks similar in terms of args, but it seems incomplete too. https://www.spinics.net/lists/linux-fbdev/msg30377.html

Also lol at this comment: https://elixir.bootlin.com/linux/v5.16/source/drivers/tty/vt/vt_ioctl.c#L326

What now?

I haven't been able to get it to actually crash. The original warning was just because the page passed to the kernel was RWX.

I'm using kernel 5.16 with the stock syzkaller kconfig, but will have to see if there's something else that influences why this crashes or not.


Report Topic

1 Reply

Reply by gren

posted
updated

this shit is

f i r e


Report Reply