[PATCH v2 3/7] drm/i915: Check for integer truncation on scatterlist creation
Mauro Carvalho Chehab
mauro.chehab at linux.intel.com
Tue Jul 5 14:48:19 UTC 2022
On Tue, 5 Jul 2022 15:24:51 +0300
Gwan-gyeong Mun <gwan-gyeong.mun at intel.com> wrote:
> From: Chris Wilson <chris at chris-wilson.co.uk>
>
> 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 at chris-wilson.co.uk>
> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun at intel.com>
> Cc: Tvrtko Ursulin <tvrtko.ursulin at intel.com>
> Cc: Brian Welty <brian.welty at intel.com>
> Cc: Matthew Auld <matthew.auld at intel.com>
> Cc: Thomas Hellström <thomas.hellstrom at linux.intel.com>
> Reviewed-by: Nirmoy Das <nirmoy.das at intel.com>
Reviewed-by: Mauro Carvalho Chehab <mchehab at kernel.org>
> ---
> drivers/gpu/drm/i915/gem/i915_gem_internal.c | 6 ++++--
> drivers/gpu/drm/i915/gem/i915_gem_object.h | 3 ---
> drivers/gpu/drm/i915/gem/i915_gem_phys.c | 4 ++++
> drivers/gpu/drm/i915/gem/i915_gem_shmem.c | 5 ++++-
> drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 4 ++++
> drivers/gpu/drm/i915/gem/i915_gem_userptr.c | 5 ++++-
> drivers/gpu/drm/i915/gvt/dmabuf.c | 9 +++++----
> drivers/gpu/drm/i915/i915_scatterlist.h | 8 ++++++++
> 8 files changed, 33 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_internal.c b/drivers/gpu/drm/i915/gem/i915_gem_internal.c
> index c698f95af15f..ff2e6e780631 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_internal.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_internal.c
> @@ -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)) {
> @@ -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;
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
> index a60c6f4517d5..31bb09dccf2f 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
> @@ -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().
> */
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_phys.c b/drivers/gpu/drm/i915/gem/i915_gem_phys.c
> index 0d0e46dae559..88ba7266a3a5 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_phys.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_phys.c
> @@ -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;
>
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
> index 4eed3dd90ba8..604e8829e8ea 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
> @@ -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
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> index 50a02d850139..cdcb3ee0c433 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> @@ -815,6 +815,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);
>
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
> index 094f06b4ce33..25785c3a0083 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
> @@ -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;
> diff --git a/drivers/gpu/drm/i915/gvt/dmabuf.c b/drivers/gpu/drm/i915/gvt/dmabuf.c
> index 01e54b45c5c1..795270cb4ec2 100644
> --- a/drivers/gpu/drm/i915/gvt/dmabuf.c
> +++ b/drivers/gpu/drm/i915/gvt/dmabuf.c
> @@ -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;
> @@ -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))
> @@ -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);
> diff --git a/drivers/gpu/drm/i915/i915_scatterlist.h b/drivers/gpu/drm/i915/i915_scatterlist.h
> index 12c6a1684081..c4d4d3c84cff 100644
> --- a/drivers/gpu/drm/i915/i915_scatterlist.h
> +++ b/drivers/gpu/drm/i915/i915_scatterlist.h
> @@ -218,4 +218,12 @@ struct i915_refct_sgt *i915_rsgt_from_mm_node(const struct drm_mm_node *node,
> struct i915_refct_sgt *i915_rsgt_from_buddy_resource(struct ttm_resource *res,
> u64 region_start);
>
> +/* 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
More information about the dri-devel
mailing list