Commit ae25885b authored by Thomas Zimmermann's avatar Thomas Zimmermann

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: default avatarNuno Gonçalves <nunojpg@gmail.com>
Signed-off-by: default avatarThomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: default avatarJavier Martinez Canillas <javierm@redhat.com>
Tested-by: default avatarNuno Gonçalves <nunojpg@gmail.com>
Fixes: 67b723f5 ("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
parent d1737806
...@@ -681,7 +681,11 @@ static void drm_fb_helper_damage(struct fb_info *info, u32 x, u32 y, ...@@ -681,7 +681,11 @@ static void drm_fb_helper_damage(struct fb_info *info, u32 x, u32 y,
schedule_work(&helper->damage_work); schedule_work(&helper->damage_work);
} }
/* Convert memory region into area of scanlines and pixels per scanline */ /*
* Convert memory region into area of scanlines and pixels per
* scanline. The parameters off and len must not reach beyond
* the end of the framebuffer.
*/
static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off, size_t len, static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off, size_t len,
struct drm_rect *clip) struct drm_rect *clip)
{ {
...@@ -716,22 +720,29 @@ static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off, ...@@ -716,22 +720,29 @@ static void drm_fb_helper_memory_range_to_clip(struct fb_info *info, off_t off,
*/ */
void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist) void drm_fb_helper_deferred_io(struct fb_info *info, struct list_head *pagereflist)
{ {
unsigned long start, end, min, max; unsigned long start, end, min_off, max_off;
struct fb_deferred_io_pageref *pageref; struct fb_deferred_io_pageref *pageref;
struct drm_rect damage_area; struct drm_rect damage_area;
min = ULONG_MAX; min_off = ULONG_MAX;
max = 0; max_off = 0;
list_for_each_entry(pageref, pagereflist, list) { list_for_each_entry(pageref, pagereflist, list) {
start = pageref->offset; start = pageref->offset;
end = start + PAGE_SIZE; end = start + PAGE_SIZE;
min = min(min, start); min_off = min(min_off, start);
max = max(max, end); max_off = max(max_off, end);
} }
if (min >= max) if (min_off >= max_off)
return; return;
drm_fb_helper_memory_range_to_clip(info, min, max - min, &damage_area); /*
* As we can only track pages, we might reach beyond the end
* of the screen and account for non-existing scanlines. Hence,
* keep the covered memory area within the screen buffer.
*/
max_off = min(max_off, info->screen_size);
drm_fb_helper_memory_range_to_clip(info, min_off, max_off - min_off, &damage_area);
drm_fb_helper_damage(info, damage_area.x1, damage_area.y1, drm_fb_helper_damage(info, damage_area.x1, damage_area.y1,
drm_rect_width(&damage_area), drm_rect_width(&damage_area),
drm_rect_height(&damage_area)); drm_rect_height(&damage_area));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment