* [BUG] KASAN: vmalloc-out-of-bounds Write in fillrect
@ 2026-08-19 19:18 Jaeyoung Chung
2026-08-19 19:30 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Jaeyoung Chung @ 2026-08-19 19:18 UTC (permalink / raw)
To: deller, dri-devel, linux-fbdev, simona, soci
Cc: linux-kernel, eulgyukim, jjy600901
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 <eulgyukim@snu.ac.kr>
Reported-by: Jaeyoung Chung <jjy600901@snu.ac.kr>
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, ®ion);
}
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
==================================================================
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [BUG] KASAN: vmalloc-out-of-bounds Write in fillrect
2026-08-19 19:18 [BUG] KASAN: vmalloc-out-of-bounds Write in fillrect Jaeyoung Chung
@ 2026-08-19 19:30 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-08-19 19:30 UTC (permalink / raw)
To: Jaeyoung Chung; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [High] The proposed patch contains fuzzer-specific debugging delays based on process names, which is unsuitable for mainline and introduces potential DoS.
- [Medium] The patch introduces data races on global tracking variables used for debugging.
- [Medium] The commit message promises a crash log and C reproducer, but they are missing from the commit.
Pre-existing issues:
- [Critical] Pre-existing vmalloc out-of-bounds write caused by mismatched virtual console and framebuffer dimensions when `vc_resize` fails.
- [High] fbcon_set_disp() silently returns early if var_to_display() fails, leaving the virtual console partially configured.
- [High] Framebuffer geometry reconfiguration races with console drawing operations.
--
--- Patch [1]: [BUG] KASAN: vmalloc-out-of-bounds Write in fillrect ---
commit 9b426af657fe7eb031d119fdffbef9574baa163d
Author: Jaeyoung Chung <jjy600901@snu.ac.kr>
KASAN: vmalloc-out-of-bounds Write in fillrect
This commit reports a vmalloc out-of-bounds write in fillrect discovered
by a race fuzzer. It provides a patch adding artificial delays to help
reproduce the race reliably.
> KASAN: vmalloc-out-of-bounds Write in fillrect
>
> 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.
[Severity: Medium]
The commit message indicates that a crash log and C reproducer are included
below, but they appear to be missing from the commit text. Can these be
provided to help with the analysis?
> diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
> index 8f467b22b799e..bf68bba39b559 100644
> --- a/drivers/tty/vt/vt.c
> +++ b/drivers/tty/vt/vt.c
[ ... ]
> @@ -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);
> + }
[Severity: High]
Is this debugging logic intended for upstream? Introducing an unbounded
delay based on user-controlled process names (current->comm) might allow
an unprivileged local user to stall the console subsystem indefinitely.
[Severity: Medium]
Can this unsynchronized increment of syz_dcw_hits lead to data races?
Concurrent execution of console output could cause undefined behavior
and incorrect hit counts.
> guard(console_lock)();
[Severity: High]
This is a pre-existing issue, but does console drawing race with
framebuffer geometry reconfiguration?
Looking at do_con_write(), it executes under console_lock but does not
take info->lock. Framebuffer reconfiguration, such as via fb_set_var(),
modifies info->var and reallocates memory under info->lock.
This could lead to data races or out-of-bounds writes on the framebuffer
memory if ioctl(FBIOPUT_VSCREENINFO) and console writes happen concurrently.
> diff --git a/drivers/video/fbdev/core/bitblit.c b/drivers/video/fbdev/core/bitblit.c
> index 39f44258d793f..f2cb909c8e5bd 100644
> --- a/drivers/video/fbdev/core/bitblit.c
> +++ b/drivers/video/fbdev/core/bitblit.c
[ ... ]
> @@ -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, ®ion);
> }
[Severity: Critical]
This is a pre-existing issue, but could the region calculation lead to a
vmalloc out-of-bounds write?
In bit_clear(), the region dimensions are calculated using vc->vc_font
geometry:
region.dx = sx * vc->vc_font.width;
If vc_resize() fails during fbcon_set_disp() but leaves vc_cols and
vc_rows at their previous sizes, the mismatched virtual console and
framebuffer dimensions can cause bit_clear() to write outside the
bounds of the framebuffer.
[Severity: Medium]
Does this code introduce data races due to the unsynchronized increment
of syz_bc_hits?
> diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
> index 23b3c536d53d1..75a14ee4ca131 100644
> --- a/drivers/video/fbdev/core/fbcon.c
> +++ b/drivers/video/fbdev/core/fbcon.c
[ ... ]
> @@ -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);
[Severity: Critical]
This is a pre-existing issue, but does ignoring the vc_resize() error
leave the console bound with desynced dimensions?
In fbcon_set_disp(), if vc_resize() fails due to a memory allocation
failure or invalid dimensions, it returns an error. However, this error
is only used to skip update_screen():
if (con_is_visible(vc) && !ret)
update_screen(vc);
This ignores the error and leaves vc_cols and vc_rows at their previous
sizes, which can later cause the out-of-bounds writes in bit_clear()
mentioned above.
[Severity: High]
This is a pre-existing issue, but does fbcon_set_disp() silently ignore
failures from var_to_display()?
fbcon_set_disp() contains an early return that aborts configuration without
propagating the error:
if (var_to_display(p, var, info))
return;
Could this leave the console state inconsistent with the framebuffer state
and lead to further memory safety issues?
> + if (strncmp(current->comm, "syzrepro", 8) == 0) {
> + mdelay(10);
> + }
[Severity: High]
Does this code introduce an unbounded delay conditional on user-controlled
input, potentially leading to a denial of service?
> if (con_is_visible(vc) && !ret)
> update_screen(vc);
> }
[ ... ]
> @@ -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);
> + }
[Severity: Medium]
Can this unsynchronized increment of syz_fr_hits lead to data races?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260819191849.46929-1-jjy600901@snu.ac.kr?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-19 19:30 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-19 19:18 [BUG] KASAN: vmalloc-out-of-bounds Write in fillrect Jaeyoung Chung
2026-08-19 19:30 ` sashiko-bot
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.