Skip to content

Commit

Permalink
drm/i915: Check for integer truncation on scatterlist creation
Browse files Browse the repository at this point in the history
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 control of userspace, we have to be prudent and catch the
conversion errors.

To catch the implicit truncation as we switch from unsigned long into the
scatterlist's unsigned int, we use overflows_type check and report
E2BIG prior to the operation. 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.

It uses pgoff_t for locals that are dealing with page indices, in this
case, the page count is the limit of the page index.
And it uses safe_conversion() macro which performs a type conversion (cast)
of an integer value into a new variable, checking that the destination is
large enough to hold the source value.

v2: Move added i915_utils's macro into drm_util header (Jani N)

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
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>
Reviewed-by: Nirmoy Das <nirmoy.das@intel.com>
Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org>
  • Loading branch information
ickle authored and intel-lab-lkp committed Jul 14, 2022
1 parent 050c60e commit 1eb4f86
Show file tree
Hide file tree
Showing 8 changed files with 33 additions and 11 deletions.
6 changes: 4 additions & 2 deletions drivers/gpu/drm/i915/gem/i915_gem_internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,13 @@ static int i915_gem_object_get_pages_internal(struct drm_i915_gem_object *obj)
struct sg_table *st;
struct scatterlist *sg;
unsigned int sg_page_sizes;
unsigned int npages;
pgoff_t npages; /* restricted by sg_alloc_table */
int max_order;
gfp_t gfp;

if (!safe_conversion(&npages, obj->base.size >> PAGE_SHIFT))
return -E2BIG;

max_order = MAX_ORDER;
#ifdef CONFIG_SWIOTLB
if (is_swiotlb_active(obj->base.dev->dev)) {
Expand All @@ -67,7 +70,6 @@ static int i915_gem_object_get_pages_internal(struct drm_i915_gem_object *obj)
if (!st)
return -ENOMEM;

npages = obj->base.size / PAGE_SIZE;
if (sg_alloc_table(st, npages, GFP_KERNEL)) {
kfree(st);
return -ENOMEM;
Expand Down
3 changes: 0 additions & 3 deletions drivers/gpu/drm/i915/gem/i915_gem_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ enum intel_region_id;
* this and catch if we ever need to fix it. In the meantime, if you do
* spot such a local variable, please consider fixing!
*
* Aside from our own locals (for which we have no excuse!):
* - sg_table embeds unsigned int for nents
*
* We can check for invalidly typed locals with typecheck(), see for example
* i915_gem_object_get_sg().
*/
Expand Down
4 changes: 4 additions & 0 deletions drivers/gpu/drm/i915/gem/i915_gem_phys.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ static int i915_gem_object_get_pages_phys(struct drm_i915_gem_object *obj)
void *dst;
int i;

/* Contiguous chunk, with a single scatterlist element */
if (overflows_type(obj->base.size, sg->length))
return -E2BIG;

if (GEM_WARN_ON(i915_gem_object_needs_bit17_swizzle(obj)))
return -EINVAL;

Expand Down
5 changes: 4 additions & 1 deletion drivers/gpu/drm/i915/gem/i915_gem_shmem.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,16 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj)
struct drm_i915_private *i915 = to_i915(obj->base.dev);
struct intel_memory_region *mem = obj->mm.region;
struct address_space *mapping = obj->base.filp->f_mapping;
const unsigned long page_count = obj->base.size / PAGE_SIZE;
unsigned int max_segment = i915_sg_segment_size();
struct sg_table *st;
struct sgt_iter sgt_iter;
pgoff_t page_count;
struct page *page;
int ret;

if (!safe_conversion(&page_count, obj->base.size >> PAGE_SHIFT))
return -E2BIG;

/*
* Assert that the object is not currently in any GPU domain. As it
* wasn't in the GTT, there shouldn't be any way it could have been in
Expand Down
4 changes: 4 additions & 0 deletions drivers/gpu/drm/i915/gem/i915_gem_ttm.c
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,10 @@ static int i915_ttm_get_pages(struct drm_i915_gem_object *obj)
{
struct ttm_place requested, busy[I915_TTM_MAX_PLACEMENTS];
struct ttm_placement placement;
pgoff_t num_pages;

if (!safe_conversion(&num_pages, obj->base.size >> PAGE_SHIFT))
return -E2BIG;

GEM_BUG_ON(obj->mm.n_placements > I915_TTM_MAX_PLACEMENTS);

Expand Down
5 changes: 4 additions & 1 deletion drivers/gpu/drm/i915/gem/i915_gem_userptr.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,16 @@ static void i915_gem_object_userptr_drop_ref(struct drm_i915_gem_object *obj)

static int i915_gem_userptr_get_pages(struct drm_i915_gem_object *obj)
{
const unsigned long num_pages = obj->base.size >> PAGE_SHIFT;
unsigned int max_segment = i915_sg_segment_size();
struct sg_table *st;
unsigned int sg_page_sizes;
struct page **pvec;
pgoff_t num_pages; /* limited by sg_alloc_table_from_pages_segment */
int ret;

if (!safe_conversion(&num_pages, obj->base.size >> PAGE_SHIFT))
return -E2BIG;

st = kmalloc(sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
Expand Down
9 changes: 5 additions & 4 deletions drivers/gpu/drm/i915/gvt/dmabuf.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@

#define GEN8_DECODE_PTE(pte) (pte & GENMASK_ULL(63, 12))

static int vgpu_gem_get_pages(
struct drm_i915_gem_object *obj)
static int vgpu_gem_get_pages(struct drm_i915_gem_object *obj)
{
struct drm_i915_private *dev_priv = to_i915(obj->base.dev);
struct intel_vgpu *vgpu;
Expand All @@ -52,7 +51,10 @@ static int vgpu_gem_get_pages(
int i, j, ret;
gen8_pte_t __iomem *gtt_entries;
struct intel_vgpu_fb_info *fb_info;
u32 page_num;
pgoff_t page_num;

if (!safe_conversion(&page_num, obj->base.size >> PAGE_SHIFT))
return -E2BIG;

fb_info = (struct intel_vgpu_fb_info *)obj->gvt_info;
if (drm_WARN_ON(&dev_priv->drm, !fb_info))
Expand All @@ -66,7 +68,6 @@ static int vgpu_gem_get_pages(
if (unlikely(!st))
return -ENOMEM;

page_num = obj->base.size >> PAGE_SHIFT;
ret = sg_alloc_table(st, page_num, GFP_KERNEL);
if (ret) {
kfree(st);
Expand Down
8 changes: 8 additions & 0 deletions drivers/gpu/drm/i915/i915_scatterlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -220,4 +220,12 @@ struct i915_refct_sgt *i915_rsgt_from_buddy_resource(struct ttm_resource *res,
u64 region_start,
u32 page_alignment);

/* Wrap scatterlist.h to sanity check for integer truncation */
typedef unsigned int __sg_size_t; /* see linux/scatterlist.h */
#define sg_alloc_table(sgt, nents, gfp) \
overflows_type(nents, __sg_size_t) ? -E2BIG : (sg_alloc_table)(sgt, (__sg_size_t)(nents), gfp)

#define sg_alloc_table_from_pages_segment(sgt, pages, npages, offset, size, max_segment, gfp) \
overflows_type(npages, __sg_size_t) ? -E2BIG : (sg_alloc_table_from_pages_segment)(sgt, pages, (__sg_size_t)(npages), offset, size, max_segment, gfp)

#endif

0 comments on commit 1eb4f86

Please sign in to comment.