Make sure framebuffer dimensions and tiling
parameters will not result in accesses beyond the
end of the GEM buffer they are bound to.

Signed-off-by: James Jones <jajo...@nvidia.com>
---
 drivers/gpu/drm/nouveau/nouveau_display.c | 93 +++++++++++++++++++++++
 1 file changed, 93 insertions(+)

diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c 
b/drivers/gpu/drm/nouveau/nouveau_display.c
index 6f038511a03a..f1509392d7b7 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -224,6 +224,72 @@ static const struct drm_framebuffer_funcs 
nouveau_framebuffer_funcs = {
        .create_handle = nouveau_user_framebuffer_create_handle,
 };
 
+static inline uint32_t
+nouveau_get_width_in_blocks(uint32_t stride)
+{
+       /* GOBs per block in the x direction is always one, and GOBs are
+        * 64 bytes wide
+        */
+       static const uint32_t log_block_width = 6;
+
+       return (stride + (1 << log_block_width) - 1) >> log_block_width;
+}
+
+static inline uint32_t
+nouveau_get_height_in_blocks(struct nouveau_drm *drm,
+                            uint32_t height,
+                            uint32_t log_block_height_in_gobs)
+{
+       uint32_t log_gob_height;
+       uint32_t log_block_height;
+
+       BUG_ON(drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA);
+
+       if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI)
+               log_gob_height = 2;
+       else
+               log_gob_height = 3;
+
+       log_block_height = log_block_height_in_gobs + log_gob_height;
+
+       return (height + (1 << log_block_height) - 1) >> log_block_height;
+}
+
+static int
+nouveau_check_bl_size(struct nouveau_drm *drm, struct nouveau_bo *nvbo,
+                     uint32_t offset, uint32_t stride, uint32_t h,
+                     uint32_t tile_mode)
+{
+       uint32_t gob_size, bw, bh;
+       uint64_t bl_size;
+
+       BUG_ON(drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA);
+
+       if (drm->client.device.info.chipset >= 0xc0)
+               tile_mode >>= 4;
+
+       BUG_ON(tile_mode & 0xFFFFFFF0);
+
+       if (drm->client.device.info.family < NV_DEVICE_INFO_V0_FERMI)
+               gob_size = 256;
+       else
+               gob_size = 512;
+
+       bw = nouveau_get_width_in_blocks(stride);
+       bh = nouveau_get_height_in_blocks(drm, h, tile_mode);
+
+       bl_size = bw * bh * (1 << tile_mode) * gob_size;
+
+       DRM_DEBUG_KMS("offset=%u stride=%u h=%u tile_mode=0x%02x bw=%u bh=%u 
gob_size=%u bl_size=%llu size=%lu\n",
+                     offset, stride, h, tile_mode, bw, bh, gob_size, bl_size,
+                     nvbo->bo.mem.size);
+
+       if (bl_size + offset > nvbo->bo.mem.size)
+               return -ERANGE;
+
+       return 0;
+}
+
 int
 nouveau_framebuffer_new(struct drm_device *dev,
                        const struct drm_mode_fb_cmd2 *mode_cmd,
@@ -232,6 +298,8 @@ nouveau_framebuffer_new(struct drm_device *dev,
 {
        struct nouveau_drm *drm = nouveau_drm(dev);
        struct nouveau_framebuffer *fb;
+       const struct drm_format_info *info;
+       unsigned int width, height, i;
        int ret;
 
         /* YUV overlays have special requirements pre-NV50 */
@@ -254,6 +322,31 @@ nouveau_framebuffer_new(struct drm_device *dev,
                return -EINVAL;
        }
 
+       info = drm_get_format_info(dev, mode_cmd);
+
+       for (i = 0; i < info->num_planes; i++) {
+               width = drm_format_info_plane_width(info,
+                                                   mode_cmd->width,
+                                                   i);
+               height = drm_format_info_plane_height(info,
+                                                     mode_cmd->height,
+                                                     i);
+
+               if (nvbo->kind) {
+                       ret = nouveau_check_bl_size(drm, nvbo,
+                                                   mode_cmd->offsets[i],
+                                                   mode_cmd->pitches[i],
+                                                   height, nvbo->mode);
+                       if (ret)
+                               return ret;
+               } else {
+                       uint32_t size = mode_cmd->pitches[i] * height;
+
+                       if (size + mode_cmd->offsets[i] > nvbo->bo.mem.size)
+                               return -ERANGE;
+               }
+       }
+
        if (!(fb = *pfb = kzalloc(sizeof(*fb), GFP_KERNEL)))
                return -ENOMEM;
 
-- 
2.17.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to