Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/i915: Check for integer truncation on scatterlist creation

There is an impedance mismatch between the scatterlist API using unsigned
int and our memory/page accounting in unsigned long. That is we may try
to create a scatterlist for a large object that overflows returning a
small table into which we try to fit very many pages. As the object size
is under the control of userspace, we have to be prudent and catch the
conversion errors.

To catch the implicit truncation we check before calling scattterlist
creation Apis. we use overflows_type check and report E2BIG if the
overflows may raise. When caller does not return errno, use WARN_ON to
report a problem.

This is already used in our create ioctls to indicate if the uABI request
is simply too large for the backing store. Failing that type check,
we have a second check at sg_alloc_table time to make sure the values
we are passing into the scatterlist API are not truncated.

v2: Move added i915_utils's macro into drm_util header (Jani N)
v5: Fix macros to be enclosed in parentheses for complex values
Fix too long line warning
v8: Replace safe_conversion() with check_assign() (Kees)
v14: Remove shadowing macros of scatterlist creation api and fix to
explicitly overflow check where the scatterlist creation APIs are
called. (Jani)
v15: Add missing returning of error code when the WARN_ON() has been
detected. (Jani)

Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Cc: Brian Welty <brian.welty@intel.com>
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Co-developed-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
Reviewed-by: Nirmoy Das <nirmoy.das@intel.com>
Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Reviewed-by: Andrzej Hajda <andrzej.hajda@intel.com>
Acked-by: Jani Nikula <jani.nikula@intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20221228192252.917299-3-gwan-gyeong.mun@intel.com

authored by

Chris Wilson and committed by
Rodrigo Vivi
c3bfba9a f47e6306

