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 <nuno...@gmail.com>
Signed-off-by: Thomas Zimmermann <tzimmerm...@suse.de>
Tested-by: Nuno Gonçalves <nuno...@gmail.com>
Fixes: 67b723f5b742 ("drm/fb-helper: Calculate damaged area in separate helper")
Cc: Thomas Zimmermann <tzimmerm...@suse.de>
Cc: Javier Martinez Canillas <javi...@redhat.com>
Cc: Maarten Lankhorst <maarten.lankho...@linux.intel.com>
Cc: Maxime Ripard <mrip...@kernel.org>
Cc: <sta...@vger.kernel.org> # v5.18+
---
 drivers/gpu/drm/drm_fb_helper.c | 27 +++++++++++++++++++--------
 1 file changed, 19 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 5ad2b6a2778c..1705e8d345ab 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -680,7 +680,11 @@ static void drm_fb_helper_damage(struct fb_info *info, u32 
x, u32 y,
        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,
                                               struct drm_rect *clip)
 {
@@ -715,22 +719,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)
 {
-       unsigned long start, end, min, max;
+       unsigned long start, end, min_off, max_off;
        struct fb_deferred_io_pageref *pageref;
        struct drm_rect damage_area;
 
-       min = ULONG_MAX;
-       max = 0;
+       min_off = ULONG_MAX;
+       max_off = 0;
        list_for_each_entry(pageref, pagereflist, list) {
                start = pageref->offset;
                end = start + PAGE_SIZE;
-               min = min(min, start);
-               max = max(max, end);
+               min_off = min(min_off, start);
+               max_off = max(max_off, end);
        }
-       if (min >= max)
+       if (min_off >= max_off)
                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_rect_width(&damage_area),
                             drm_rect_height(&damage_area));
-- 
2.36.1

Reply via email to