[BUG] KASAN: vmalloc-out-of-bounds Write in fillrect

Jaeyoung Chung <[email protected]>
Newsgroups org.kernel.vger.linux-fbdev,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Hello,

We found a "KASAN: vmalloc-out-of-bounds Write in fillrect" on Linux v7.2.
The issue was found by our own race fuzzer. We have not analyzed the root cause,
so we do not have a proposed fix to offer.

To reproduce the race reliably, we applied the delay patch below to the
kernel and ran the C reproducer as root inside an x86_64 QEMU guest. The
crash log we observed, the delay patch and the reproducer are all included
below.

The following kernel config options are required to reproduce the issue:
    CONFIG_VT=y
    CONFIG_FB=y
    CONFIG_FB_CORE=y
    CONFIG_FRAMEBUFFER_CONSOLE=y
    CONFIG_DRM=y
    CONFIG_DRM_FBDEV_EMULATION=y
    CONFIG_DRM_BOCHS=y
    CONFIG_DRM_UDL=y
    CONFIG_USB_SUPPORT=y
    CONFIG_USB=y
    CONFIG_USB_GADGET=y
    CONFIG_USB_DUMMY_HCD=y
    CONFIG_USB_RAW_GADGET=y
    # CONFIG_DRM_VKMS is not set
    CONFIG_KASAN=y
    CONFIG_KASAN_VMALLOC=y

We hope this report is useful. Please let us know if any further
information would help.

Reported-by: Eulgyu Kim <[email protected]>
Reported-by: Jaeyoung Chung <[email protected]>

Kernel delay patch:
==================================================================
diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 8f467b22b799..bf68bba39b55 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -73,6 +73,7 @@
 
 #include <linux/module.h>
 #include <linux/types.h>
+#include <linux/delay.h>
 #include <linux/sched/signal.h>
 #include <linux/tty.h>
 #include <linux/tty_flip.h>
@@ -3272,6 +3273,7 @@ static int vc_con_write_normal(struct vc_data *vc, int tc, int c,
 	return 0;
 }
 
+static int syz_dcw_hits;
 /* acquires console_lock */
 static int do_con_write(struct tty_struct *tty, const u8 *buf, int count)
 {
@@ -3287,6 +3289,10 @@ static int do_con_write(struct tty_struct *tty, const u8 *buf, int count)
 	if (in_interrupt())
 		return count;
 
+	if (syz_dcw_hits < 256 && strncmp(current->comm, "syzrepro2", 9) == 0) {
+		syz_dcw_hits++;
+		mdelay(5);
+	}
 	guard(console_lock)();
 	currcons = vc->vc_num;
 	if (!vc_cons_allocated(currcons)) {
diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c
index 39f44258d793..f2cb909c8e5b 100644
--- a/drivers/video/fbdev/core/bitblit.c
+++ b/drivers/video/fbdev/core/bitblit.c
@@ -16,9 +16,13 @@
 #include <linux/fb.h>
 #include <linux/vt_kern.h>
 #include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/printk.h>
+#include <linux/sched.h>
 #include <asm/types.h>
 #include "fbcon.h"
 
+static int syz_bc_hits;
 /*
  * Accelerated handlers.
  */
@@ -69,6 +73,10 @@ static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy,
 	region.height = height * vc->vc_font.height;
 	region.rop = ROP_COPY;
 
+	if (syz_bc_hits < 96 && strncmp(current->comm, "syzrepro", 8) == 0) {
+		syz_bc_hits++;
+		mdelay(5);
+	}
 	info->fbops->fb_fillrect(info, &region);
 }
 
diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
index 23b3c536d53d..75a14ee4ca13 100644
--- a/drivers/video/fbdev/core/fbcon.c
+++ b/drivers/video/fbdev/core/fbcon.c
@@ -62,6 +62,7 @@
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/delay.h>	/* MSch: for IRQ probe */
+#include <linux/sched.h>
 #include <linux/console.h>
 #include <linux/string.h>
 #include <linux/kd.h>
@@ -1492,6 +1493,9 @@ static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var,
 	rows /= vc->vc_font.height;
 	ret = vc_resize(vc, cols, rows);
 
+	if (strncmp(current->comm, "syzrepro", 8) == 0) {
+		mdelay(10);
+	}
 	if (con_is_visible(vc) && !ret)
 		update_screen(vc);
 }
@@ -2082,6 +2086,7 @@ static void updatescrollmode(struct fbcon_display *p,
 	updatescrollmode_accel(p, info, vc);
 }
 
+static int syz_fr_hits;
 static int fbcon_resize(struct vc_data *vc, unsigned int width,
 			unsigned int height, bool from_user)
 {
@@ -2091,6 +2096,10 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width,
 	struct fb_var_screeninfo var = info->var;
 	int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh;
 
+	if (syz_fr_hits < 32 && strncmp(current->comm, "syzrepro", 8) == 0) {
+		syz_fr_hits++;
+		mdelay(5);
+	}
 	if (font_data_size(p->fontdata)) {
 		unsigned int size = vc_font_size(&vc->vc_font);
 
@@ -2119,6 +2128,9 @@ static int fbcon_resize(struct vc_data *vc, unsigned int width,
 
 		pr_debug("attempting resize %ix%i\n", var.xres, var.yres);
 		mode = fb_find_best_mode(&var, &info->modelist);
+		if (strncmp(current->comm, "syzrepro", 8) == 0) {
+			mdelay(5);
+		}
 		if (mode == NULL)
 			return -EINVAL;
 		display_to_var(&var, p);
==================================================================

C reproducer:
==================================================================
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#include <linux/types.h>
#include <linux/fb.h>

#define UDC_NAME_LENGTH_MAX 128

struct usb_raw_init {
	__u8 driver_name[UDC_NAME_LENGTH_MAX];
	__u8 device_name[UDC_NAME_LENGTH_MAX];
	__u8 speed;
};

enum usb_raw_event_type {
	USB_RAW_EVENT_INVALID = 0,
	USB_RAW_EVENT_CONNECT = 1,
	USB_RAW_EVENT_CONTROL = 2,
	USB_RAW_EVENT_SUSPEND = 3,
	USB_RAW_EVENT_RESUME = 4,
	USB_RAW_EVENT_RESET = 5,
	USB_RAW_EVENT_DISCONNECT = 6,
};

struct usb_raw_event {
	__u32 type;
	__u32 length;
	__u8 data[0];
};

struct usb_raw_ep_io {
	__u16 ep;
	__u16 flags;
	__u32 length;
	__u8 data[0];
};

#define USB_RAW_IOCTL_INIT		_IOW('U', 0, struct usb_raw_init)
#define USB_RAW_IOCTL_RUN		_IO('U', 1)
#define USB_RAW_IOCTL_EVENT_FETCH	_IOR('U', 2, struct usb_raw_event)
#define USB_RAW_IOCTL_EP0_WRITE		_IOW('U', 3, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP0_READ		_IOWR('U', 4, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_ENABLE		_IOW('U', 5, struct usb_endpoint_descriptor)
#define USB_RAW_IOCTL_EP_DISABLE	_IOW('U', 6, __u32)
#define USB_RAW_IOCTL_EP_WRITE		_IOW('U', 7, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_EP_READ		_IOWR('U', 8, struct usb_raw_ep_io)
#define USB_RAW_IOCTL_CONFIGURE		_IO('U', 9)
#define USB_RAW_IOCTL_VBUS_DRAW		_IOW('U', 10, __u32)
#define USB_RAW_IOCTL_EP0_STALL		_IO('U', 12)

#define RAW_IO_MAX	4096

struct usb_ctrlrequest {
	__u8 bRequestType;
	__u8 bRequest;
	__u16 wValue;
	__u16 wIndex;
	__u16 wLength;
} __attribute__((packed));

struct usb_endpoint_descriptor {
	__u8 bLength;
	__u8 bDescriptorType;
	__u8 bEndpointAddress;
	__u8 bmAttributes;
	__u16 wMaxPacketSize;
	__u8 bInterval;
} __attribute__((packed));

#define USB_DIR_OUT			0x00
#define USB_DIR_IN			0x80
#define USB_TYPE_VENDOR			0x40
#define USB_REQ_GET_STATUS		0x00
#define USB_REQ_SET_ADDRESS		0x05
#define USB_REQ_GET_DESCRIPTOR		0x06
#define USB_REQ_GET_CONFIGURATION	0x08
#define USB_REQ_SET_CONFIGURATION	0x09
#define USB_REQ_SET_INTERFACE		0x0b
#define USB_DT_DEVICE			0x01
#define USB_DT_CONFIG			0x02
#define USB_DT_STRING			0x03
#define USB_SPEED_HIGH			3

#define UDL_REQ_EDID			0x02
#define UDL_EDID_REQTYPE		0xc0
#define UDL_EDID_WINDEX			0x00a1

#ifndef FBIOPUT_CON2FBMAP
#define FBIOGET_CON2FBMAP	0x460F
#define FBIOPUT_CON2FBMAP	0x4610
#endif

#define TTY_PATH	"/dev/tty1"
#define VT_INDEX	1

#define UDL_W		640
#define UDL_H		480

#define OOB_ROW_DEFAULT	30

#define FONT_H		16

#define FB0_PIXEL_ROWS	768

static int g_raw_fd = -1;
static volatile int g_ep_bulk = -1;
static volatile int g_stop;
static volatile int g_armed;
static int g_tty_fd = -1;
static unsigned char g_edid[128];
static volatile unsigned int g_edid_reads;
static volatile unsigned int g_ctrl_reqs;

static void set_name(const char *n)
{
	prctl(PR_SET_NAME, (unsigned long)n, 0UL, 0UL, 0UL);
}

static void msleep_(int ms)
{
	struct timespec ts;

	ts.tv_sec = ms / 1000;
	ts.tv_nsec = (long)(ms % 1000) * 1000000L;
	nanosleep(&ts, NULL);
}

static void rlog(const char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	vfprintf(stdout, fmt, ap);
	va_end(ap);
	fputc('\n', stdout);
	fflush(stdout);
}

static void build_edid(void)
{
	unsigned char *d;
	int i, sum;

	memset(g_edid, 0, sizeof(g_edid));

	g_edid[0] = 0x00;
	g_edid[1] = 0xff; g_edid[2] = 0xff; g_edid[3] = 0xff;
	g_edid[4] = 0xff; g_edid[5] = 0xff; g_edid[6] = 0xff;
	g_edid[7] = 0x00;

	g_edid[8] = 0x4f; g_edid[9] = 0x3a;
	g_edid[10] = 0x01; g_edid[11] = 0x00;
	g_edid[16] = 1;
	g_edid[17] = 30;
	g_edid[18] = 1; g_edid[19] = 3;
	g_edid[20] = 0x80;
	g_edid[21] = 32; g_edid[22] = 24;
	g_edid[23] = 120;
	g_edid[24] = 0x0a;

	g_edid[35] = 0x20;

	for (i = 38; i < 54; i++)
		g_edid[i] = 0x01;

	d = &g_edid[54];
	d[0] = 0xd5; d[1] = 0x09;
	d[2] = 0x80;
	d[3] = 0xa0;
	d[4] = 0x20;
	d[5] = 0xe0;
	d[6] = 0x2d;
	d[7] = 0x10;
	d[8] = 0x10;
	d[9] = 0x60;
	d[10] = 0xa2;
	d[11] = 0x00;
	d[12] = 0x00; d[13] = 0x00; d[14] = 0x00;
	d[15] = 0x00; d[16] = 0x00;
	d[17] = 0x18;

	d = &g_edid[72];
	d[3] = 0xfd;
	d[5] = 50; d[6] = 70;
	d[7] = 30; d[8] = 60;
	d[9] = 10;
	d[11] = 0x0a;
	for (i = 12; i < 18; i++)
		d[i] = 0x20;

	d = &g_edid[90];
	d[3] = 0xfc;
	d[5] = 'S'; d[6] = 'Y'; d[7] = 'Z'; d[8] = 0x0a;
	for (i = 9; i < 18; i++)
		d[i] = 0x20;

	d = &g_edid[108];
	d[3] = 0x10;

	g_edid[126] = 0;

	sum = 0;
	for (i = 0; i < 127; i++)
		sum += g_edid[i];
	g_edid[127] = (unsigned char)((0x100 - (sum & 0xff)) & 0xff);
}

static const unsigned char dev_desc[18] = {
	18, USB_DT_DEVICE,
	0x00, 0x02,
	0x00, 0x00, 0x00,
	64,
	0xe9, 0x17,
	0x01, 0x43,
	0x00, 0x01,
	0, 0, 0,
	1,
};

static const unsigned char cfg_desc[25] = {

	9, USB_DT_CONFIG,
	25, 0,
	1,
	1,
	0,
	0xa0,
	0x32,

	9, 0x04,
	0,
	0,
	1,
	0xff, 0x00, 0x00,
	0,

	7, 0x05,
	0x01,
	0x02,
	0x00, 0x02,
	0,
};

static struct usb_endpoint_descriptor bulk_ep_desc = {
	.bLength = 7,
	.bDescriptorType = 0x05,
	.bEndpointAddress = 0x01,
	.bmAttributes = 0x02,
	.wMaxPacketSize = 512,
	.bInterval = 0,
};

struct ep_io_buf {
	struct usb_raw_ep_io inner;
	unsigned char data[RAW_IO_MAX];
};

static void ep0_stall(int fd)
{
	if (ioctl(fd, USB_RAW_IOCTL_EP0_STALL, 0) < 0)
		rlog("repro: ep0 stall failed: %s", strerror(errno));
}

static void ep0_in(int fd, const void *buf, int len, int wlen)
{
	static struct ep_io_buf io;

	if (len > wlen)
		len = wlen;
	if (len > RAW_IO_MAX)
		len = RAW_IO_MAX;
	memset(&io.inner, 0, sizeof(io.inner));
	io.inner.ep = 0;
	io.inner.flags = 0;
	io.inner.length = len;
	if (len)
		memcpy(io.data, buf, len);
	if (ioctl(fd, USB_RAW_IOCTL_EP0_WRITE, &io) < 0)
		rlog("repro: ep0 IN write(%d) failed: %s", len, strerror(errno));
}

static void ep0_out(int fd, int wlen)
{
	static struct ep_io_buf io;

	if (wlen < 0)
		wlen = 0;
	if (wlen > RAW_IO_MAX)
		wlen = RAW_IO_MAX;
	memset(&io.inner, 0, sizeof(io.inner));
	io.inner.ep = 0;
	io.inner.flags = 0;
	io.inner.length = wlen;
	if (ioctl(fd, USB_RAW_IOCTL_EP0_READ, &io) < 0)
		rlog("repro: ep0 OUT read(%d) failed: %s", wlen, strerror(errno));
}

static void *bulk_thread(void *unused)
{
	static struct ep_io_buf io;

	(void)unused;
	set_name("syzrepro3");
	while (!g_stop) {
		int ep = g_ep_bulk;

		if (ep < 0) {
			msleep_(20);
			continue;
		}
		memset(&io.inner, 0, sizeof(io.inner));
		io.inner.ep = ep;
		io.inner.flags = 0;
		io.inner.length = RAW_IO_MAX;
		if (ioctl(g_raw_fd, USB_RAW_IOCTL_EP_READ, &io) < 0) {
			if (errno == EINTR)
				continue;
			msleep_(2);
		}
	}
	return NULL;
}

static void handle_control(int fd, struct usb_ctrlrequest *ctrl)
{
	int wlen = ctrl->wLength;
	unsigned char rep[2];
	int idx, ep;

	if (g_ctrl_reqs < 24)
		rlog("repro: ctrl bRT=%02x bR=%02x wV=%04x wI=%04x wL=%u",
		     ctrl->bRequestType, ctrl->bRequest, ctrl->wValue,
		     ctrl->wIndex, ctrl->wLength);
	g_ctrl_reqs++;

	if (ctrl->bRequestType & USB_DIR_IN) {
		switch (ctrl->bRequestType) {
		case 0x80:
			if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) {
				switch (ctrl->wValue >> 8) {
				case USB_DT_DEVICE:
					ep0_in(fd, dev_desc, sizeof(dev_desc), wlen);
					return;
				case USB_DT_CONFIG:
					ep0_in(fd, cfg_desc, sizeof(cfg_desc), wlen);
					return;
				case USB_DT_STRING: {
					unsigned char s[4] = { 4, USB_DT_STRING, 0x09, 0x04 };

					ep0_in(fd, s, sizeof(s), wlen);
					return;
				}
				default:

					ep0_stall(fd);
					return;
				}
			}
			if (ctrl->bRequest == USB_REQ_GET_STATUS) {
				rep[0] = 0; rep[1] = 0;
				ep0_in(fd, rep, 2, wlen);
				return;
			}
			if (ctrl->bRequest == USB_REQ_GET_CONFIGURATION) {
				rep[0] = 1;
				ep0_in(fd, rep, 1, wlen);
				return;
			}
			ep0_stall(fd);
			return;
		case UDL_EDID_REQTYPE:
			if (ctrl->bRequest == UDL_REQ_EDID &&
			    ctrl->wIndex == UDL_EDID_WINDEX) {
				idx = (ctrl->wValue >> 8) & 0xff;
				rep[0] = 0x00;
				rep[1] = (idx < 128) ? g_edid[idx] : 0x00;
				g_edid_reads++;
				ep0_in(fd, rep, 2, wlen);
				return;
			}
			ep0_stall(fd);
			return;
		default:
			ep0_stall(fd);
			return;
		}
	}

	switch (ctrl->bRequestType) {
	case 0x00:
		if (ctrl->bRequest == USB_REQ_SET_CONFIGURATION) {
			if (ioctl(fd, USB_RAW_IOCTL_VBUS_DRAW, 0x32) < 0)
				rlog("repro: vbus_draw failed: %s", strerror(errno));
			if (ioctl(fd, USB_RAW_IOCTL_CONFIGURE, 0) < 0)
				rlog("repro: configure failed: %s", strerror(errno));

			ep0_out(fd, wlen);
			ep = ioctl(fd, USB_RAW_IOCTL_EP_ENABLE, &bulk_ep_desc);
			if (ep < 0)
				rlog("repro: ep_enable failed: %s (udl URBs will "
				     "fail with -EPIPE, harmless here)",
				     strerror(errno));
			else
				g_ep_bulk = ep;
			return;
		}
		ep0_out(fd, wlen);
		return;
	case 0x01:
	case 0x02:
		ep0_out(fd, wlen);
		return;
	case (USB_DIR_OUT | USB_TYPE_VENDOR):
		ep0_out(fd, wlen);
		return;
	default:
		ep0_stall(fd);
		return;
	}
}

static void *usb_thread(void *unused)
{
	struct usb_raw_init init;
	struct {
		struct usb_raw_event inner;
		struct usb_ctrlrequest ctrl;
	} ev;
	int fd;

	(void)unused;
	set_name("syzrepro0");

	fd = open("/dev/raw-gadget", O_RDWR);
	if (fd < 0) {
		return NULL;
	}
	g_raw_fd = fd;

	memset(&init, 0, sizeof(init));
	strcpy((char *)init.driver_name, "dummy_udc");
	strcpy((char *)init.device_name, "dummy_udc.0");
	init.speed = USB_SPEED_HIGH;
	if (ioctl(fd, USB_RAW_IOCTL_INIT, &init) < 0) {
		return NULL;
	}
	if (ioctl(fd, USB_RAW_IOCTL_RUN, 0) < 0) {
		return NULL;
	}

	while (!g_stop) {
		memset(&ev, 0, sizeof(ev));
		ev.inner.type = 0;
		ev.inner.length = sizeof(ev.ctrl);
		if (ioctl(fd, USB_RAW_IOCTL_EVENT_FETCH, &ev) < 0) {
			if (errno == EINTR)
				continue;
			break;
		}
		if (ev.inner.type != USB_RAW_EVENT_CONTROL)
			continue;
		handle_control(fd, &ev.ctrl);
	}
	return NULL;
}

static const char scroll_seq[] = "\033[H\033[M";

static void tty_write(const char *s)
{
	if (g_tty_fd >= 0)
		(void)!write(g_tty_fd, s, strlen(s));
}

static void *writer_thread(void *unused)
{
	int i;

	(void)unused;
	set_name("syzrepro2");

	for (i = 0; i < 2000 && !g_stop; i++) {
		tty_write(scroll_seq);
		msleep_(20);
	}
	return NULL;
}

static int open_udl_fb(int *node_out, unsigned int *buf_rows_out)
{
	char path[64];
	int i;

	for (i = 1; i < 8; i++) {
		struct fb_var_screeninfo var;
		struct fb_fix_screeninfo fix;
		int fd;

		snprintf(path, sizeof(path), "/dev/fb%d", i);
		fd = open(path, O_RDWR);
		if (fd < 0)
			continue;
		memset(&var, 0, sizeof(var));
		memset(&fix, 0, sizeof(fix));
		if (ioctl(fd, FBIOGET_VSCREENINFO, &var) == 0 &&
		    ioctl(fd, FBIOGET_FSCREENINFO, &fix) == 0) {
			if (strstr(fix.id, "udl") ||
			    (var.xres == UDL_W && var.yres == UDL_H)) {
				*node_out = i;

				*buf_rows_out = fix.line_length ?
					fix.smem_len / fix.line_length : 0;
				return fd;
			}
		}
		close(fd);
	}
	return -1;
}

static int shrink_fb_var(int fd)
{
	struct fb_var_screeninfo var;

	memset(&var, 0, sizeof(var));
	if (ioctl(fd, FBIOGET_VSCREENINFO, &var) < 0) {
		return -1;
	}
	var.xres = 8;
	var.yres = 8;
	var.xres_virtual = 8;
	var.yres_virtual = 8;
	var.xoffset = 0;
	var.yoffset = 0;
	var.activate = FB_ACTIVATE_NOW;
	if (ioctl(fd, FBIOPUT_VSCREENINFO, &var) < 0) {
		return -1;
	}
	memset(&var, 0, sizeof(var));
	ioctl(fd, FBIOGET_VSCREENINFO, &var);
	return 0;
}

static int bind_console(int fd, int node)
{
	struct fb_con2fbmap c2f;

	memset(&c2f, 0, sizeof(c2f));
	c2f.console = VT_INDEX;
	c2f.framebuffer = node;
	if (ioctl(fd, FBIOPUT_CON2FBMAP, &c2f) < 0) {
		return -1;
	}
	memset(&c2f, 0, sizeof(c2f));
	c2f.console = VT_INDEX;
	if (ioctl(fd, FBIOGET_CON2FBMAP, &c2f) == 0)
		rlog("repro: vt%d is now mapped to fb%d", VT_INDEX, c2f.framebuffer);
	return 0;
}

int main(void)
{
	pthread_t th_usb, th_bulk, th_writer;
	unsigned int buf_rows = UDL_H, oob_row;
	int fb_fd, fb_node = -1;
	char seq[64];
	int i;


	setvbuf(stdout, NULL, _IOLBF, 0);
	set_name("syzrepro1");
	build_edid();

	g_tty_fd = open(TTY_PATH, O_RDWR | O_NOCTTY);
	if (g_tty_fd < 0) {
		return 1;
	}

	tty_write("\033c");
	msleep_(50);
	tty_write("\033[2J\033[H");

	pthread_create(&th_usb, NULL, usb_thread, NULL);
	pthread_create(&th_bulk, NULL, bulk_thread, NULL);

	fb_fd = -1;
	for (i = 0; i < 100 && fb_fd < 0; i++) {
		msleep_(200);
		fb_fd = open_udl_fb(&fb_node, &buf_rows);
	}

	if (fb_fd < 0) {
		g_stop = 1;
		msleep_(200);
		return 0;
	}

	if (buf_rows == 0 || buf_rows >= FB0_PIXEL_ROWS) {
		g_stop = 1;
		msleep_(200);
		return 0;
	}

	if (shrink_fb_var(fb_fd) < 0) {
		g_stop = 1;
		return 1;
	}

	oob_row = buf_rows / FONT_H;
	if (oob_row < 1 || oob_row > 46)
		oob_row = OOB_ROW_DEFAULT;

	snprintf(seq, sizeof(seq), "\033[2J\033[1;%ur\033[H", oob_row + 1);
	tty_write(seq);
	msleep_(50);

	if (bind_console(fb_fd, fb_node) < 0) {
		g_stop = 1;
		return 1;
	}
	g_armed = 1;

	pthread_create(&th_writer, NULL, writer_thread, NULL);

	for (i = 0; i < 400; i++) {
		tty_write(scroll_seq);
		msleep_(20);
	}

	g_stop = 1;
	msleep_(200);
	return 0;
}
==================================================================

Crash log:
==================================================================
BUG: KASAN: vmalloc-out-of-bounds in fb_write_offset drivers/video/fbdev/core/sysmem.h:30 [inline]
BUG: KASAN: vmalloc-out-of-bounds in bitfill drivers/video/fbdev/core/fb_fillrect.h:133 [inline]
BUG: KASAN: vmalloc-out-of-bounds in fb_fillrect_static drivers/video/fbdev/core/fb_fillrect.h:219 [inline]
BUG: KASAN: vmalloc-out-of-bounds in fb_fillrect drivers/video/fbdev/core/fb_fillrect.h:278 [inline]
BUG: KASAN: vmalloc-out-of-bounds in sys_fillrect+0x134b/0x1920 drivers/video/fbdev/core/sysfillrect.c:24
Write of size 8 at addr ffffc90002707000 by task syzrepro1/400

CPU: 0 UID: 0 PID: 400 Comm: syzrepro1 Not tainted 7.2.0-dirty #4 PREEMPT 
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1ubuntu1.1 04/01/2014
Call Trace:
 <TASK>
 dump_stack_lvl+0x5e/0x80 lib/dump_stack.c:120
 print_address_description+0x77/0x200 mm/kasan/report.c:378
 print_report+0x64/0x70 mm/kasan/report.c:482
 kasan_report+0x118/0x150 mm/kasan/report.c:595
 fb_write_offset drivers/video/fbdev/core/sysmem.h:30 [inline]
 bitfill drivers/video/fbdev/core/fb_fillrect.h:133 [inline]
 fb_fillrect_static drivers/video/fbdev/core/fb_fillrect.h:219 [inline]
 fb_fillrect drivers/video/fbdev/core/fb_fillrect.h:278 [inline]
 sys_fillrect+0x134b/0x1920 drivers/video/fbdev/core/sysfillrect.c:24
 drm_fbdev_shmem_defio_fillrect+0x26/0x110 drivers/gpu/drm/drm_fbdev_shmem.c:39
 bit_clear+0x12c/0x1d0 drivers/video/fbdev/core/bitblit.c:80
 __fbcon_clear+0x7ee/0xd00 drivers/video/fbdev/core/fbcon.c:-1
 fbcon_scroll+0x2a8/0x6e0 drivers/video/fbdev/core/fbcon.c:1931
 con_scroll+0x62b/0x8a0 drivers/tty/vt/vt.c:617
 csi_M drivers/tty/vt/vt.c:2192 [inline]
 csi_ECMA+0x58c/0x6610 drivers/tty/vt/vt.c:2615
 do_con_trol drivers/tty/vt/vt.c:-1 [inline]
 do_con_write+0x3452/0x4960 drivers/tty/vt/vt.c:3331
 con_write+0x13/0x30 drivers/tty/vt/vt.c:3667
 process_output_block drivers/tty/n_tty.c:557 [inline]
 n_tty_write+0xd4c/0x10c0 drivers/tty/n_tty.c:2366
 iterate_tty_write drivers/tty/tty_io.c:1006 [inline]
 file_tty_write+0x4f2/0x870 drivers/tty/tty_io.c:1081
 new_sync_write fs/read_write.c:595 [inline]
 vfs_write+0x7a2/0xa10 fs/read_write.c:687
 ksys_write+0xb0/0x170 fs/read_write.c:739
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0xf7/0x370 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x76/0x7e
RIP: 0033:0x7ae99dec038f
Code: 89 54 24 18 48 89 74 24 10 89 7c 24 08 e8 a9 d4 f8 ff 48 8b 54 24 18 48 8b 74 24 10 41 89 c0 8b 7c 24 08 b8 01 00 00 00 0f 05 <48> 3d 00 f0 ff ff 77 31 44 89 c7 48 89 44 24 08 e8 fc d4 f8 ff 48
RSP: 002b:00007ffe8ce95bd0 EFLAGS: 00000293 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 0000000000000190 RCX: 00007ae99dec038f
RDX: 0000000000000006 RSI: 0000594d1e9ad1c0 RDI: 0000000000000003
RBP: 0000594d1e9ad1c0 R08: 0000000000000000 R09: 00007ffe8ce95b27
R10: 0000000000000008 R11: 0000000000000293 R12: 000000000000001e
R13: 00007ffe8ce95d20 R14: 0000000000000000 R15: 00007ae99dfef020
 </TASK>

The buggy address belongs to a vmalloc virtual mapping
Memory state around the buggy address:
 ffffc90002706f00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 ffffc90002706f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>ffffc90002707000: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
                   ^
 ffffc90002707080: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
 ffffc90002707100: f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8 f8
==================================================================
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.