Re: [PATCH V4] fbcon: fix fbcon out-of-bounds write in sys_imageblit

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 2021/08/30 17:16, Daniel Vetter wrote:
> On Mon, Aug 30, 2021 at 11:30:23AM +0800, tcs.kernel@xxxxxxxxx wrote:
>> From: Haimin Zhang <tcs_kernel@xxxxxxxxxxx>
>>
>> yres and vyres can be controlled by user mode parameters, and cause
>> p->vrows to become a negative value. While this value be passed to real_y
>> function, the ypos will be out of screen range.This is an out-of-bounds
>> write bug.
>> some driver will check xres and yres in fb_check_var callback,but some not
>> so we add a common check after that callback.
>>
>> Signed-off-by: Haimin Zhang <tcs_kernel@xxxxxxxxxxx>
>> Signed-off-by: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx>

Please s/Signed-off-by: Tetsuo Handa/Suggested-by: Tetsuo Handa/ .
It is Haimin who debugged this problem and wrote this patch.

> 
> Does this fix a syzbot crash or how was this discovered?

Yes, Haimin's team is running syzkaller locally and found this bug.
Therefore, no Reported-by: syzbot tag for this patch.


-------- Forwarded Message --------
Subject: Re: [PATCH v2] fbcon: fix Out-Of-Bounds write in sys_imageblit
Message-ID: <33fc0e30-b94c-939f-a708-4b939af43100@xxxxxxxxx>
Date: Mon, 2 Aug 2021 14:50:24 +0800

hi, Tetsuo Handa

i made a test with your suggested code, it can block the out-of-bound bug.

where to add this check logic, i suggest to add it after the driver's 
fb_check_var callback.because what we plan to add is a common check by 
framework,but some driver can fault-tolerant invalid parameters(like 
yres_virtual > yres)

	/* exist common check */
	if (var->xres < 8 || var->yres < 8)
		return -EINVAL;

        /* callback to drivers, some driver can fix invalid virtual 
xres or virtual yres */
	ret = info->fbops->fb_check_var(var, info);
	if (ret)
		return ret;
	/* we add a more check here, if some drivers can't fix invalid x,y 
virtual values, we return a -EINVAL */
	if (var->yres_virtual < var->yres || var->xres_virtual < var->xres)
		return -EINVAL;

how about this fix ?  i can make a v3 patch.



