drivers/video/fbdev/core/fb_imageblit.h | 66 +++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 4 deletions(-)
From: Shaurya Rane <ssrane_b23@ee.vjti.ac.in>
syzbot reported a vmalloc-out-of-bounds write in fb_imageblit. The crash
occurs when drawing an image at the very end of the framebuffer memory.
The current bounds check in fb_imageblit limits the drawing height (max_y)
by dividing the screen size by the line length. However, this calculation
only ensures that the start of the last line fits within the buffer. It
fails to account for the width of the image on that final line. If the
image width (multiplied by bpp) exceeds the remaining space on the last
line, the drawing routine writes past the end of the allocated video
memory.
This patch replaces the insufficient check with a more precise one. It
calculates the effective width in bytes of the image (accounting for
clipping against xres_virtual) and ensures that the last byte of the
operation falls within the screen buffer. Specifically, it checks if
'(dy + height - 1) * line_length + effective_width_bytes' exceeds
screen_size. If it does, the drawing height max_y is reduced to
prevent the out-of-bounds access.
Reported-by: syzbot+5a40432dfe8f86ee657a@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=5a40432dfe8f86ee657a
Signed-off-by: Shaurya Rane <ssrane_b23@ee.vjti.ac.in>
---
drivers/video/fbdev/core/fb_imageblit.h | 66 +++++++++++++++++++++++--
1 file changed, 62 insertions(+), 4 deletions(-)
diff --git a/drivers/video/fbdev/core/fb_imageblit.h b/drivers/video/fbdev/core/fb_imageblit.h
index 3b2bb4946505..0c0d05cff3f8 100644
--- a/drivers/video/fbdev/core/fb_imageblit.h
+++ b/drivers/video/fbdev/core/fb_imageblit.h
@@ -485,11 +485,69 @@ static inline void fb_imageblit(struct fb_info *p, const struct fb_image *image)
struct fb_address dst = fb_address_init(p);
struct fb_reverse reverse = fb_reverse_init(p);
const u32 *palette = fb_palette(p);
+ struct fb_image clipped_image;
+ u32 max_x, max_y;
+ unsigned long max_offset_bytes;
+
+ /* Validate basic parameters */
+ if (!image || !p->screen_buffer || !p->screen_size ||
+ !image->width || !image->height)
+ return;
+
+ /* Calculate maximum addressable coordinates based on virtual resolution and buffer size */
+ max_x = p->var.xres_virtual;
+ max_y = p->var.yres_virtual;
+
+ /* Check against actual buffer size to prevent vmalloc overflow */
+ {
+ unsigned long effective_width_bytes;
+ u32 right_edge = image->dx + image->width;
+
+ if (right_edge < image->dx)
+ right_edge = max_x;
+ else
+ right_edge = min(right_edge, max_x);
+
+ effective_width_bytes = (unsigned long)right_edge * bpp;
+ effective_width_bytes = (effective_width_bytes + 7) / 8;
+
+ if (effective_width_bytes > p->screen_size) {
+ max_y = 0;
+ } else if (p->fix.line_length) {
+ u32 max_lines = (p->screen_size - effective_width_bytes) /
+ p->fix.line_length + 1;
+ if (max_lines < max_y)
+ max_y = max_lines;
+ }
+ }
+
+ /* If image is completely outside bounds, skip it */
+ if (image->dx >= max_x || image->dy >= max_y)
+ return;
+
+ /* Create clipped image - clip to virtual resolution bounds */
+ clipped_image = *image;
+
+ /* Clip width if it extends beyond right edge */
+ if (clipped_image.dx + clipped_image.width > max_x) {
+ if (clipped_image.dx < max_x)
+ clipped_image.width = max_x - clipped_image.dx;
+ else
+ return; /* completely outside */
+ }
+
+ /* Clip height if it extends beyond bottom edge */
+ if (clipped_image.dy + clipped_image.height > max_y) {
+ if (clipped_image.dy < max_y)
+ clipped_image.height = max_y - clipped_image.dy;
+ else
+ return; /* completely outside */
+ }
- fb_address_forward(&dst, image->dy * bits_per_line + image->dx * bpp);
+ fb_address_forward(&dst, clipped_image.dy * bits_per_line + clipped_image.dx * bpp);
- if (image->depth == 1)
- fb_bitmap_imageblit(image, &dst, bits_per_line, palette, bpp, reverse);
+ if (clipped_image.depth == 1)
+ fb_bitmap_imageblit(&clipped_image, &dst, bits_per_line, palette, bpp, reverse);
else
- fb_color_imageblit(image, &dst, bits_per_line, palette, bpp, reverse);
+ fb_color_imageblit(&clipped_image, &dst, bits_per_line, palette, bpp, reverse);
}
--
2.34.1
Hi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on drm-misc/drm-misc-next]
[also build test WARNING on linus/master v6.18-rc7 next-20251126]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/ssrane_b23-ee-vjti-ac-in/fbdev-core-Fix-vmalloc-out-of-bounds-in-fb_imageblit/20251119-215054
base: git://anongit.freedesktop.org/drm/drm-misc drm-misc-next
patch link: https://lore.kernel.org/r/20251119133821.89998-1-ssranevjti%40gmail.com
patch subject: [PATCH] fbdev: core: Fix vmalloc-out-of-bounds in fb_imageblit
config: x86_64-rhel-9.4-ltp (https://download.01.org/0day-ci/archive/20251126/202511260749.KJgv3MyF-lkp@intel.com/config)
compiler: gcc-14 (Debian 14.2.0-19) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251126/202511260749.KJgv3MyF-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202511260749.KJgv3MyF-lkp@intel.com/
All warnings (new ones prefixed by >>):
In file included from drivers/video/fbdev/core/cfbimgblt.c:17:
drivers/video/fbdev/core/fb_imageblit.h: In function 'fb_imageblit':
>> drivers/video/fbdev/core/fb_imageblit.h:490:23: warning: unused variable 'max_offset_bytes' [-Wunused-variable]
490 | unsigned long max_offset_bytes;
| ^~~~~~~~~~~~~~~~
vim +/max_offset_bytes +490 drivers/video/fbdev/core/fb_imageblit.h
480
481 static inline void fb_imageblit(struct fb_info *p, const struct fb_image *image)
482 {
483 int bpp = p->var.bits_per_pixel;
484 unsigned int bits_per_line = BYTES_TO_BITS(p->fix.line_length);
485 struct fb_address dst = fb_address_init(p);
486 struct fb_reverse reverse = fb_reverse_init(p);
487 const u32 *palette = fb_palette(p);
488 struct fb_image clipped_image;
489 u32 max_x, max_y;
> 490 unsigned long max_offset_bytes;
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi,
kernel test robot noticed the following build warnings:
[auto build test WARNING on drm-misc/drm-misc-next]
[also build test WARNING on drm-tip/drm-tip linus/master v6.18-rc6 next-20251119]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/ssrane_b23-ee-vjti-ac-in/fbdev-core-Fix-vmalloc-out-of-bounds-in-fb_imageblit/20251119-215054
base: git://anongit.freedesktop.org/drm/drm-misc drm-misc-next
patch link: https://lore.kernel.org/r/20251119133821.89998-1-ssranevjti%40gmail.com
patch subject: [PATCH] fbdev: core: Fix vmalloc-out-of-bounds in fb_imageblit
config: nios2-randconfig-r073-20251120 (https://download.01.org/0day-ci/archive/20251120/202511201752.4fVbQwPc-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 10.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251120/202511201752.4fVbQwPc-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202511201752.4fVbQwPc-lkp@intel.com/
All warnings (new ones prefixed by >>):
In file included from drivers/video/fbdev/core/cfbimgblt.c:17:
drivers/video/fbdev/core/fb_imageblit.h: In function 'fb_imageblit':
>> drivers/video/fbdev/core/fb_imageblit.h:490:16: warning: unused variable 'max_offset_bytes' [-Wunused-variable]
490 | unsigned long max_offset_bytes;
| ^~~~~~~~~~~~~~~~
vim +/max_offset_bytes +490 drivers/video/fbdev/core/fb_imageblit.h
480
481 static inline void fb_imageblit(struct fb_info *p, const struct fb_image *image)
482 {
483 int bpp = p->var.bits_per_pixel;
484 unsigned int bits_per_line = BYTES_TO_BITS(p->fix.line_length);
485 struct fb_address dst = fb_address_init(p);
486 struct fb_reverse reverse = fb_reverse_init(p);
487 const u32 *palette = fb_palette(p);
488 struct fb_image clipped_image;
489 u32 max_x, max_y;
> 490 unsigned long max_offset_bytes;
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hello! > This patch replaces the insufficient check with a more precise one. It > calculates the effective width in bytes of the image (accounting for > clipping against xres_virtual) and ensures that the last byte of the > operation falls within the screen buffer. Specifically, it checks if > '(dy + height - 1) * line_length + effective_width_bytes' exceeds > screen_size. If it does, the drawing height max_y is reduced to > prevent the out-of-bounds access. I know my opinion doesn't count much but would like make a note. Any bound checks which are applied here or at the entry of the other 2 low level drawing routines are just masking an issue somewhere in the console code. The text area should be entirely within bounds of the screen memory. If that's always the case then there shouldn't be any drawing request outside of the framebuffer either. Please consider at least to add a warning instead of silent clipping, as every time such clipping was done it was a result of a bug. -- -soci-
© 2016 - 2025 Red Hat, Inc.