Re: [syzbot] BUG: unable to handle kernel paging request in vga16fb_imageblit (2)

From: Tetsuo Handa
Date: Mon May 03 2021 - 09:41:34 EST


On 2021/05/02 10:53, syzbot wrote:
> syzbot has bisected this issue to:
>
> commit 988d0763361bb65690d60e2bc53a6b72777040c3
> Author: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx>
> Date: Sun Sep 27 11:46:30 2020 +0000
>
> vt_ioctl: make VT_RESIZEX behave like VT_RESIZE
>

That commit is irrelevant. Below is a simplified reproducer.

----------
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#include <linux/vt.h>

int main(int argc, char *argv[])
{
const int fd = open("/dev/char/4:1", O_RDWR);
struct vt_sizes vt = { 0x4100, 2 };

ioctl(fd, KDSETMODE, KD_GRAPHICS);
ioctl(fd, VT_RESIZE, &vt);
ioctl(fd, KDSETMODE, KD_TEXT);
return 0;
}
----------

In vga16fb_probe(), we have

----------
/* XXX share VGA_FB_PHYS and I/O region with vgacon and others */
info->screen_base = (void __iomem *)VGA_MAP_MEM(VGA_FB_PHYS, 0);

if (!info->screen_base) {
printk(KERN_ERR "vga16fb: unable to map device\n");
ret = -ENOMEM;
goto err_ioremap;
}

printk(KERN_INFO "vga16fb: mapped to 0x%p\n", info->screen_base);
----------

and ffff8880000a0000 is assigned for 80 x 30 screen upon boot.

----------
[ 4.584361][ T1] vga16fb: mapped to 0xffff8880000a0000
[ 6.137556][ T1] Console: switching to colour frame buffer device 80x30
[ 7.829276][ T1] fb0: VGA16 VGA frame buffer device
----------

With debug printk() patch shown below,

----------
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 01645e87b3d5..af860b12db44 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -641,6 +641,8 @@ static void con_scroll(struct vc_data *vc, unsigned int t, unsigned int b,
scr_memsetw(clear, vc->vc_video_erase_char, vc->vc_size_row * nr);
}