diff --git a/drivers/video/fbdev/core/fbmem.c 
b/drivers/video/fbdev/core/fbmem.c
index 1c85514..9fb7e94 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -1012,6 +1012,10 @@ static int fb_check_caps(struct fb_info *info, 
struct fb_var_screeninfo *var,

        if (ret)
                return ret;
+
+       /* Virtual resolution cannot be smaller than visible resolution. */
+       if (var->yres_virtual < var->yres || var->xres_virtual < var->xres)
+               return -EINVAL;

        if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
                return 0;



-------- Forwarded Message --------
Subject: Re: [PATCH]fbcon:fix Out-Of-Bounds write in sys_imageblit
Message-ID: <a0a228b4-d2e2-4d0d-9bfa-074049f4066b@xxxxxxxxx>
Date: Wed, 28 Jul 2021 11:10:52 +0800

this is my debug log:

1. environment
linux kernel:  git checkout  a4c30b8691f26c6115db6e11ec837c1fb6073953
qemu version:QEMU emulator version 6.0.90 (v6.1.0-rc0-99-g76bf66b913)
qemu-system-x86_64  -smp 4 -kernel 
linux_allyes/build/arch/x86/boot/bzImage  -m 4G \
-hda /data/h_image/linux_debug/buster1.img -serial stdio \
-append "earlyprintk=serial console=ttyS0 root=/dev/sda nokaslr" \
-enable-kvm -cpu host -display none -net nic -net 
user,hostfwd=tcp::1236-:22 -s -S

1.kasan report:
==================================================================
BUG: KASAN: vmalloc-out-of-bounds in sys_imageblit+0x12f4/0x1430
Write of size 4 at addr ffffc9000b8c1fe0 by task syz-executor.7/29927

CPU: 0 PID: 29927 Comm: syz-executor.7 Tainted: G            E 
5.13.0-rc5+ #2
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 
rel-1.12.0-59-gc9ba5276e321-prebuilt.qemu.org 04/01/2014
Call Trace:
 dump_stack+0x141/0x1d7
 print_address_description.constprop.0.cold+0x5/0x2f8
 kasan_report.cold+0x7c/0xd8
 sys_imageblit+0x12f4/0x1430
 drm_fbdev_fb_imageblit+0x15c/0x350
 soft_cursor+0x514/0xa30
 bit_cursor+0xd07/0x1740
 fbcon_cursor+0x51d/0x630
 hide_cursor+0x85/0x280
 putconsxy+0x1d/0x450
 vcs_write+0x9cf/0xb90
 vfs_write+0x28e/0xa30
 ksys_write+0x12d/0x250
 do_syscall_64+0x3a/0xb0
 entry_SYSCALL_64_after_hwframe+0x44/0xae
RIP: 0033:0x7f6c6d34cf59
Code: 00 c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 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 8b 0d 07 6f 0c 00 f7 d8 64 89 01 48
RSP: 002b:00007f6c6a832d98 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 000000000069c038 RCX: 00007f6c6d34cf59
RDX: 000000000000008d RSI: 0000000020000040 RDI: 0000000000000003
RBP: 000000000069c038 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 000000000069c044
R13: 00007ffd72091c8f R14: 000000000069c038 R15: 0000000000000001


Memory state around the buggy address:
 ffffc9000b8c1e80: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
 ffffc9000b8c1f00: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
>ffffc9000b8c1f80: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
                                                       ^
 ffffc9000b8c2000: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
 ffffc9000b8c2080: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
==================================================================

in
void sys_imageblit(struct fb_info *p, const struct fb_image *image)
{
	u32 fgcolor, bgcolor, start_index, bitstart, pitch_index = 0;
	u32 bpl = sizeof(u32), bpp = p->var.bits_per_pixel;
	u32 width = image->width;
	u32 dx = image->dx, dy = image->dy;
	void *dst1;

	if (p->state != FBINFO_STATE_RUNNING)
		return;

	bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
	start_index = bitstart & (32 - 1);
	pitch_index = (p->fix.line_length & (bpl - 1)) * 8;

	bitstart /= 8;
	bitstart &= ~(bpl - 1);
	dst1 = (void __force *)p->screen_base + bitstart;  // here is the bug
...
}
 p->screen_base is  alloced by  drm_fb_helper_generic_probe and size is 
0x3000000
(gdb) bt
#0  drm_fb_helper_generic_probe (fb_helper=0xffff888105b17800, 
sizes=<optimized out>) at ../drivers/gpu/drm/drm_fb_helper.c:2342
#1  0xffffffff843f9c6f in drm_fb_helper_single_fb_probe 
(preferred_bpp=32, fb_helper=0xffff888105b17800) at 
../drivers/gpu/drm/drm_fb_helper.c:1668
#2  __drm_fb_helper_initial_config_and_unlock 
(fb_helper=fb_helper@entry=0xffff888105b17800, bpp_sel=bpp_sel@entry=32) 
at ../drivers/gpu/drm/drm_fb_helper.c:1826
#3  0xffffffff843fea05 in drm_fb_helper_initial_config 
(bpp_sel=<optimized out>, fb_helper=0xffff888105b17800) at 
../drivers/gpu/drm/drm_fb_helper.c:1921
#4  drm_fb_helper_initial_config (bpp_sel=<optimized out>, 
fb_helper=0xffff888105b17800) at ../drivers/gpu/drm/drm_fb_helper.c:1913
#5  drm_fbdev_client_hotplug (client=client@entry=0xffff888105b17800) at 
../drivers/gpu/drm/drm_fb_helper.c:2423
#6  0xffffffff84402504 in drm_fbdev_generic_setup 
(dev=dev@entry=0xffff888014632000, preferred_bpp=<optimized out>, 
preferred_bpp@entry=32) at ../drivers/gpu/drm/drm_fb_helper.c:2510
#7  0xffffffff84587e71 in bochs_pci_probe (ent=<optimized out>, 
pdev=<optimized out>) at ../drivers/gpu/drm/bochs/bochs_drv.c:134
#8  bochs_pci_probe (pdev=<optimized out>, ent=<optimized out>) at 
../drivers/gpu/drm/bochs/bochs_drv.c:99
#9  0xffffffff83f7f3d6 in local_pci_probe 
(_ddi=_ddi@entry=0xffffc90000647b98) at ../drivers/pci/pci-driver.c:309
#10 0xffffffff83f842e6 in pci_call_probe (id=<optimized out>, 
dev=0xffff888015a50000, drv=<optimized out>) at 
../drivers/pci/pci-driver.c:366
#11 __pci_device_probe (pci_dev=0xffff888015a50000, drv=<optimized out>) 
at ../drivers/pci/pci-driver.c:391
#12 pci_device_probe (dev=0xffff888015a500c8) at 
../drivers/pci/pci-driver.c:434
#13 0xffffffff845d5b50 in really_probe 
(dev=dev@entry=0xffff888015a500c8, drv=drv@entry=0xffffffff8c21a930 
<bochs_pci_driver+112>) at ../drivers/base/dd.c:576
#14 0xffffffff845d686d in driver_probe_device 
(drv=drv@entry=0xffffffff8c21a930 <bochs_pci_driver+112>, 
dev=dev@entry=0xffff888015a500c8) at ../drivers/base/dd.c:763
#15 0xffffffff845d7397 in device_driver_attach 
(drv=drv@entry=0xffffffff8c21a930 <bochs_pci_driver+112>, 
dev=dev@entry=0xffff888015a500c8) at ../drivers/base/dd.c:1039
#16 0xffffffff845d7556 in __driver_attach (dev=0xffff888015a500c8, 
data=0xffffffff8c21a930 <bochs_pci_driver+112>) at ../drivers/base/dd.c:1117
#17 0xffffffff845cf008 in bus_for_each_dev (bus=<optimized out>, 
start=start@entry=0x0 <fixed_percpu_data>, 
data=data@entry=0xffffffff8c21a930 <bochs_pci_driver+112>, 
fn=fn@entry=0xffffffff845d73d0 <__driver_attach>) at 
../drivers/base/bus.c:305
#18 0xffffffff845d477a in driver_attach 
(drv=drv@entry=0xffffffff8c21a930 <bochs_pci_driver+112>) at 
../drivers/base/dd.c:1133
#19 0xffffffff845d3203 in bus_add_driver 
(drv=drv@entry=0xffffffff8c21a930 <bochs_pci_driver+112>) at 
../drivers/base/bus.c:622
#20 0xffffffff845d95db in driver_register 
(drv=drv@entry=0xffffffff8c21a930 <bochs_pci_driver+112>) at 
../drivers/base/driver.c:171
#21 0xffffffff83f7dcba in __pci_register_driver 
(drv=drv@entry=0xffffffff8c21a8c0 <bochs_pci_driver>, 
owner=owner@entry=0x0 <fixed_percpu_data>, 
mod_name=mod_name@entry=0xffffffff89d9c4c0 "bochs_drm") at 
../drivers/pci/pci-driver.c:1393
#22 0xffffffff8e85e6cb in bochs_init () at 
../drivers/gpu/drm/bochs/bochs_drv.c:191
#23 0xffffffff81002285 in do_one_initcall (fn=0xffffffff8e85e65d 
<bochs_init>) at ../init/main.c:1249
#24 0xffffffff8e7ab5f8 in do_initcall_level 
(command_line=0xffff888135a06c00 "earlyprintk", level=<optimized out>) 
at ../include/linux/compiler.h:234
#25 do_initcalls () at ../init/main.c:1338
#26 do_basic_setup () at ../init/main.c:1358
#27 kernel_init_freeable () at ../init/main.c:1560
#28 0xffffffff8915d45b in kernel_init (unused=<optimized out>) at 
../init/main.c:1447
#29 0xffffffff810046ff in ret_from_fork () at 
../arch/x86/entry/entry_64.S:294
#30 0x0000000000000000 in ?? ()
(gdb) l
2337	
2338		drm_fb_helper_fill_info(fbi, fb_helper, sizes);
2339	
2340		if (drm_fbdev_use_shadow_fb(fb_helper)) {
2341			fbi->screen_buffer = vzalloc(fbi->screen_size);
2342			if (!fbi->screen_buffer)
2343				return -ENOMEM;
2344	
2345			fbi->fbdefio = &drm_fbdev_defio;
2346	
(gdb) p/x  fbi->screen_size
$1 = 0x300000
(gdb) p/x fbi->screen_buffer
$2 = 0xffffc9000c561000
(gdb) p/x fbi->screen_base
$3 = 0xffffc9000c561000


when  run the poc :


Thread 1 hit Breakpoint 2, updatescrollmode 
(info=info@entry=0xffff8881081bd000, vc=vc@entry=0xffff888010479000, 
p=<optimized out>) at ../drivers/video/fbdev/core/fbcon.c:1950
1950	static void updatescrollmode(struct fbcon_display *p,
(gdb) frame 1
#1  0xffffffff8401d9c3 in fbcon_resize (vc=vc@entry=0xffff888010479000, 
width=width@entry=20, height=height@entry=7, user=user@entry=0) at 
../drivers/video/fbdev/core/fbcon.c:2030
2030		updatescrollmode(p, info, vc);
(gdb) p p->vrows
$5 = 48
(gdb) p &p->vrows
$6 = (int *) 0xffffffff90056094 <fb_display+20>
(gdb) frame 0
#0  updatescrollmode (info=info@entry=0xffff8881081bd000, 
vc=vc@entry=0xffff888010479000, p=<optimized out>) at 
../drivers/video/fbdev/core/fbcon.c:1950
1950	static void updatescrollmode(struct fbcon_display *p,
(gdb) n
1954		struct fbcon_ops *ops = info->fbcon_par;
(gdb)
1955		int fh = vc->vc_font.height;
(gdb)
1956		int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
(gdb)
1957		int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual,
(gdb)
1960		p->vrows = vyres/fh;
(gdb)
1961		if (yres > (fh * (vc->vc_rows + 1)))
(gdb) p info.var.yres_virtual
$8 = 0
(gdb) p info.var.xres_virtual
$9 = 0
(gdb) p info.var.xres
$10 = 160
(gdb) p info.var.yres
$11 = 120
(gdb) p yres
$12 = 120
(gdb) n
1963		if ((yres % fh) && (vyres % fh < yres % fh))
(gdb) n
1964			p->vrows--;

(gdb) x/1wd 0xffffffff90056094
0xffffffff90056094 <fb_display+20>:	-1
(gdb) p vc->vc_font
$19 = {width = 8, height = 16, charcount = 256, data = 
0xffffffff89c532d0 <fontdata_8x16+16> ""}

the kasan backtrace  debug  in gdb

Thread 2 hit Breakpoint 6, sys_imageblit (p=p@entry=0xffff8881081bd000, 
image=image@entry=0xffff888011f85080) at 
../drivers/video/fbdev/core/sysimgblt.c:244
244		u32 width = image->width;
(gdb) n
248		if (p->state != FBINFO_STATE_RUNNING)
(gdb)
251		bitstart = (dy * p->fix.line_length * 8) + (dx * bpp);
(gdb)
252		start_index = bitstart & (32 - 1);
(gdb)
253		pitch_index = (p->fix.line_length & (bpl - 1)) * 8;
(gdb)
257		dst1 = (void __force *)p->screen_base + bitstart;
(gdb) n
259		if (p->fbops->fb_sync)
(gdb) p dst1
$23 = (void *) 0xffffc9000c5a1220
(gdb) p p->screen_base
$24 = 0xffffc9000c561000 ""
(gdb) p/x 0xffffc9000c5a1220-0xffffc9000c561000
$25 = 0x40220
(gdb) bt
#0  sys_imageblit (p=p@entry=0xffff8881081bd000, 
image=image@entry=0xffff888011f85080) at 
../drivers/video/fbdev/core/sysimgblt.c:259
#1  0xffffffff843fbbcc in drm_fb_helper_sys_imageblit 
(info=info@entry=0xffff8881081bd000, 
image=image@entry=0xffff888011f85080) at 
../drivers/gpu/drm/drm_fb_helper.c:794
#2  0xffffffff843fc831 in drm_fbdev_fb_imageblit 
(info=0xffff8881081bd000, image=0xffff888011f85080) at 
../drivers/gpu/drm/drm_fb_helper.c:2276
#3  0xffffffff8402eff4 in soft_cursor 
(info=info@entry=0xffff8881081bd000, 
cursor=cursor@entry=0xffffc9001090f9f0) at 
../drivers/video/fbdev/core/softcursor.c:74
#4  0xffffffff8402deb2 in bit_cursor (vc=vc@entry=0xffff888010479000, 
info=info@entry=0xffff8881081bd000, mode=mode@entry=1, fg=<optimized 
out>, bg=bg@entry=0) at ../drivers/video/fbdev/core/bitblit.c:377
#5  0xffffffff84019d5e in fbcon_cursor (vc=0xffff888010479000, mode=1) 
at ../drivers/video/fbdev/core/fbcon.c:1339
#6  0xffffffff842a5155 in set_cursor (vc=0xffff888010479000) at 
../drivers/tty/vt/vt.c:920
#7  set_cursor (vc=0xffff888010479000) at ../drivers/tty/vt/vt.c:911
#8  0xffffffff842a6135 in redraw_screen (vc=vc@entry=0xffff888010479000, 
is_switch=is_switch@entry=0) at ../drivers/tty/vt/vt.c:1037
#9  0xffffffff8401c46c in fbcon_modechanged 
(info=info@entry=0xffff8881081bd000) at 
../drivers/video/fbdev/core/fbcon.c:2651
#10 0xffffffff8401d2da in fbcon_update_vcs 
(info=info@entry=0xffff8881081bd000, all=all@entry=false) at 
../drivers/video/fbdev/core/fbcon.c:2696
#11 0xffffffff83ffa426 in do_fb_ioctl 
(info=info@entry=0xffff8881081bd000, cmd=cmd@entry=17921, 
arg=arg@entry=536870912) at ../drivers/video/fbdev/core/fbmem.c:1110
#12 0xffffffff83ffa577 in fb_ioctl (file=file@entry=0xffff88810e274500, 
cmd=cmd@entry=17921, arg=arg@entry=536870912) at 
../drivers/video/fbdev/core/fbmem.c:1185
#13 0xffffffff81cab763 in vfs_ioctl (arg=536870912, cmd=17921, 
filp=0xffff88810e274500) at ../fs/ioctl.c:51
#14 __do_sys_ioctl (arg=536870912, cmd=17921, fd=3) at ../fs/ioctl.c:1069
#15 __se_sys_ioctl (arg=536870912, cmd=17921, fd=3) at ../fs/ioctl.c:1055
#16 __x64_sys_ioctl (regs=<optimized out>) at ../fs/ioctl.c:1055
#17 0xffffffff89158596 in do_syscall_64 (nr=<optimized out>, 
regs=0xffffc9001090ff58) at ../arch/x86/entry/common.c:47
#18 0xffffffff89200068 in entry_SYSCALL_64 () at 
../arch/x86/entry/entry_64.S:112
#19 0x0000000000000000 in ?? ()

