Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/fb-helper: Fix out-of-bounds access

Clip memory range to screen-buffer size to avoid out-of-bounds access
in fbdev deferred I/O's damage handling.

Fbdev's deferred I/O can only track pages. From the range of pages, the
damage handler computes the clipping rectangle for the display update.
If the fbdev screen buffer ends near the beginning of a page, that page
could contain more scanlines. The damage handler would then track these
non-existing scanlines as dirty and provoke an out-of-bounds access
during the screen update. Hence, clip the maximum memory range to the
size of the screen buffer.

While at it, rename the variables min/max to min_off/max_off in
drm_fb_helper_deferred_io(). This avoids confusion with the macros of
the same name.

Reported-by: Nuno Gonçalves <nunojpg@gmail.com>
Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Tested-by: Nuno Gonçalves <nunojpg@gmail.com>
Fixes: 67b723f5b742 ("drm/fb-helper: Calculate damaged area in separate helper")
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Javier Martinez Canillas <javierm@redhat.com>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: <stable@vger.kernel.org> # v5.18+
Link: https://patchwork.freedesktop.org/patch/msgid/20220621104617.8817-1-tzimmermann@suse.de

+19 -8
+19 -8
drivers/gpu/drm/drm_fb_helper.c
··· 681 681 schedule_work(&helper->damage_work); 682 682 } 683 683 684 - /* Convert memory region into area of scanlines and pixels per scanline */ 684 + /* 685 + * Convert memory region into area of scanlines and pixels per 686 + * scanline. The parameters off and len must not reach beyond 687 + * the end of the framebuffer. 688 + */ 685 689 static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off, size_t len, 686 690 struct drm_rect *clip) 687 691 { ··· 720 716 */ 721 717 void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist) 722 718 { 723 - unsigned long start, end, min, max; 719 + unsigned long start, end, min_off, max_off; 724 720 struct fb_deferred_io_pageref *pageref; 725 721 struct drm_rect damage_area; 726 722 727 - min = ULONG_MAX; 728 - max = 0; 723 + min_off = ULONG_MAX; 724 + max_off = 0; 729 725 list_for_each_entry(pageref, pagereflist, list) { 730 726 start = pageref->offset; 731 727 end = start + PAGE_SIZE; 732 - min = min(min, start); 733 - max = max(max, end); 728 + min_off = min(min_off, start); 729 + max_off = max(max_off, end); 734 730 } 735 - if (min >= max) 731 + if (min_off >= max_off) 736 732 return; 737 733 738 - drm_fb_helper_memory_range_to_clip(info, min, max - min, &damage_area); 734 + /* 735 + * As we can only track pages, we might reach beyond the end 736 + * of the screen and account for non-existing scanlines. Hence, 737 + * keep the covered memory area within the screen buffer. 738 + */ 739 + max_off = min(max_off, info->screen_size); 740 + 741 + drm_fb_helper_memory_range_to_clip(info, min_off, max_off - min_off, &damage_area); 739 742 drm_fb_helper_damage(info, damage_area.x1, damage_area.y1, 740 743 drm_rect_width(&damage_area), 741 744 drm_rect_height(&damage_area));