This patch series fixes integer overflow or integer truncation issues in
page lookups, ttm place configuration and scatterlist creation, etc.
We need to check that we avoid integer overflows when looking up a page,
and so fix all the instances where we have mistakenly used a plain integer
instead of a more suitable long.
And 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 as we switch from
unsigned long into the scatterlist's unsigned int, we use our 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.
And ttm place also has the same problem with scatterlist creation,
and we fix the integer truncation problem with the way approached by
scatterlist creation.
And It corrects the error code to return -E2BIG when creating gem objects
using ttm or shmem, if the size is too large in each case.
In order to provide a common macro, it moves and adds a few utility macros
into overflow/util_macros header
v6: Move macro addition location so that it can be used by other than drm subsystem (Jani, Mauro, Andi)
Fix to follow general use case for GEM_BUG_ON(). (Jani)
v5: Fix an alignment to match open parenthesis
Fix macros to be enclosed in parentheses for complex values
Fix too long line warning
v4: Fix build warnins that reported by kernel test robot. (kernel test robot <[email protected]>)
Add kernel-doc markups to the kAPI functions and macros (Mauoro)
v3: Modify overflows_type() macro to consider signed data types and
add is_type_unsigned() macro (Mauro)
Make not use the same macro name on a function. (Mauro)
For kernel-doc, macros and functions are handled in the same namespace,
the same macro name on a function prevents ever adding documentation for it.
Not to change execution inside a macro. (Mauro)
Fix the problem that safe_conversion() macro always returns true (G.G)
Add safe_conversion_gem_bug_on() macro and remove temporal SAFE_CONVERSION() macro. (G.G.)
Chris Wilson (3):
drm/i915/gem: Typecheck page lookups
drm/i915: Check for integer truncation on scatterlist creation
drm/i915: Remove truncation warning for large objects
Gwan-gyeong Mun (5):
overflow: Move and add few utility macros into overflow
util_macros: Add exact_type macro to catch type mis-match while
compiling
drm/i915: Check for integer truncation on the configuration of ttm
place
drm/i915: Check if the size is too big while creating shmem file
drm/i915: Use error code as -E2BIG when the size of gem ttm object is
too large
drivers/gpu/drm/i915/gem/i915_gem_internal.c | 6 +-
drivers/gpu/drm/i915/gem/i915_gem_object.c | 7 +-
drivers/gpu/drm/i915/gem/i915_gem_object.h | 303 +++++++++++++++---
drivers/gpu/drm/i915/gem/i915_gem_pages.c | 27 +-
drivers/gpu/drm/i915/gem/i915_gem_phys.c | 4 +
drivers/gpu/drm/i915/gem/i915_gem_shmem.c | 19 +-
drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 23 +-
drivers/gpu/drm/i915/gem/i915_gem_userptr.c | 5 +-
.../drm/i915/gem/selftests/i915_gem_context.c | 12 +-
.../drm/i915/gem/selftests/i915_gem_mman.c | 8 +-
.../drm/i915/gem/selftests/i915_gem_object.c | 8 +-
drivers/gpu/drm/i915/gvt/dmabuf.c | 9 +-
drivers/gpu/drm/i915/i915_gem.c | 18 +-
drivers/gpu/drm/i915/i915_scatterlist.h | 11 +
drivers/gpu/drm/i915/i915_utils.h | 6 +-
drivers/gpu/drm/i915/i915_vma.c | 8 +-
drivers/gpu/drm/i915/intel_region_ttm.c | 22 +-
include/linux/overflow.h | 54 ++++
include/linux/util_macros.h | 25 ++
19 files changed, 482 insertions(+), 93 deletions(-)
--
2.34.1
The __shmem_file_setup() function returns -EINVAL if size is greater than
MAX_LFS_FILESIZE. To handle the same error as other code that returns
-E2BIG when the size is too large, it add a code that returns -E2BIG when
the size is larger than the size that can be handled.
v4: If BITS_PER_LONG is 32, size > MAX_LFS_FILESIZE is always false, so it
checks only when BITS_PER_LONG is 64.
Signed-off-by: Gwan-gyeong Mun <[email protected]>
Cc: Chris Wilson <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Thomas Hellström <[email protected]>
Reviewed-by: Nirmoy Das <[email protected]>
Reviewed-by: Mauro Carvalho Chehab <[email protected]>
Reported-by: kernel test robot <[email protected]>
Reviewed-by: Andrzej Hajda <[email protected]>
---
drivers/gpu/drm/i915/gem/i915_gem_shmem.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
index 4cb35808e431..4a7a6d65fc7a 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
@@ -541,6 +541,20 @@ static int __create_shmem(struct drm_i915_private *i915,
drm_gem_private_object_init(&i915->drm, obj, size);
+ /* XXX: The __shmem_file_setup() function returns -EINVAL if size is
+ * greater than MAX_LFS_FILESIZE.
+ * To handle the same error as other code that returns -E2BIG when
+ * the size is too large, we add a code that returns -E2BIG when the
+ * size is larger than the size that can be handled.
+ * If BITS_PER_LONG is 32, size > MAX_LFS_FILESIZE is always false,
+ * so we only needs to check when BITS_PER_LONG is 64.
+ * If BITS_PER_LONG is 32, E2BIG checks are processed when
+ * i915_gem_object_size_2big() is called before init_object() callback
+ * is called.
+ */
+ if (BITS_PER_LONG == 64 && size > MAX_LFS_FILESIZE)
+ return -E2BIG;
+
if (i915->mm.gemfs)
filp = shmem_file_setup_with_mnt(i915->mm.gemfs, "i915", size,
flags);
--
2.34.1
From: Chris Wilson <[email protected]>
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)
v5: Fix macros to be enclosed in parentheses for complex values
Fix too long line warning
Signed-off-by: Chris Wilson <[email protected]>
Signed-off-by: Gwan-gyeong Mun <[email protected]>
Cc: Tvrtko Ursulin <[email protected]>
Cc: Brian Welty <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Thomas Hellström <[email protected]>
Reviewed-by: Nirmoy Das <[email protected]>
Reviewed-by: Mauro Carvalho Chehab <[email protected]>
Reviewed-by: Andrzej Hajda <[email protected]>
---
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 | 11 +++++++++++
8 files changed, 36 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 5da872afc4ba..0cf31adbfd41 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 f42ca1179f37..4cb35808e431 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 52c4c1fac7f9..9f2be1892b6c 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
@@ -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);
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 9ddb3e743a3e..1d1802beb42b 100644
--- a/drivers/gpu/drm/i915/i915_scatterlist.h
+++ b/drivers/gpu/drm/i915/i915_scatterlist.h
@@ -220,4 +220,15 @@ 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
--
2.34.1
The ttm_bo_init_reserved() functions returns -ENOSPC if the size is too big
to add vma. The direct function that returns -ENOSPC is drm_mm_insert_node_in_range().
To handle the same error as other code returning -E2BIG when the size is
too large, it converts return value to -E2BIG.
Signed-off-by: Gwan-gyeong Mun <[email protected]>
Cc: Chris Wilson <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Thomas Hellström <[email protected]>
Reviewed-by: Nirmoy Das <[email protected]>
Reviewed-by: Mauro Carvalho Chehab <[email protected]>
Reviewed-by: Andrzej Hajda <[email protected]>
---
drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
index 30f488712abe..88d1bfd11f1d 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
@@ -1249,6 +1249,17 @@ int __i915_gem_ttm_object_init(struct intel_memory_region *mem,
ret = ttm_bo_init_reserved(&i915->bdev, i915_gem_to_ttm(obj), bo_type,
&i915_sys_placement, page_size >> PAGE_SHIFT,
&ctx, NULL, NULL, i915_ttm_bo_destroy);
+
+ /*
+ * XXX: The ttm_bo_init_reserved() functions returns -ENOSPC if the size
+ * is too big to add vma. The direct function that returns -ENOSPC is
+ * drm_mm_insert_node_in_range(). To handle the same error as other code
+ * that returns -E2BIG when the size is too large, it converts -ENOSPC to
+ * -E2BIG.
+ */
+ if (size >> PAGE_SHIFT > INT_MAX && ret == -ENOSPC)
+ ret = -E2BIG;
+
if (ret)
return i915_ttm_err_to_gem(ret);
--
2.34.1
From: Chris Wilson <[email protected]>
Having addressed the issues surrounding incorrect types for local
variables and potential integer truncation in using the scatterlist API,
we have closed all the loop holes we had previously identified with
dangerously large object creation. As such, we can eliminate the warning
put in place to remind us to complete the review.
Signed-off-by: Chris Wilson <[email protected]>
Signed-off-by: Gwan-gyeong Mun <[email protected]>
Cc: Tvrtko Ursulin <[email protected]>
Cc: Brian Welty <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Thomas Hellström <[email protected]>
Testcase: igt@gem_create@create-massive
Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/4991
Reviewed-by: Nirmoy Das <[email protected]>
Reviewed-by: Mauro Carvalho Chehab <[email protected]>
Reviewed-by: Andrzej Hajda <[email protected]>
---
drivers/gpu/drm/i915/gem/i915_gem_object.h | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
index 0cf31adbfd41..dd2762da332f 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
@@ -20,25 +20,10 @@
enum intel_region_id;
-/*
- * XXX: There is a prevalence of the assumption that we fit the
- * object's page count inside a 32bit _signed_ variable. Let's document
- * this and catch if we ever need to fix it. In the meantime, if you do
- * spot such a local variable, please consider fixing!
- *
- * We can check for invalidly typed locals with typecheck(), see for example
- * i915_gem_object_get_sg().
- */
-#define GEM_CHECK_SIZE_OVERFLOW(sz) \
- GEM_WARN_ON((sz) >> PAGE_SHIFT > INT_MAX)
-
static inline bool i915_gem_object_size_2big(u64 size)
{
struct drm_i915_gem_object *obj;
- if (GEM_CHECK_SIZE_OVERFLOW(size))
- return true;
-
if (overflows_type(size, obj->base.size))
return true;
--
2.34.1
There is an impedance mismatch between the first/last valid page
frame number of ttm place in unsigned and our memory/page accounting in
unsigned long.
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 as we switch from unsigned long to
unsigned, we use overflows_type check and report E2BIG or overflow_type
prior to the operation.
v3: Not to change execution inside a macro. (Mauro)
Add safe_conversion_gem_bug_on() macro and remove temporal
SAFE_CONVERSION() macro.
v4: Fix unhandled GEM_BUG_ON() macro call from safe_conversion_gem_bug_on()
v6: Fix to follow general use case for GEM_BUG_ON(). (Jani)
Signed-off-by: Gwan-gyeong Mun <[email protected]>
Cc: Chris Wilson <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Thomas Hellström <[email protected]>
Cc: Jani Nikula <[email protected]>
Reviewed-by: Nirmoy Das <[email protected]> (v2)
Reviewed-by: Mauro Carvalho Chehab <[email protected]> (v3)
Reported-by: kernel test robot <[email protected]>
Reviewed-by: Andrzej Hajda <[email protected]> (v5)
---
drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 6 +++---
drivers/gpu/drm/i915/intel_region_ttm.c | 22 +++++++++++++++++++---
2 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
index 9f2be1892b6c..30f488712abe 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
@@ -140,14 +140,14 @@ i915_ttm_place_from_region(const struct intel_memory_region *mr,
if (flags & I915_BO_ALLOC_CONTIGUOUS)
place->flags |= TTM_PL_FLAG_CONTIGUOUS;
if (offset != I915_BO_INVALID_OFFSET) {
- place->fpfn = offset >> PAGE_SHIFT;
- place->lpfn = place->fpfn + (size >> PAGE_SHIFT);
+ GEM_BUG_ON(!safe_conversion(&place->fpfn, offset >> PAGE_SHIFT));
+ GEM_BUG_ON(!safe_conversion(&place->lpfn, place->fpfn + (size >> PAGE_SHIFT)));
} else if (mr->io_size && mr->io_size < mr->total) {
if (flags & I915_BO_ALLOC_GPU_ONLY) {
place->flags |= TTM_PL_FLAG_TOPDOWN;
} else {
place->fpfn = 0;
- place->lpfn = mr->io_size >> PAGE_SHIFT;
+ GEM_BUG_ON(!safe_conversion(&place->lpfn, mr->io_size >> PAGE_SHIFT));
}
}
}
diff --git a/drivers/gpu/drm/i915/intel_region_ttm.c b/drivers/gpu/drm/i915/intel_region_ttm.c
index 575d67bc6ffe..c480b0b50bcc 100644
--- a/drivers/gpu/drm/i915/intel_region_ttm.c
+++ b/drivers/gpu/drm/i915/intel_region_ttm.c
@@ -209,14 +209,28 @@ intel_region_ttm_resource_alloc(struct intel_memory_region *mem,
if (flags & I915_BO_ALLOC_CONTIGUOUS)
place.flags |= TTM_PL_FLAG_CONTIGUOUS;
if (offset != I915_BO_INVALID_OFFSET) {
- place.fpfn = offset >> PAGE_SHIFT;
- place.lpfn = place.fpfn + (size >> PAGE_SHIFT);
+ if (!safe_conversion(&place.fpfn, offset >> PAGE_SHIFT)) {
+ GEM_BUG_ON(!safe_conversion(&place.fpfn,offset >> PAGE_SHIFT));
+ ret = -E2BIG;
+ goto out;
+ }
+ if (!safe_conversion(&place.lpfn, place.fpfn + (size >> PAGE_SHIFT))) {
+ GEM_BUG_ON(!safe_conversion(&place.lpfn,
+ place.fpfn + (size >> PAGE_SHIFT)));
+ ret = -E2BIG;
+ goto out;
+ }
} else if (mem->io_size && mem->io_size < mem->total) {
if (flags & I915_BO_ALLOC_GPU_ONLY) {
place.flags |= TTM_PL_FLAG_TOPDOWN;
} else {
place.fpfn = 0;
- place.lpfn = mem->io_size >> PAGE_SHIFT;
+ if (!safe_conversion(&place.lpfn, mem->io_size >> PAGE_SHIFT)) {
+ GEM_BUG_ON(!safe_conversion(&place.lpfn,
+ mem->io_size >> PAGE_SHIFT));
+ ret = -E2BIG;
+ goto out;
+ }
}
}
@@ -224,6 +238,8 @@ intel_region_ttm_resource_alloc(struct intel_memory_region *mem,
mock_bo.bdev = &mem->i915->bdev;
ret = man->func->alloc(man, &mock_bo, &place, &res);
+
+out:
if (ret == -ENOSPC)
ret = -ENXIO;
if (!ret)
--
2.34.1
It moves overflows_type utility macro into overflow header from i915_utils
header. The overflows_type can be used to catch the truncation between data
types. And it adds safe_conversion() macro which performs a type conversion
(cast) of an source value into a new variable, checking that the
destination is large enough to hold the source value. And the functionality
of overflows_type has been improved to handle the signbit.
The is_unsigned_type macro has been added to check the sign bit of the
built-in type.
v3: Add is_type_unsigned() macro (Mauro)
Modify overflows_type() macro to consider signed data types (Mauro)
Fix the problem that safe_conversion() macro always returns true
v4: Fix kernel-doc markups
v6: Move macro addition location so that it can be used by other than drm
subsystem (Jani, Mauro, Andi)
Change is_type_unsigned to is_unsigned_type to have the same name form
as is_signed_type macro
Signed-off-by: Gwan-gyeong Mun <[email protected]>
Cc: Thomas Hellström <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Nirmoy Das <[email protected]>
Cc: Jani Nikula <[email protected]>
Cc: Andi Shyti <[email protected]>
Reviewed-by: Mauro Carvalho Chehab <[email protected]> (v5)
---
drivers/gpu/drm/i915/i915_utils.h | 5 +--
include/linux/overflow.h | 54 +++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h
index c10d68cdc3ca..eb0ded23fa9c 100644
--- a/drivers/gpu/drm/i915/i915_utils.h
+++ b/drivers/gpu/drm/i915/i915_utils.h
@@ -32,6 +32,7 @@
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/sched/clock.h>
+#include <linux/overflow.h>
#ifdef CONFIG_X86
#include <asm/hypervisor.h>
@@ -111,10 +112,6 @@ bool i915_error_injected(void);
#define range_overflows_end_t(type, start, size, max) \
range_overflows_end((type)(start), (type)(size), (type)(max))
-/* Note we don't consider signbits :| */
-#define overflows_type(x, T) \
- (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T))
-
#define ptr_mask_bits(ptr, n) ({ \
unsigned long __v = (unsigned long)(ptr); \
(typeof(ptr))(__v & -BIT(n)); \
diff --git a/include/linux/overflow.h b/include/linux/overflow.h
index f1221d11f8e5..462a03454377 100644
--- a/include/linux/overflow.h
+++ b/include/linux/overflow.h
@@ -35,6 +35,60 @@
#define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
#define type_min(T) ((T)((T)-type_max(T)-(T)1))
+/**
+ * is_unsigned_type - helper for checking data type which is an unsigned data
+ * type or not
+ * @x: The data type to check
+ *
+ * Returns:
+ * True if the data type is an unsigned data type, false otherwise.
+ */
+#define is_unsigned_type(x) ((typeof(x))-1 >= (typeof(x))0)
+
+/**
+ * overflows_type - helper for checking the truncation between data types
+ * @x: Source for overflow type comparison
+ * @T: Destination for overflow type comparison
+ *
+ * It compares the values and size of each data type between the first and
+ * second argument to check whether truncation can occur when assigning the
+ * first argument to the variable of the second argument.
+ * Source and Destination can be used with or without sign bit.
+ * Composite data structures such as union and structure are not considered.
+ * Enum data types are not considered.
+ * Floating point data types are not considered.
+ *
+ * Returns:
+ * True if truncation can occur, false otherwise.
+ */
+#define overflows_type(x, T) \
+ (is_unsigned_type(x) ? \
+ is_unsigned_type(T) ? \
+ (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
+ : (sizeof(x) >= sizeof(T) && (x) >> (BITS_PER_TYPE(T) - 1)) ? 1 : 0 \
+ : is_unsigned_type(T) ? \
+ ((x) < 0) ? 1 : (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
+ : (sizeof(x) > sizeof(T)) ? \
+ ((x) < 0) ? (((x) * -1) >> BITS_PER_TYPE(T)) ? 1 : 0 \
+ : ((x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
+ : 0)
+
+/**
+ * safe_conversion - perform a type conversion (cast) of an source value into
+ * a new variable, checking that the destination is large enough to hold the
+ * source value.
+ * @ptr: Destination pointer address
+ * @value: Source value
+ *
+ * Returns:
+ * If the value would overflow the destination, it returns false.
+ */
+#define safe_conversion(ptr, value) ({ \
+ typeof(value) __v = (value); \
+ typeof(ptr) __ptr = (ptr); \
+ overflows_type(__v, *__ptr) ? 0 : ((*__ptr = (typeof(*__ptr))__v), 1); \
+})
+
/*
* Avoids triggering -Wtype-limits compilation warning,
* while using unsigned data types to check a < 0.
--
2.34.1
On Sat, 13 Aug 2022, Gwan-gyeong Mun <[email protected]> wrote:
> There is an impedance mismatch between the first/last valid page
> frame number of ttm place in unsigned and our memory/page accounting in
> unsigned long.
> 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 as we switch from unsigned long to
> unsigned, we use overflows_type check and report E2BIG or overflow_type
> prior to the operation.
>
> v3: Not to change execution inside a macro. (Mauro)
> Add safe_conversion_gem_bug_on() macro and remove temporal
> SAFE_CONVERSION() macro.
> v4: Fix unhandled GEM_BUG_ON() macro call from safe_conversion_gem_bug_on()
> v6: Fix to follow general use case for GEM_BUG_ON(). (Jani)
>
> Signed-off-by: Gwan-gyeong Mun <[email protected]>
> Cc: Chris Wilson <[email protected]>
> Cc: Matthew Auld <[email protected]>
> Cc: Thomas Hellström <[email protected]>
> Cc: Jani Nikula <[email protected]>
> Reviewed-by: Nirmoy Das <[email protected]> (v2)
> Reviewed-by: Mauro Carvalho Chehab <[email protected]> (v3)
> Reported-by: kernel test robot <[email protected]>
> Reviewed-by: Andrzej Hajda <[email protected]> (v5)
> ---
> drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 6 +++---
> drivers/gpu/drm/i915/intel_region_ttm.c | 22 +++++++++++++++++++---
> 2 files changed, 22 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> index 9f2be1892b6c..30f488712abe 100644
> --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
> @@ -140,14 +140,14 @@ i915_ttm_place_from_region(const struct intel_memory_region *mr,
> if (flags & I915_BO_ALLOC_CONTIGUOUS)
> place->flags |= TTM_PL_FLAG_CONTIGUOUS;
> if (offset != I915_BO_INVALID_OFFSET) {
> - place->fpfn = offset >> PAGE_SHIFT;
> - place->lpfn = place->fpfn + (size >> PAGE_SHIFT);
> + GEM_BUG_ON(!safe_conversion(&place->fpfn, offset >> PAGE_SHIFT));
> + GEM_BUG_ON(!safe_conversion(&place->lpfn, place->fpfn + (size >> PAGE_SHIFT)));
This would be the natural thing to do with BUG_ON/WARN_ON. And I'd like
it if we could use it like this. But, as I tried to say, GEM_BUG_ON is
nothing like BUG_ON/WARN_ON, and no code is generated for
CONFIG_DRM_I915_DEBUG_GEM=n. And our CI will never catch it because it
always has CONFIG_DRM_I915_DEBUG_GEM=y.
BR,
Jani.
> } else if (mr->io_size && mr->io_size < mr->total) {
> if (flags & I915_BO_ALLOC_GPU_ONLY) {
> place->flags |= TTM_PL_FLAG_TOPDOWN;
> } else {
> place->fpfn = 0;
> - place->lpfn = mr->io_size >> PAGE_SHIFT;
> + GEM_BUG_ON(!safe_conversion(&place->lpfn, mr->io_size >> PAGE_SHIFT));
> }
> }
> }
> diff --git a/drivers/gpu/drm/i915/intel_region_ttm.c b/drivers/gpu/drm/i915/intel_region_ttm.c
> index 575d67bc6ffe..c480b0b50bcc 100644
> --- a/drivers/gpu/drm/i915/intel_region_ttm.c
> +++ b/drivers/gpu/drm/i915/intel_region_ttm.c
> @@ -209,14 +209,28 @@ intel_region_ttm_resource_alloc(struct intel_memory_region *mem,
> if (flags & I915_BO_ALLOC_CONTIGUOUS)
> place.flags |= TTM_PL_FLAG_CONTIGUOUS;
> if (offset != I915_BO_INVALID_OFFSET) {
> - place.fpfn = offset >> PAGE_SHIFT;
> - place.lpfn = place.fpfn + (size >> PAGE_SHIFT);
> + if (!safe_conversion(&place.fpfn, offset >> PAGE_SHIFT)) {
> + GEM_BUG_ON(!safe_conversion(&place.fpfn,offset >> PAGE_SHIFT));
> + ret = -E2BIG;
> + goto out;
> + }
> + if (!safe_conversion(&place.lpfn, place.fpfn + (size >> PAGE_SHIFT))) {
> + GEM_BUG_ON(!safe_conversion(&place.lpfn,
> + place.fpfn + (size >> PAGE_SHIFT)));
> + ret = -E2BIG;
> + goto out;
> + }
> } else if (mem->io_size && mem->io_size < mem->total) {
> if (flags & I915_BO_ALLOC_GPU_ONLY) {
> place.flags |= TTM_PL_FLAG_TOPDOWN;
> } else {
> place.fpfn = 0;
> - place.lpfn = mem->io_size >> PAGE_SHIFT;
> + if (!safe_conversion(&place.lpfn, mem->io_size >> PAGE_SHIFT)) {
> + GEM_BUG_ON(!safe_conversion(&place.lpfn,
> + mem->io_size >> PAGE_SHIFT));
> + ret = -E2BIG;
> + goto out;
> + }
> }
> }
>
> @@ -224,6 +238,8 @@ intel_region_ttm_resource_alloc(struct intel_memory_region *mem,
> mock_bo.bdev = &mem->i915->bdev;
>
> ret = man->func->alloc(man, &mock_bo, &place, &res);
> +
> +out:
> if (ret == -ENOSPC)
> ret = -ENXIO;
> if (!ret)
--
Jani Nikula, Intel Open Source Graphics Center
On 8/15/22 5:03 PM, Jani Nikula wrote:
> On Sat, 13 Aug 2022, Gwan-gyeong Mun <[email protected]> wrote:
>> There is an impedance mismatch between the first/last valid page
>> frame number of ttm place in unsigned and our memory/page accounting in
>> unsigned long.
>> 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 as we switch from unsigned long to
>> unsigned, we use overflows_type check and report E2BIG or overflow_type
>> prior to the operation.
>>
>> v3: Not to change execution inside a macro. (Mauro)
>> Add safe_conversion_gem_bug_on() macro and remove temporal
>> SAFE_CONVERSION() macro.
>> v4: Fix unhandled GEM_BUG_ON() macro call from safe_conversion_gem_bug_on()
>> v6: Fix to follow general use case for GEM_BUG_ON(). (Jani)
>>
>> Signed-off-by: Gwan-gyeong Mun <[email protected]>
>> Cc: Chris Wilson <[email protected]>
>> Cc: Matthew Auld <[email protected]>
>> Cc: Thomas Hellström <[email protected]>
>> Cc: Jani Nikula <[email protected]>
>> Reviewed-by: Nirmoy Das <[email protected]> (v2)
>> Reviewed-by: Mauro Carvalho Chehab <[email protected]> (v3)
>> Reported-by: kernel test robot <[email protected]>
>> Reviewed-by: Andrzej Hajda <[email protected]> (v5)
>> ---
>> drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 6 +++---
>> drivers/gpu/drm/i915/intel_region_ttm.c | 22 +++++++++++++++++++---
>> 2 files changed, 22 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>> index 9f2be1892b6c..30f488712abe 100644
>> --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>> +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
>> @@ -140,14 +140,14 @@ i915_ttm_place_from_region(const struct intel_memory_region *mr,
>> if (flags & I915_BO_ALLOC_CONTIGUOUS)
>> place->flags |= TTM_PL_FLAG_CONTIGUOUS;
>> if (offset != I915_BO_INVALID_OFFSET) {
>> - place->fpfn = offset >> PAGE_SHIFT;
>> - place->lpfn = place->fpfn + (size >> PAGE_SHIFT);
>> + GEM_BUG_ON(!safe_conversion(&place->fpfn, offset >> PAGE_SHIFT));
>> + GEM_BUG_ON(!safe_conversion(&place->lpfn, place->fpfn + (size >> PAGE_SHIFT)));
>
> This would be the natural thing to do with BUG_ON/WARN_ON. And I'd like
> it if we could use it like this. But, as I tried to say, GEM_BUG_ON is
> nothing like BUG_ON/WARN_ON, and no code is generated for
> CONFIG_DRM_I915_DEBUG_GEM=n. And our CI will never catch it because it
> always has CONFIG_DRM_I915_DEBUG_GEM=y.
>
Hi Jani,
Thanks for the detailed explanation of what the build option
CONFIG_DRM_I915_DEBUG_GEM doesn't cover.
Using the WARN_ON() macro, I modified with the way in your comments on
v5 version and sent the v7 patch again.
Many thanks,
G.G
> BR,
> Jani.
>
>
>> } else if (mr->io_size && mr->io_size < mr->total) {
>> if (flags & I915_BO_ALLOC_GPU_ONLY) {
>> place->flags |= TTM_PL_FLAG_TOPDOWN;
>> } else {
>> place->fpfn = 0;
>> - place->lpfn = mr->io_size >> PAGE_SHIFT;
>> + GEM_BUG_ON(!safe_conversion(&place->lpfn, mr->io_size >> PAGE_SHIFT));
>> }
>> }
>> }
>> diff --git a/drivers/gpu/drm/i915/intel_region_ttm.c b/drivers/gpu/drm/i915/intel_region_ttm.c
>> index 575d67bc6ffe..c480b0b50bcc 100644
>> --- a/drivers/gpu/drm/i915/intel_region_ttm.c
>> +++ b/drivers/gpu/drm/i915/intel_region_ttm.c
>> @@ -209,14 +209,28 @@ intel_region_ttm_resource_alloc(struct intel_memory_region *mem,
>> if (flags & I915_BO_ALLOC_CONTIGUOUS)
>> place.flags |= TTM_PL_FLAG_CONTIGUOUS;
>> if (offset != I915_BO_INVALID_OFFSET) {
>> - place.fpfn = offset >> PAGE_SHIFT;
>> - place.lpfn = place.fpfn + (size >> PAGE_SHIFT);
>> + if (!safe_conversion(&place.fpfn, offset >> PAGE_SHIFT)) {
>> + GEM_BUG_ON(!safe_conversion(&place.fpfn,offset >> PAGE_SHIFT));
>> + ret = -E2BIG;
>> + goto out;
>> + }
>> + if (!safe_conversion(&place.lpfn, place.fpfn + (size >> PAGE_SHIFT))) {
>> + GEM_BUG_ON(!safe_conversion(&place.lpfn,
>> + place.fpfn + (size >> PAGE_SHIFT)));
>> + ret = -E2BIG;
>> + goto out;
>> + }
>> } else if (mem->io_size && mem->io_size < mem->total) {
>> if (flags & I915_BO_ALLOC_GPU_ONLY) {
>> place.flags |= TTM_PL_FLAG_TOPDOWN;
>> } else {
>> place.fpfn = 0;
>> - place.lpfn = mem->io_size >> PAGE_SHIFT;
>> + if (!safe_conversion(&place.lpfn, mem->io_size >> PAGE_SHIFT)) {
>> + GEM_BUG_ON(!safe_conversion(&place.lpfn,
>> + mem->io_size >> PAGE_SHIFT));
>> + ret = -E2BIG;
>> + goto out;
>> + }
>> }
>> }
>>
>> @@ -224,6 +238,8 @@ intel_region_ttm_resource_alloc(struct intel_memory_region *mem,
>> mock_bo.bdev = &mem->i915->bdev;
>>
>> ret = man->func->alloc(man, &mock_bo, &place, &res);
>> +
>> +out:
>> if (ret == -ENOSPC)
>> ret = -ENXIO;
>> if (!ret)
>