(gdb) frame 0
#0  sys_imageblit (p=p@entry=0xffff8881081bd000, 
image=image@entry=0xffff888011f85080) at 
../drivers/video/fbdev/core/sysimgblt.c:259
259		if (p->fbops->fb_sync)
(gdb) p image.dy
$29 = 64
(gdb) p image.dx
$30 = 136
(gdb) frame 1
#1  0xffffffff843fbbcc in drm_fb_helper_sys_imageblit 
(info=info@entry=0xffff8881081bd000, 
image=image@entry=0xffff888011f85080) at 
../drivers/gpu/drm/drm_fb_helper.c:794
794		sys_imageblit(info, image);
(gdb) p info.screen_
screen_base    screen_buffer  screen_size
(gdb) p info.screen_size
$31 = 3145728
(gdb) p/x  info.screen_size
$32 = 0x300000

(gdb) frame 4
#4  0xffffffff8402deb2 in bit_cursor (vc=vc@entry=0xffff888010479000, 
info=info@entry=0xffff8881081bd000, mode=mode@entry=1, fg=<optimized 
out>, bg=bg@entry=0) at ../drivers/video/fbdev/core/bitblit.c:377
377			soft_cursor(info, &cursor);
(gdb) p ops->p.vrows
$38 = -1
(gdb) p &ops->p.vrows
$39 = (int *) 0xffffffff90056094 <fb_display+20>


