[Nouveau] [PATCH v2 2/3] drm/nouveau: Check framebuffer size against bo
James Jones
jajones at nvidia.com
Mon Jan 6 19:06:07 UTC 2020
On 1/5/20 5:25 PM, Ben Skeggs wrote:
> On Tue, 17 Dec 2019 at 10:45, James Jones <jajones at nvidia.com> wrote:
>>
>> 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 <jajones at 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);
> As far as I can tell, tile_mode can be fed into this function
> unsanitised from userspace, so we probably want something different to
> a BUG_ON() here.
Good catch. I had assumed nouveau_bo::mode was validated at creation
time. I'll get this fixed up.
Thanks,
-James
>> +
>> + 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
>>
>> _______________________________________________
>> Nouveau mailing list
>> Nouveau at lists.freedesktop.org
>> https://lists.freedesktop.org/mailman/listinfo/nouveau
More information about the dri-devel
mailing list