+60 -14
+5 -2
drivers/gpu/drm/i915/gem/i915_gem_internal.c
··· 35 35 struct drm_i915_private *i915 = to_i915(obj->base.dev); 36 36 struct sg_table *st; 37 37 struct scatterlist *sg; 38 - unsigned int npages; 38 + unsigned int npages; /* restricted by sg_alloc_table */ 39 39 int max_order = MAX_ORDER; 40 40 unsigned int max_segment; 41 41 gfp_t gfp; 42 42 43 + if (overflows_type(obj->base.size >> PAGE_SHIFT, npages)) 44 + return -E2BIG; 45 + 46 + npages = obj->base.size >> PAGE_SHIFT; 43 47 max_segment = i915_sg_segment_size(i915->drm.dev) >> PAGE_SHIFT; 44 48 max_order = min(max_order, get_order(max_segment)); 45 49 ··· 59 55 if (!st) 60 56 return -ENOMEM; 61 57 62 - npages = obj->base.size / PAGE_SIZE; 63 58 if (sg_alloc_table(st, npages, GFP_KERNEL)) { 64 59 kfree(st); 65 60 return -ENOMEM;
-3
drivers/gpu/drm/i915/gem/i915_gem_object.h
··· 26 26 * this and catch if we ever need to fix it. In the meantime, if you do 27 27 * spot such a local variable, please consider fixing! 28 28 * 29 - * Aside from our own locals (for which we have no excuse!): 30 - * - sg_table embeds unsigned int for nents 31 - * 32 29 * We can check for invalidly typed locals with typecheck(), see for example 33 30 * i915_gem_object_get_sg(). 34 31 */
+4
drivers/gpu/drm/i915/gem/i915_gem_phys.c
··· 28 28 void *dst; 29 29 int i; 30 30 31 + /* Contiguous chunk, with a single scatterlist element */ 32 + if (overflows_type(obj->base.size, sg->length)) 33 + return -E2BIG; 34 + 31 35 if (GEM_WARN_ON(i915_gem_object_needs_bit17_swizzle(obj))) 32 36 return -EINVAL; 33 37
+6 -3
drivers/gpu/drm/i915/gem/i915_gem_shmem.c
··· 60 60 struct address_space *mapping, 61 61 unsigned int max_segment) 62 62 { 63 - const unsigned long page_count = size / PAGE_SIZE; 63 + unsigned int page_count; /* restricted by sg_alloc_table */ 64 64 unsigned long i; 65 65 struct scatterlist *sg; 66 66 struct page *page; ··· 68 68 gfp_t noreclaim; 69 69 int ret; 70 70 71 + if (overflows_type(size / PAGE_SIZE, page_count)) 72 + return -E2BIG; 73 + 74 + page_count = size / PAGE_SIZE; 71 75 /* 72 76 * If there's no chance of allocating enough pages for the whole 73 77 * object, bail early. ··· 197 193 struct drm_i915_private *i915 = to_i915(obj->base.dev); 198 194 struct intel_memory_region *mem = obj->mm.region; 199 195 struct address_space *mapping = obj->base.filp->f_mapping; 200 - const unsigned long page_count = obj->base.size / PAGE_SIZE; 201 196 unsigned int max_segment = i915_sg_segment_size(i915->drm.dev); 202 197 struct sg_table *st; 203 198 struct sgt_iter sgt_iter; ··· 239 236 } else { 240 237 dev_warn(i915->drm.dev, 241 238 "Failed to DMA remap %lu pages\n", 242 - page_count); 239 + obj->base.size >> PAGE_SHIFT); 243 240 goto err_pages; 244 241 } 245 242 }
+4
drivers/gpu/drm/i915/gem/i915_gem_ttm.c
··· 832 832 struct ttm_place requested, busy[I915_TTM_MAX_PLACEMENTS]; 833 833 struct ttm_placement placement; 834 834 835 + /* restricted by sg_alloc_table */ 836 + if (overflows_type(obj->base.size >> PAGE_SHIFT, unsigned int)) 837 + return -E2BIG; 838 + 835 839 GEM_BUG_ON(obj->mm.n_placements > I915_TTM_MAX_PLACEMENTS); 836 840 837 841 /* Move to the requested placement. */
+5 -1
drivers/gpu/drm/i915/gem/i915_gem_userptr.c
··· 128 128 129 129 static int i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj) 130 130 { 131 - const unsigned long num_pages = obj->base.size >> PAGE_SHIFT; 132 131 unsigned int max_segment = i915_sg_segment_size(obj->base.dev->dev); 133 132 struct sg_table *st; 134 133 struct page **pvec; 134 + unsigned int num_pages; /* limited by sg_alloc_table_from_pages_segment */ 135 135 int ret; 136 136 137 + if (overflows_type(obj->base.size >> PAGE_SHIFT, num_pages)) 138 + return -E2BIG; 139 + 140 + num_pages = obj->base.size >> PAGE_SHIFT; 137 141 st = kmalloc(sizeof(*st), GFP_KERNEL); 138 142 if (!st) 139 143 return -ENOMEM;
+5 -1
drivers/gpu/drm/i915/gem/selftests/huge_gem_object.c
··· 29 29 { 30 30 #define GFP (GFP_KERNEL | __GFP_NOWARN | __GFP_RETRY_MAYFAIL) 31 31 const unsigned long nreal = obj->scratch / PAGE_SIZE; 32 - const unsigned long npages = obj->base.size / PAGE_SIZE; 32 + unsigned int npages; /* restricted by sg_alloc_table */ 33 33 struct scatterlist *sg, *src, *end; 34 34 struct sg_table *pages; 35 35 unsigned long n; 36 36 37 + if (overflows_type(obj->base.size / PAGE_SIZE, npages)) 38 + return -E2BIG; 39 + 40 + npages = obj->base.size / PAGE_SIZE; 37 41 pages = kmalloc(sizeof(*pages), GFP); 38 42 if (!pages) 39 43 return -ENOMEM;
+8
drivers/gpu/drm/i915/gem/selftests/huge_pages.c
··· 84 84 unsigned int sg_page_sizes; 85 85 u64 rem; 86 86 87 + /* restricted by sg_alloc_table */ 88 + if (overflows_type(obj->base.size >> PAGE_SHIFT, unsigned int)) 89 + return -E2BIG; 90 + 87 91 st = kmalloc(sizeof(*st), GFP); 88 92 if (!st) 89 93 return -ENOMEM; ··· 215 211 struct sg_table *st; 216 212 struct scatterlist *sg; 217 213 u64 rem; 214 + 215 + /* restricted by sg_alloc_table */ 216 + if (overflows_type(obj->base.size >> PAGE_SHIFT, unsigned int)) 217 + return -E2BIG; 218 218 219 219 st = kmalloc(sizeof(*st), GFP); 220 220 if (!st)
+6 -4
drivers/gpu/drm/i915/gvt/dmabuf.c
··· 42 42 43 43 #define GEN8_DECODE_PTE(pte) (pte & GENMASK_ULL(63, 12)) 44 44 45 - static int vgpu_gem_get_pages( 46 - struct drm_i915_gem_object *obj) 45 + static int vgpu_gem_get_pages(struct drm_i915_gem_object *obj) 47 46 { 48 47 struct drm_i915_private *dev_priv = to_i915(obj->base.dev); 49 48 struct intel_vgpu *vgpu; ··· 51 52 int i, j, ret; 52 53 gen8_pte_t __iomem *gtt_entries; 53 54 struct intel_vgpu_fb_info *fb_info; 54 - u32 page_num; 55 + unsigned int page_num; /* limited by sg_alloc_table */ 55 56 57 + if (overflows_type(obj->base.size >> PAGE_SHIFT, page_num)) 58 + return -E2BIG; 59 + 60 + page_num = obj->base.size >> PAGE_SHIFT; 56 61 fb_info = (struct intel_vgpu_fb_info *)obj->gvt_info; 57 62 if (drm_WARN_ON(&dev_priv->drm, !fb_info)) 58 63 return -ENODEV; ··· 69 66 if (unlikely(!st)) 70 67 return -ENOMEM; 71 68 72 - page_num = obj->base.size >> PAGE_SHIFT; 73 69 ret = sg_alloc_table(st, page_num, GFP_KERNEL); 74 70 if (ret) { 75 71 kfree(st);
+9
drivers/gpu/drm/i915/i915_scatterlist.c
··· 96 96 97 97 i915_refct_sgt_init(rsgt, node->size << PAGE_SHIFT); 98 98 st = &rsgt->table; 99 + /* restricted by sg_alloc_table */ 100 + if (WARN_ON(overflows_type(DIV_ROUND_UP_ULL(node->size, segment_pages), 101 + unsigned int))) 102 + return ERR_PTR(-E2BIG); 103 + 99 104 if (sg_alloc_table(st, DIV_ROUND_UP_ULL(node->size, segment_pages), 100 105 GFP_KERNEL)) { 101 106 i915_refct_sgt_put(rsgt); ··· 182 177 183 178 i915_refct_sgt_init(rsgt, size); 184 179 st = &rsgt->table; 180 + /* restricted by sg_alloc_table */ 181 + if (WARN_ON(overflows_type(PFN_UP(res->size), unsigned int))) 182 + return ERR_PTR(-E2BIG); 183 + 185 184 if (sg_alloc_table(st, PFN_UP(res->size), GFP_KERNEL)) { 186 185 i915_refct_sgt_put(rsgt); 187 186 return ERR_PTR(-ENOMEM);
+4
drivers/gpu/drm/i915/selftests/i915_gem_gtt.c
··· 68 68 return -ENOMEM; 69 69 70 70 rem = round_up(obj->base.size, BIT(31)) >> 31; 71 + /* restricted by sg_alloc_table */ 72 + if (overflows_type(rem, unsigned int)) 73 + return -E2BIG; 74 + 71 75 if (sg_alloc_table(pages, rem, GFP)) { 72 76 kfree(pages); 73 77 return -ENOMEM;
+4
drivers/gpu/drm/i915/selftests/scatterlist.c
··· 220 220 struct scatterlist *sg; 221 221 unsigned long n, pfn; 222 222 223 + /* restricted by sg_alloc_table */ 224 + if (overflows_type(max, unsigned int)) 225 + return -E2BIG; 226 + 223 227 if (sg_alloc_table(&pt->st, max, 224 228 GFP_KERNEL | __GFP_NORETRY | __GFP_NOWARN)) 225 229 return alloc_error;