在 2021/7/27 23:10, Tetsuo Handa 写道:
> OK, test.c was generated by syzkaller, and I guess that
> your team is running syzkaller in your environment.
> 
> It seems that test.c is doing
> 
>    struct fb_var_screeninfo var = { ... };
>    int fd_fb0 = open("/dev/fb0", O_RDONLY);
>    ioctl(fd_fb0, FBIOPUT_VSCREENINFO, &var);
> 
>    int fd_vcs = open("/dev/vcsa5", O_WRONLY|O_NOCTTY|O_NONBLOCK);
>    write(fd_vcs, "\16\272P\323\236\3\377\231\34\0\0\0", 12);
> 
> in parallel and hitting some race.
> 
> I don't have environment to test with KASAN now. But syzbot dashboard
> indicates that crash in sys_imageblit is not happening recently.
> 
>    KASAN: vmalloc-out-of-bounds Write in sys_imageblit
>    https://syzkaller.appspot.com/bug?id=d2e785fe8b66425f3a2be0a209b08456f8200b53
> 
>    BUG: unable to handle kernel paging request in sys_imageblit
>    https://syzkaller.appspot.com/bug?id=26d6ef43f58d17c6899c63a53544ee9bb26d0bfd
> 
> You need to provide more information.
> Are you seeing this problem with v5.14-rc3 ? Please provide full oops report.
> Please show a URL for this problem if this problem was reported somewhere.
> 




[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux