drm/i915: Make sure fb gtt offsets stay within 32bits
authorVille Syrjälä <ville.syrjala@linux.intel.com>
Tue, 23 Oct 2018 16:02:01 +0000 (19:02 +0300)
committerVille Syrjälä <ville.syrjala@linux.intel.com>
Thu, 29 Nov 2018 20:00:49 +0000 (22:00 +0200)
Let's try to make sure the fb offset computations never hit
an integer overflow by making sure the entire fb stays
below 32bits. framebuffer_check() in the core already does
the same check, but as it doesn't know about tiling some things
can slip through. Repeat the check in the driver with tiling
taken into account.

v2: Use add_overflows() after massaging it to work for me (Chris)
v3: Call it add_overflow_t() to match min_t() & co. (Chris)

Cc: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20181023160201.9840-1-ville.syrjala@linux.intel.com
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
drivers/gpu/drm/i915/i915_utils.h
drivers/gpu/drm/i915/intel_display.c

index 5858a43e19daa908221d36428a8f4a62787ef245..9726df37c4c41b2ce2316a2fd6438363275e654c 100644 (file)
                             __stringify(x), (long)(x))
 
 #if defined(GCC_VERSION) && GCC_VERSION >= 70000
-#define add_overflows(A, B) \
-       __builtin_add_overflow_p((A), (B), (typeof((A) + (B)))0)
+#define add_overflows_t(T, A, B) \
+       __builtin_add_overflow_p((A), (B), (T)0)
 #else
-#define add_overflows(A, B) ({ \
+#define add_overflows_t(T, A, B) ({ \
        typeof(A) a = (A); \
        typeof(B) b = (B); \
-       a + b < a; \
+       (T)(a + b) < a; \
 })
 #endif
 
+#define add_overflows(A, B) \
+       add_overflows_t(typeof((A) + (B)), (A), (B))
+
 #define range_overflows(start, size, max) ({ \
        typeof(start) start__ = (start); \
        typeof(size) size__ = (size); \
index d07fa4456150cebb323b5f7fe4c0b5cbe513e0b9..14cca56f274377e52d6b4cd7508393e3408bf635 100644 (file)
@@ -2341,10 +2341,26 @@ static int intel_fb_offset_to_xy(int *x, int *y,
                                 int color_plane)
 {
        struct drm_i915_private *dev_priv = to_i915(fb->dev);
+       unsigned int height;
 
        if (fb->modifier != DRM_FORMAT_MOD_LINEAR &&
-           fb->offsets[color_plane] % intel_tile_size(dev_priv))
+           fb->offsets[color_plane] % intel_tile_size(dev_priv)) {
+               DRM_DEBUG_KMS("Misaligned offset 0x%08x for color plane %d\n",
+                             fb->offsets[color_plane], color_plane);
                return -EINVAL;
+       }
+
+       height = drm_framebuffer_plane_height(fb->height, fb, color_plane);
+       height = ALIGN(height, intel_tile_height(fb, color_plane));
+
+       /* Catch potential overflows early */
+       if (add_overflows_t(u32, mul_u32_u32(height, fb->pitches[color_plane]),
+                           fb->offsets[color_plane])) {
+               DRM_DEBUG_KMS("Bad offset 0x%08x or pitch %d for color plane %d\n",
+                             fb->offsets[color_plane], fb->pitches[color_plane],
+                             color_plane);
+               return -ERANGE;
+       }
 
        *x = 0;
        *y = 0;