+extern struct task_struct *trace_me;
+
static void do_update_region(struct vc_data *vc, unsigned long start, int count)
{
unsigned int xx, yy, offset;
@@ -656,6 +658,8 @@ static void do_update_region(struct vc_data *vc, unsigned long start, int count)
start = vc->vc_sw->con_getxy(vc, start, &nxx, &nyy);
xx = nxx; yy = nyy;
}
+ if (trace_me == current)
+ pr_info("p=%px vc->{ vc_origin=%lx vc_rows=%u vc_cols=%u vc_scr_end=%lx } start=%lx count=%d xx=%u yy=%u\n", p, vc->vc_origin, vc->vc_rows, vc->vc_cols, vc->vc_scr_end, start, count, xx, yy);
for(;;) {
u16 attrib = scr_readw(p) & 0xff00;
int startx = xx;
@@ -1227,6 +1231,8 @@ static int vc_do_resize(struct tty_struct *tty, struct vc_data *vc,
newscreen = kzalloc(new_screen_size, GFP_USER);
if (!newscreen)
return -ENOMEM;
+ if (trace_me == current)
+ pr_info("vc=%px new_cols=%u new_rows=%u new_screen_size=%u newscreen=%px\n", vc, new_cols, new_rows, new_screen_size, newscreen);

if (get_vc_uniscr(vc)) {
new_uniscr = vc_uniscr_alloc(new_cols, new_rows);
diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index 89aeaf3c1bca..137befd09d22 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -240,6 +240,8 @@ int vt_waitactive(int n)
#define GPLAST 0x3df
#define GPNUM (GPLAST - GPFIRST + 1)

+struct task_struct *trace_me;
+
/*
* currently, setting the mode from KD_TEXT to KD_GRAPHICS doesn't do a whole
* lot. i'm not sure if it should do any restoration of modes or what...
@@ -272,10 +274,12 @@ static int vt_kdsetmode(struct vc_data *vc, unsigned long mode)

/* explicitly blank/unblank the screen if switching modes */
console_lock();
+ trace_me = current;
if (mode == KD_TEXT)
do_unblank_screen(1);
else
do_blank_screen(1);
+ trace_me = NULL;
console_unlock();

return 0;
@@ -877,6 +881,7 @@ int vt_ioctl(struct tty_struct *tty,
return -EFAULT;

console_lock();
+ trace_me = current;
for (i = 0; i < MAX_NR_CONSOLES; i++) {
vc = vc_cons[i].d;

@@ -886,6 +891,7 @@ int vt_ioctl(struct tty_struct *tty,
vc_resize(vc_cons[i].d, cc, ll);
}
}
+ trace_me = NULL;
console_unlock();
break;
}
diff --git a/drivers/video/fbdev/vga16fb.c b/drivers/video/fbdev/vga16fb.c
index 1e8a38a7967d..fca76f6b9ab6 100644
--- a/drivers/video/fbdev/vga16fb.c
+++ b/drivers/video/fbdev/vga16fb.c
@@ -1145,6 +1145,8 @@ static void vga_8planes_imageblit(struct fb_info *info, const struct fb_image *i
setindex(oldindex);
}

+extern struct task_struct *trace_me;
+
static void vga_imageblit_expand(struct fb_info *info, const struct fb_image *image)
{
char __iomem *where = info->screen_base + (image->dx/8) +
@@ -1170,6 +1172,9 @@ static void vga_imageblit_expand(struct fb_info *info, const struct fb_image *im
readb(where); /* fill latches */
setmode(3);
wmb();
+ if (trace_me == current)
+ pr_info_ratelimited("image->{ data=%px dx=%u dy=%u height=%u width=%u } info->{ screen_base=%px fix.line_length=%u } where=%px\n",
+ cdat, image->dx, image->dy, image->height, image->width, info->screen_base, info->fix.line_length, where);
for (y = 0; y < image->height; y++) {
dst = where;
for (x = image->width/8; x--;)
----------

we can see that at least ffff8880000a0000-ffff888001000040 are accessed, and 'dy'
would be 201520 when 'where' is ffff888001000000, which corresponds with 'real_y()'
being 12595, for ioctl(VT_RESIZE) changed screen size to 2 x 16640.

----------
[ 222.885841][ T1675] vc=ffff888100109800 new_cols=2 new_rows=16640 new_screen_size=66560 newscreen=ffff888112120000
[ 222.886520][ T1675] vc=ffff888105d26800 new_cols=2 new_rows=16640 new_screen_size=66560 newscreen=ffff888112140000
[ 222.886662][ T1675] vc=ffff888105ca5800 new_cols=2 new_rows=16640 new_screen_size=66560 newscreen=ffff888112140000
[ 222.886799][ T1675] vc=ffff8881017ff800 new_cols=2 new_rows=16640 new_screen_size=66560 newscreen=ffff888112140000
[ 222.886951][ T1675] vc=ffff888102770800 new_cols=2 new_rows=16640 new_screen_size=66560 newscreen=ffff888112140000
[ 222.887089][ T1675] vc=ffff8881052e5800 new_cols=2 new_rows=16640 new_screen_size=66560 newscreen=ffff888112140000
[ 222.956019][ T1675] image->{ data=ffff888100a6e180 dx=8 dy=48 height=16 width=8 } info->{ screen_base=ffff8880000a0000 fix.line_length=80 } where=ffff8880000a0f01
[ 223.252930][ T1675] p=ffff888112120000 vc->{ vc_origin=ffff888112120000 vc_rows=16640 vc_cols=2 vc_scr_end=ffff888112130400 } start=ffff888112120004 count=33280 xx=0 yy=0
[ 223.253187][ T1675] image->{ data=ffff888100a6e190 dx=0 dy=0 height=16 width=16 } info->{ screen_base=ffff8880000a0000 fix.line_length=80 } where=ffff8880000a0000
[ 223.253777][ T1675] image->{ data=ffff888100a6e1b0 dx=0 dy=16 height=16 width=16 } info->{ screen_base=ffff8880000a0000 fix.line_length=80 } where=ffff8880000a0500
[ 223.254373][ T1675] image->{ data=ffff888100a6e1d0 dx=0 dy=32 height=16 width=16 } info->{ screen_base=ffff8880000a0000 fix.line_length=80 } where=ffff8880000a0a00
[ 223.254867][ T1675] image->{ data=ffff888100a6e1f0 dx=0 dy=48 height=16 width=16 } info->{ screen_base=ffff8880000a0000 fix.line_length=80 } where=ffff8880000a0f00
[ 223.255340][ T1675] image->{ data=ffff888100a6e210 dx=0 dy=64 height=16 width=16 } info->{ screen_base=ffff8880000a0000 fix.line_length=80 } where=ffff8880000a1400
[ 223.255834][ T1675] image->{ data=ffff888100a6e230 dx=0 dy=80 height=16 width=16 } info->{ screen_base=ffff8880000a0000 fix.line_length=80 } where=ffff8880000a1900
[ 223.256307][ T1675] image->{ data=ffff888100a6e250 dx=0 dy=96 height=16 width=16 } info->{ screen_base=ffff8880000a0000 fix.line_length=80 } where=ffff8880000a1e00
[ 223.256800][ T1675] image->{ data=ffff888100a6e270 dx=0 dy=112 height=16 width=16 } info->{ screen_base=ffff8880000a0000 fix.line_length=80 } where=ffff8880000a2300
[ 223.257274][ T1675] image->{ data=ffff888100a6e290 dx=0 dy=128 height=16 width=16 } info->{ screen_base=ffff8880000a0000 fix.line_length=80 } where=ffff8880000a2800
[ 224.897239][ T1675] BUG: unable to handle page fault for address: ffff888001000040
[ 224.897276][ T1675] #PF: supervisor write access in kernel mode
[ 224.897316][ T1675] #PF: error_code(0x0003) - permissions violation
[ 224.897354][ T1675] PGD 3c01067 P4D 3c01067 PUD 3c02067 PMD 80000000010001e1
[ 224.897454][ T1675] Oops: 0003 [#1] PREEMPT SMP
[ 224.897516][ T1675] CPU: 3 PID: 1675 Comm: a.out Not tainted 5.12.0+ #652
[ 224.897591][ T1675] Hardware name: innotek GmbH VirtualBox/VirtualBox, BIOS VirtualBox 12/01/2006
[ 224.897632][ T1675] RIP: 0010:vga16fb_imageblit+0x4b1/0x9d0
[ 224.897740][ T1675] Code: f6 74 47 45 89 fe 48 89 1c 24 4d 89 ef e8 b7 a0 9d ff 48 8b 04 24 49 83 c7 01 48 89 c2 48 83 c0 01 48 89 04 24 41 0f b6 47 ff <88> 02 31 ff 44 89 f6 41 83 ee 01 e8 ef a1 9d ff 41 83 fe ff 75 cd
[ 224.897820][ T1675] RSP: 0018:ffffc900010d3a30 EFLAGS: 00010286
[ 224.897879][ T1675] RAX: 0000000000000000 RBX: ffff888001000040 RCX: ffff888103a40100
[ 224.897972][ T1675] RDX: ffff888001000040 RSI: ffff888103a40100 RDI: 0000000000000002
[ 224.898027][ T1675] RBP: ffffc900010d3af8 R08: ffffffff8182e1b9 R09: 0000000000000000
[ 224.898083][ T1675] R10: 0000000000000005 R11: 0000000000080000 R12: ffff888101f36800
[ 224.898137][ T1675] R13: ffff888100a6ebc8 R14: 0000000000000001 R15: ffff888100a6ebc9
[ 224.898194][ T1675] FS: 00007fbefdeb6540(0000) GS:ffff88811bd80000(0000) knlGS:0000000000000000
[ 224.898269][ T1675] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 224.898328][ T1675] CR2: ffff888001000040 CR3: 0000000103a56003 CR4: 00000000000706e0
[ 224.898387][ T1675] Call Trace:
[ 224.898405][ T1675] bit_putcs+0x4dd/0x700
[ 224.898493][ T1675] ? write_comp_data+0x1c/0x70
[ 224.898568][ T1675] ? __sanitizer_cov_trace_switch+0x50/0x90
[ 224.898655][ T1675] ? bit_clear+0x1e0/0x1e0
[ 224.898742][ T1675] fbcon_putcs+0x13c/0x150
[ 224.898823][ T1675] do_update_region+0x1c6/0x2b0
[ 224.898935][ T1675] redraw_screen+0x2e4/0x310
[ 224.899051][ T1675] fbcon_blank+0x38f/0x440
[ 224.899139][ T1675] do_unblank_screen+0x10f/0x210
[ 224.899240][ T1675] vt_ioctl+0x116f/0x19c0
[ 224.899325][ T1675] ? lock_is_held_type+0xfc/0x170
[ 224.899413][ T1675] ? write_comp_data+0x1c/0x70
[ 224.899488][ T1675] ? __sanitizer_cov_trace_switch+0x50/0x90
[ 224.899576][ T1675] ? complete_change_console+0x160/0x160
[ 224.899661][ T1675] tty_ioctl+0x630/0xbb0
[ 224.899731][ T1675] ? __sanitizer_cov_trace_pc+0x1a/0x40
[ 224.899813][ T1675] ? do_vfs_ioctl+0x9b/0xca0
[ 224.899904][ T1675] ? lock_is_held_type+0xfc/0x170
[ 224.899982][ T1675] ? tty_vhangup+0x30/0x30
[ 224.900053][ T1675] __x64_sys_ioctl+0xbb/0x110
[ 224.900134][ T1675] do_syscall_64+0x3a/0xb0
[ 224.900229][ T1675] entry_SYSCALL_64_after_hwframe+0x44/0xae
[ 224.900326][ T1675] RIP: 0033:0x7fbefddda50b
[ 224.900379][ T1675] Code: 0f 1e fa 48 8b 05 85 39 0d 00 64 c7 00 26 00 00 00 48 c7 c0 ff ff ff ff c3 66 0f 1f 44 00 00 f3 0f 1e fa b8 10 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 55 39 0d 00 f7 d8 64 89 01 48
[ 224.900459][ T1675] RSP: 002b:00007ffe60c4e2b8 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
[ 224.900532][ T1675] RAX: ffffffffffffffda RBX: 0000557f9f780220 RCX: 00007fbefddda50b
[ 224.900589][ T1675] RDX: 0000000000000000 RSI: 0000000000004b3a RDI: 0000000000000003
[ 224.900641][ T1675] RBP: 0000000000000003 R08: 0000000000000000 R09: 00007fbefded0d50
[ 224.900694][ T1675] R10: 0000000000000000 R11: 0000000000000246 R12: 0000557f9f780130
[ 224.900748][ T1675] R13: 00007ffe60c4e3c0 R14: 0000000000000000 R15: 0000000000000000
[ 224.900804][ T1675] Modules linked in: sg video rapl evdev backlight input_leds mousedev led_class button ac binfmt_misc sd_mod t10_pi crc_t10dif crct10dif_generic sr_mod cdrom crct10dif_pclmul ata_generic crct10dif_common crc32_pclmul crc32c_intel ahci ghash_clmulni_intel psmouse libahci aesni_intel atkbd ata_piix libaes libps2 crypto_simd i2c_piix4 libata i8042 rtc_cmos i2c_core cryptd serio scsi_mod
[ 224.901745][ T1675] CR2: ffff888001000040
[ 224.901777][ T1675] ---[ end trace 045541aa43f10c56 ]---
[ 224.901841][ T1675] RIP: 0010:vga16fb_imageblit+0x4b1/0x9d0
[ 224.901948][ T1675] Code: f6 74 47 45 89 fe 48 89 1c 24 4d 89 ef e8 b7 a0 9d ff 48 8b 04 24 49 83 c7 01 48 89 c2 48 83 c0 01 48 89 04 24 41 0f b6 47 ff <88> 02 31 ff 44 89 f6 41 83 ee 01 e8 ef a1 9d ff 41 83 fe ff 75 cd
[ 224.902062][ T1675] RSP: 0018:ffffc900010d3a30 EFLAGS: 00010286
[ 224.902181][ T1675] RAX: 0000000000000000 RBX: ffff888001000040 RCX: ffff888103a40100
[ 224.902245][ T1675] RDX: ffff888001000040 RSI: ffff888103a40100 RDI: 0000000000000002
[ 224.902347][ T1675] RBP: ffffc900010d3af8 R08: ffffffff8182e1b9 R09: 0000000000000000
[ 224.902461][ T1675] R10: 0000000000000005 R11: 0000000000080000 R12: ffff888101f36800
[ 224.902524][ T1675] R13: ffff888100a6ebc8 R14: 0000000000000001 R15: ffff888100a6ebc9
[ 224.902580][ T1675] FS: 00007fbefdeb6540(0000) GS:ffff88811bd80000(0000) knlGS:0000000000000000
[ 224.902655][ T1675] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 224.902714][ T1675] CR2: ffff888001000040 CR3: 0000000103a56003 CR4: 00000000000706e0
[ 224.902777][ T1675] Kernel panic - not syncing: Fatal exception
[ 224.903014][ T1675] Kernel Offset: disabled
[ 225.661115][ T1675] Rebooting in 10 seconds..
----------

Therefore, I guess that the problem is that fbcon_putcs() from do_update_region() from
redraw_screen() from vt_kdsetmode(KD_TEXT) from ioctl(fd, KDSETMODE, KD_TEXT) tries to
redraw 2 x 16640 despite memory amount allocated for actual screen is only 80 x 30.

I don't know how to fix this problem...