dri-devel.lists.freedesktop.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v9 0/8] Fixes integer overflow or integer truncation issues in page lookups, ttm place configuration and scatterlist creation
@ 2022-08-24  8:45 Gwan-gyeong Mun
  2022-08-24  8:45 ` [PATCH v9 1/8] overflow: Move and add few utility macros into overflow Gwan-gyeong Mun
                   ` (7 more replies)
  0 siblings, 8 replies; 17+ messages in thread
From: Gwan-gyeong Mun @ 2022-08-24  8:45 UTC (permalink / raw)
  To: intel-gfx
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, keescook,
	jani.nikula, linux-kernel, dri-devel, chris, airlied,
	andrzej.hajda, matthew.auld, intel-gfx-trybot, mchehab,
	nirmoy.das

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 improved
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

v9: Fix overflows_type() to use __builtin_add_overflow() instead of
    __builtin_add_overflow_p() (Andrzej)
    Fix overflows_ptr() to use overflows_type() with the unsigned long type (Andrzej)
v8: Add check_assign() and remove safe_conversion() (Kees)
    Replace safe_conversion() with check_assign() (Kees)
    Fix overflows_type() to use gcc's built-in overflow function (Andrzej)
    Add overflows_ptr() to allow overflow checking when assigning a value
    into a pointer variable (G.G.)
v7: Fix to use WARN_ON() macro where GEM_BUG_ON() macro was used. (Jani)
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 <lkp@intel.com>)
    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_user_extensions.c   |   3 +-
 drivers/gpu/drm/i915/i915_utils.h             |   6 +-
 drivers/gpu/drm/i915/i915_vma.c               |   8 +-
 drivers/gpu/drm/i915/intel_region_ttm.c       |  17 +-
 include/linux/overflow.h                      |  62 ++++
 include/linux/util_macros.h                   |  25 ++
 20 files changed, 486 insertions(+), 95 deletions(-)

-- 
2.37.1


^ permalink raw reply	[flat|nested] 17+ messages in thread

* [PATCH v9 1/8] overflow: Move and add few utility macros into overflow
  2022-08-24  8:45 [PATCH v9 0/8] Fixes integer overflow or integer truncation issues in page lookups, ttm place configuration and scatterlist creation Gwan-gyeong Mun
@ 2022-08-24  8:45 ` Gwan-gyeong Mun
  2022-08-24 10:06   ` Andrzej Hajda
  2022-08-25 16:47   ` Kees Cook
  2022-08-24  8:45 ` [PATCH v9 2/8] util_macros: Add exact_type macro to catch type mis-match while compiling Gwan-gyeong Mun
                   ` (6 subsequent siblings)
  7 siblings, 2 replies; 17+ messages in thread
From: Gwan-gyeong Mun @ 2022-08-24  8:45 UTC (permalink / raw)
  To: intel-gfx
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, keescook,
	jani.nikula, linux-kernel, dri-devel, chris, airlied,
	andrzej.hajda, matthew.auld, intel-gfx-trybot, mchehab,
	nirmoy.das

It moves overflows_type utility macro into overflow header from i915_utils
header. The overflows_type can be used to catch the truncaion (overflow)
between different data types. And it adds check_assign() macro which
performs an assigning source value into destination ptr along with an
overflow check. overflow_type macro has been improved to handle the signbit
by gcc's built-in overflow check function. And it adds overflows_ptr()
helper macro for checking the overflows between a value and a pointer
type/value.

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
v8: Add check_assign() and remove safe_conversion() (Kees)
    Fix overflows_type() to use gcc's built-in overflow function (Andrzej)
    Add overflows_ptr() to allow overflow checking when assigning a value
    into a pointer variable (G.G.)
v9: Fix overflows_type() to use __builtin_add_overflow() instead of
    __builtin_add_overflow_p() (Andrzej)
    Fix overflows_ptr() to use overflows_type() with the unsigned long type
    (Andrzej)

Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Nirmoy Das <nirmoy.das@intel.com>
Cc: Jani Nikula <jani.nikula@intel.com>
Cc: Andi Shyti <andi.shyti@linux.intel.com>
Cc: Andrzej Hajda <andrzej.hajda@intel.com>
Cc: Mauro Carvalho Chehab <mauro.chehab@linux.intel.com>
Cc: Kees Cook <keescook@chromium.org>
Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org> (v5)
---
 drivers/gpu/drm/i915/i915_user_extensions.c |  3 +-
 drivers/gpu/drm/i915/i915_utils.h           |  5 +-
 include/linux/overflow.h                    | 62 +++++++++++++++++++++
 3 files changed, 64 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_user_extensions.c b/drivers/gpu/drm/i915/i915_user_extensions.c
index c822d0aafd2d..6f6b5b910968 100644
--- a/drivers/gpu/drm/i915/i915_user_extensions.c
+++ b/drivers/gpu/drm/i915/i915_user_extensions.c
@@ -50,8 +50,7 @@ int i915_user_extensions(struct i915_user_extension __user *ext,
 		if (err)
 			return err;
 
-		if (get_user(next, &ext->next_extension) ||
-		    overflows_type(next, ext))
+		if (get_user(next, &ext->next_extension) || overflows_ptr(next))
 			return -EFAULT;
 
 		ext = u64_to_user_ptr(next);
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..6af9df1d67a1 100644
--- a/include/linux/overflow.h
+++ b/include/linux/overflow.h
@@ -52,6 +52,68 @@ static inline bool __must_check __must_check_overflow(bool overflow)
 	return unlikely(overflow);
 }
 
+/**
+ * overflows_type - helper for checking the overflows between data types or
+ *                  values
+ *
+ * @x: Source value or data type for overflow check
+ * @T: Destination value or data type for overflow check
+ *
+ * It compares the values or data type between the first and second argument to
+ * check whether overflow can occur when assigning the first argument to the
+ * variable of the second argument. Source and Destination can be singned or
+ * unsigned data types. Source and Destination can be different data types.
+ *
+ * Returns:
+ * True if overflow can occur, false otherwise.
+ */
+#define overflows_type(x, T) __must_check_overflow(({	\
+	typeof(T) v = 0;				\
+	__builtin_add_overflow((x), v, &v);		\
+}))
+
+/**
+ * overflows_ptr - helper for checking the occurrence of overflows when a value
+ *                 assigns to  the pointer data type
+ *
+ * @x: Source value for overflow check
+ *
+ * gcc's built-in overflow check functions don't support checking between the
+ * pointer type and non-pointer type. And ILP32 and LP64 have the same bit size
+ * between long and pointer. Therefore it internally compares the source value
+ * and unsigned long data type for checking overflow.
+ *
+ * Returns:
+ * True if overflow can occur, false otherwise.
+ */
+#define overflows_ptr(x) __must_check_overflow(overflows_type(x, unsigned long))
+
+/**
+ * check_assign - perform an assigning source value into destination ptr along
+ *                with an overflow check.
+ *
+ * @value: Source value
+ * @ptr: Destination pointer address, If the pointer type is not used,
+ *       a warning message is output during build.
+ *
+ * It checks internally the ptr is a pointer type. And it uses gcc's built-in
+ * overflow check function.
+ * It does not use the check_*() wrapper functions, but directly uses gcc's
+ * built-in overflow check function so that it can be used even when
+ * the type of value and the type pointed to by ptr are different without build
+ * warning messages.
+ *
+ * Returns:
+ * If the value would overflow the destination, it returns true. If not return
+ * false. When overflow does not occur, the assigning into ptr from value
+ * succeeds. It follows the return policy as other check_*_overflow() functions
+ * return non-zero as a failure.
+ */
+#define check_assign(value, ptr) __must_check_overflow(({	\
+	typecheck_pointer(ptr); 		\
+	__builtin_add_overflow(0, value, ptr);	\
+}))
+
 /*
  * For simplicity and code hygiene, the fallback code below insists on
  * a, b and *d having the same type (similar to the min() and max()
-- 
2.37.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v9 2/8] util_macros: Add exact_type macro to catch type mis-match while compiling
  2022-08-24  8:45 [PATCH v9 0/8] Fixes integer overflow or integer truncation issues in page lookups, ttm place configuration and scatterlist creation Gwan-gyeong Mun
  2022-08-24  8:45 ` [PATCH v9 1/8] overflow: Move and add few utility macros into overflow Gwan-gyeong Mun
@ 2022-08-24  8:45 ` Gwan-gyeong Mun
  2022-08-25  8:04   ` [Intel-gfx] " Gwan-gyeong Mun
  2022-08-25 17:19   ` Kees Cook
  2022-08-24  8:45 ` [PATCH v9 3/8] drm/i915/gem: Typecheck page lookups Gwan-gyeong Mun
                   ` (5 subsequent siblings)
  7 siblings, 2 replies; 17+ messages in thread
From: Gwan-gyeong Mun @ 2022-08-24  8:45 UTC (permalink / raw)
  To: intel-gfx
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, keescook,
	jani.nikula, linux-kernel, dri-devel, chris, airlied,
	andrzej.hajda, matthew.auld, intel-gfx-trybot, mchehab,
	nirmoy.das

It adds exact_type and exactly_pgoff_t macro to catch type mis-match while
compiling. The existing typecheck() macro outputs build warnings, but the
newly added exact_type() macro uses the BUILD_BUG_ON() macro to generate
a build break when the types are different and can be used to detect
explicit build errors.

v6: Move macro addition location so that it can be used by other than drm
    subsystem (Jani, Mauro, Andi)

Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Nirmoy Das <nirmoy.das@intel.com>
Cc: Jani Nikula <jani.nikula@intel.com>
Cc: Andi Shyti <andi.shyti@linux.intel.com>
Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
---
 include/linux/util_macros.h | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/include/linux/util_macros.h b/include/linux/util_macros.h
index 72299f261b25..b6624b275257 100644
--- a/include/linux/util_macros.h
+++ b/include/linux/util_macros.h
@@ -2,6 +2,9 @@
 #ifndef _LINUX_HELPER_MACROS_H_
 #define _LINUX_HELPER_MACROS_H_
 
+#include <linux/types.h>
+#include <linux/bug.h>
+
 #define __find_closest(x, a, as, op)					\
 ({									\
 	typeof(as) __fc_i, __fc_as = (as) - 1;				\
@@ -38,4 +41,26 @@
  */
 #define find_closest_descending(x, a, as) __find_closest(x, a, as, >=)
 
+/**
+ * exact_type - break compile if source type and destination value's type are
+ * not the same
+ * @T: Source type
+ * @n: Destination value
+ *
+ * It is a helper macro for a poor man's -Wconversion: only allow variables of
+ * an exact type. It determines whether the source type and destination value's
+ * type are the same while compiling, and it breaks compile if two types are
+ * not the same
+ */
+#define exact_type(T, n) \
+	BUILD_BUG_ON(!__builtin_constant_p(n) && !__builtin_types_compatible_p(T, typeof(n)))
+
+/**
+ * exactly_pgoff_t - helper to check if the type of a value is pgoff_t
+ * @n: value to compare pgoff_t type
+ *
+ * It breaks compile if the argument value's type is not pgoff_t type.
+ */
+#define exactly_pgoff_t(n) exact_type(pgoff_t, n)
+
 #endif
-- 
2.37.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v9 3/8] drm/i915/gem: Typecheck page lookups
  2022-08-24  8:45 [PATCH v9 0/8] Fixes integer overflow or integer truncation issues in page lookups, ttm place configuration and scatterlist creation Gwan-gyeong Mun
  2022-08-24  8:45 ` [PATCH v9 1/8] overflow: Move and add few utility macros into overflow Gwan-gyeong Mun
  2022-08-24  8:45 ` [PATCH v9 2/8] util_macros: Add exact_type macro to catch type mis-match while compiling Gwan-gyeong Mun
@ 2022-08-24  8:45 ` Gwan-gyeong Mun
  2022-08-24  8:45 ` [PATCH v9 4/8] drm/i915: Check for integer truncation on scatterlist creation Gwan-gyeong Mun
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Gwan-gyeong Mun @ 2022-08-24  8:45 UTC (permalink / raw)
  To: intel-gfx
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, keescook,
	jani.nikula, linux-kernel, dri-devel, chris, airlied,
	andrzej.hajda, matthew.auld, intel-gfx-trybot, mchehab,
	nirmoy.das

From: Chris Wilson <chris@chris-wilson.co.uk>

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. Be pedantic and add integer
typechecking to the lookup so that we can be sure that we are safe.
And it also uses pgoff_t as our page lookups must remain compatible with
the page cache, pgoff_t is currently exactly unsigned long.

v2: Move added i915_utils's macro into drm_util header (Jani N)
v3: 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.
v4: Add kernel-doc markups to the kAPI functions and macros (Mauoro)
v5: Fix an alignment to match open parenthesis
v6: Rebase

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@linux.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>
Reviewed-by: Andrzej Hajda <andrzej.hajda@intel.com>
---
 drivers/gpu/drm/i915/gem/i915_gem_object.c    |   7 +-
 drivers/gpu/drm/i915/gem/i915_gem_object.h    | 293 ++++++++++++++++--
 drivers/gpu/drm/i915/gem/i915_gem_pages.c     |  27 +-
 drivers/gpu/drm/i915/gem/i915_gem_ttm.c       |   2 +-
 .../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/i915_gem.c               |  18 +-
 drivers/gpu/drm/i915/i915_utils.h             |   1 +
 drivers/gpu/drm/i915/i915_vma.c               |   8 +-
 10 files changed, 323 insertions(+), 61 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c
index 389e9f157ca5..b3861739c1eb 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c
@@ -413,10 +413,11 @@ void __i915_gem_object_invalidate_frontbuffer(struct drm_i915_gem_object *obj,
 static void
 i915_gem_object_read_from_page_kmap(struct drm_i915_gem_object *obj, u64 offset, void *dst, int size)
 {
+	pgoff_t idx = offset >> PAGE_SHIFT;
 	void *src_map;
 	void *src_ptr;
 
-	src_map = kmap_atomic(i915_gem_object_get_page(obj, offset >> PAGE_SHIFT));
+	src_map = kmap_atomic(i915_gem_object_get_page(obj, idx));
 
 	src_ptr = src_map + offset_in_page(offset);
 	if (!(obj->cache_coherent & I915_BO_CACHE_COHERENT_FOR_READ))
@@ -429,9 +430,10 @@ i915_gem_object_read_from_page_kmap(struct drm_i915_gem_object *obj, u64 offset,
 static void
 i915_gem_object_read_from_page_iomap(struct drm_i915_gem_object *obj, u64 offset, void *dst, int size)
 {
+	pgoff_t idx = offset >> PAGE_SHIFT;
+	dma_addr_t dma = i915_gem_object_get_dma_address(obj, idx);
 	void __iomem *src_map;
 	void __iomem *src_ptr;
-	dma_addr_t dma = i915_gem_object_get_dma_address(obj, offset >> PAGE_SHIFT);
 
 	src_map = io_mapping_map_wc(&obj->mm.region->iomap,
 				    dma - obj->mm.region->region.start,
@@ -460,6 +462,7 @@ i915_gem_object_read_from_page_iomap(struct drm_i915_gem_object *obj, u64 offset
  */
 int i915_gem_object_read_from_page(struct drm_i915_gem_object *obj, u64 offset, void *dst, int size)
 {
+	GEM_BUG_ON(overflows_type(offset >> PAGE_SHIFT, pgoff_t));
 	GEM_BUG_ON(offset >= obj->base.size);
 	GEM_BUG_ON(offset_in_page(offset) > PAGE_SIZE - size);
 	GEM_BUG_ON(!i915_gem_object_has_pinned_pages(obj));
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
index 6f0a3ce35567..5da872afc4ba 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
@@ -27,8 +27,10 @@ enum intel_region_id;
  * 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 num_pages
- * - get_user_pages*() mixed ints with longs
+ * - sg_table embeds unsigned int for nents
+ *
+ * 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)
@@ -363,44 +365,289 @@ i915_gem_object_get_tile_row_size(const struct drm_i915_gem_object *obj)
 int i915_gem_object_set_tiling(struct drm_i915_gem_object *obj,
 			       unsigned int tiling, unsigned int stride);
 
+/**
+ * __i915_gem_object_page_iter_get_sg - helper to find the target scatterlist
+ * pointer and the target page position using pgoff_t n input argument and
+ * i915_gem_object_page_iter
+ * @obj: i915 GEM buffer object
+ * @iter: i915 GEM buffer object page iterator
+ * @n: page offset
+ * @offset: searched physical offset,
+ *          it will be used for returning physical page offset value
+ *
+ * Context: Takes and releases the mutex lock of the i915_gem_object_page_iter.
+ *          Takes and releases the RCU lock to search the radix_tree of
+ *          i915_gem_object_page_iter.
+ *
+ * Returns:
+ * The target scatterlist pointer and the target page position.
+ *
+ * Recommended to use wrapper macro: i915_gem_object_page_iter_get_sg()
+ */
 struct scatterlist *
-__i915_gem_object_get_sg(struct drm_i915_gem_object *obj,
-			 struct i915_gem_object_page_iter *iter,
-			 unsigned int n,
-			 unsigned int *offset, bool dma);
+__i915_gem_object_page_iter_get_sg(struct drm_i915_gem_object *obj,
+				   struct i915_gem_object_page_iter *iter,
+				   pgoff_t  n,
+				   unsigned int *offset);
 
+/**
+ * i915_gem_object_page_iter_get_sg - wrapper macro for
+ * __i915_gem_object_page_iter_get_sg()
+ * @obj: i915 GEM buffer object
+ * @it: i915 GEM buffer object page iterator
+ * @n: page offset
+ * @offset: searched physical offset,
+ *          it will be used for returning physical page offset value
+ *
+ * Context: Takes and releases the mutex lock of the i915_gem_object_page_iter.
+ *          Takes and releases the RCU lock to search the radix_tree of
+ *          i915_gem_object_page_iter.
+ *
+ * Returns:
+ * The target scatterlist pointer and the target page position.
+ *
+ * In order to avoid the truncation of the input parameter, it checks the page
+ * offset n's type from the input parameter before calling
+ * __i915_gem_object_page_iter_get_sg().
+ */
+#define i915_gem_object_page_iter_get_sg(obj, it, n, offset) ({ \
+	exactly_pgoff_t(n); \
+	__i915_gem_object_page_iter_get_sg(obj, it, n, offset); \
+})
+
+/**
+ * __i915_gem_object_get_sg - helper to find the target scatterlist
+ * pointer and the target page position using pgoff_t n input argument and
+ * drm_i915_gem_object. It uses an internal shmem scatterlist lookup function.
+ * @obj: i915 GEM buffer object
+ * @n: page offset
+ * @offset: searched physical offset,
+ *          it will be used for returning physical page offset value
+ *
+ * It uses drm_i915_gem_object's internal shmem scatterlist lookup function as
+ * i915_gem_object_page_iter and calls __i915_gem_object_page_iter_get_sg().
+ *
+ * Returns:
+ * The target scatterlist pointer and the target page position.
+ *
+ * Recommended to use wrapper macro: i915_gem_object_get_sg()
+ * See also __i915_gem_object_page_iter_get_sg()
+ */
 static inline struct scatterlist *
-i915_gem_object_get_sg(struct drm_i915_gem_object *obj,
-		       unsigned int n,
-		       unsigned int *offset)
+__i915_gem_object_get_sg(struct drm_i915_gem_object *obj, pgoff_t n,
+			 unsigned int *offset)
 {
-	return __i915_gem_object_get_sg(obj, &obj->mm.get_page, n, offset, false);
+	return __i915_gem_object_page_iter_get_sg(obj, &obj->mm.get_page, n, offset);
 }
 
+/**
+ * i915_gem_object_get_sg - wrapper macro for __i915_gem_object_get_sg()
+ * @obj: i915 GEM buffer object
+ * @n: page offset
+ * @offset: searched physical offset,
+ *          it will be used for returning physical page offset value
+ *
+ * Returns:
+ * The target scatterlist pointer and the target page position.
+ *
+ * In order to avoid the truncation of the input parameter, it checks the page
+ * offset n's type from the input parameter before calling
+ * __i915_gem_object_get_sg().
+ * See also __i915_gem_object_page_iter_get_sg()
+ */
+#define i915_gem_object_get_sg(obj, n, offset) ({ \
+	exactly_pgoff_t(n); \
+	__i915_gem_object_get_sg(obj, n, offset); \
+})
+
+/**
+ * __i915_gem_object_get_sg_dma - helper to find the target scatterlist
+ * pointer and the target page position using pgoff_t n input argument and
+ * drm_i915_gem_object. It uses an internal DMA mapped scatterlist lookup function
+ * @obj: i915 GEM buffer object
+ * @n: page offset
+ * @offset: searched physical offset,
+ *          it will be used for returning physical page offset value
+ *
+ * It uses drm_i915_gem_object's internal DMA mapped scatterlist lookup function
+ * as i915_gem_object_page_iter and calls __i915_gem_object_page_iter_get_sg().
+ *
+ * Returns:
+ * The target scatterlist pointer and the target page position.
+ *
+ * Recommended to use wrapper macro: i915_gem_object_get_sg_dma()
+ * See also __i915_gem_object_page_iter_get_sg()
+ */
 static inline struct scatterlist *
-i915_gem_object_get_sg_dma(struct drm_i915_gem_object *obj,
-			   unsigned int n,
-			   unsigned int *offset)
+__i915_gem_object_get_sg_dma(struct drm_i915_gem_object *obj, pgoff_t n,
+			     unsigned int *offset)
 {
-	return __i915_gem_object_get_sg(obj, &obj->mm.get_dma_page, n, offset, true);
+	return __i915_gem_object_page_iter_get_sg(obj, &obj->mm.get_dma_page, n, offset);
 }
 
+/**
+ * i915_gem_object_get_sg_dma - wrapper macro for __i915_gem_object_get_sg_dma()
+ * @obj: i915 GEM buffer object
+ * @n: page offset
+ * @offset: searched physical offset,
+ *          it will be used for returning physical page offset value
+ *
+ * Returns:
+ * The target scatterlist pointer and the target page position.
+ *
+ * In order to avoid the truncation of the input parameter, it checks the page
+ * offset n's type from the input parameter before calling
+ * __i915_gem_object_get_sg_dma().
+ * See also __i915_gem_object_page_iter_get_sg()
+ */
+#define i915_gem_object_get_sg_dma(obj, n, offset) ({ \
+	exactly_pgoff_t(n); \
+	__i915_gem_object_get_sg_dma(obj, n, offset); \
+})
+
+/**
+ * __i915_gem_object_get_page - helper to find the target page with a page offset
+ * @obj: i915 GEM buffer object
+ * @n: page offset
+ *
+ * It uses drm_i915_gem_object's internal shmem scatterlist lookup function as
+ * i915_gem_object_page_iter and calls __i915_gem_object_page_iter_get_sg()
+ * internally.
+ *
+ * Returns:
+ * The target page pointer.
+ *
+ * Recommended to use wrapper macro: i915_gem_object_get_page()
+ * See also __i915_gem_object_page_iter_get_sg()
+ */
 struct page *
-i915_gem_object_get_page(struct drm_i915_gem_object *obj,
-			 unsigned int n);
+__i915_gem_object_get_page(struct drm_i915_gem_object *obj, pgoff_t n);
 
+/**
+ * i915_gem_object_get_page - wrapper macro for __i915_gem_object_get_page
+ * @obj: i915 GEM buffer object
+ * @n: page offset
+ *
+ * Returns:
+ * The target page pointer.
+ *
+ * In order to avoid the truncation of the input parameter, it checks the page
+ * offset n's type from the input parameter before calling
+ * __i915_gem_object_get_page().
+ * See also __i915_gem_object_page_iter_get_sg()
+ */
+#define i915_gem_object_get_page(obj, n) ({ \
+	exactly_pgoff_t(n); \
+	__i915_gem_object_get_page(obj, n); \
+})
+
+/**
+ * __i915_gem_object_get_dirty_page - helper to find the target page with a page
+ * offset
+ * @obj: i915 GEM buffer object
+ * @n: page offset
+ *
+ * It works like i915_gem_object_get_page(), but it marks the returned page dirty.
+ *
+ * Returns:
+ * The target page pointer.
+ *
+ * Recommended to use wrapper macro: i915_gem_object_get_dirty_page()
+ * See also __i915_gem_object_page_iter_get_sg() and __i915_gem_object_get_page()
+ */
 struct page *
-i915_gem_object_get_dirty_page(struct drm_i915_gem_object *obj,
-			       unsigned int n);
+__i915_gem_object_get_dirty_page(struct drm_i915_gem_object *obj, pgoff_t n);
+
+/**
+ * i915_gem_object_get_dirty_page - wrapper macro for __i915_gem_object_get_dirty_page
+ * @obj: i915 GEM buffer object
+ * @n: page offset
+ *
+ * Returns:
+ * The target page pointer.
+ *
+ * In order to avoid the truncation of the input parameter, it checks the page
+ * offset n's type from the input parameter before calling
+ * __i915_gem_object_get_dirty_page().
+ * See also __i915_gem_object_page_iter_get_sg() and __i915_gem_object_get_page()
+ */
+#define i915_gem_object_get_dirty_page(obj, n) ({ \
+	exactly_pgoff_t(n); \
+	__i915_gem_object_get_dirty_page(obj, n); \
+})
 
+/**
+ * __i915_gem_object_get_dma_address_len - helper to get bus addresses of
+ * targeted DMA mapped scatterlist from i915 GEM buffer object and it's length
+ * @obj: i915 GEM buffer object
+ * @n: page offset
+ * @len: DMA mapped scatterlist's DMA bus addresses length to return
+ *
+ * Returns:
+ * Bus addresses of targeted DMA mapped scatterlist
+ *
+ * Recommended to use wrapper macro: i915_gem_object_get_dma_address_len()
+ * See also __i915_gem_object_page_iter_get_sg() and __i915_gem_object_get_sg_dma()
+ */
 dma_addr_t
-i915_gem_object_get_dma_address_len(struct drm_i915_gem_object *obj,
-				    unsigned long n,
-				    unsigned int *len);
+__i915_gem_object_get_dma_address_len(struct drm_i915_gem_object *obj, pgoff_t n,
+				      unsigned int *len);
 
+/**
+ * i915_gem_object_get_dma_address_len - wrapper macro for
+ * __i915_gem_object_get_dma_address_len
+ * @obj: i915 GEM buffer object
+ * @n: page offset
+ * @len: DMA mapped scatterlist's DMA bus addresses length to return
+ *
+ * Returns:
+ * Bus addresses of targeted DMA mapped scatterlist
+ *
+ * In order to avoid the truncation of the input parameter, it checks the page
+ * offset n's type from the input parameter before calling
+ * __i915_gem_object_get_dma_address_len().
+ * See also __i915_gem_object_page_iter_get_sg() and
+ * __i915_gem_object_get_dma_address_len()
+ */
+#define i915_gem_object_get_dma_address_len(obj, n, len) ({ \
+	exactly_pgoff_t(n); \
+	__i915_gem_object_get_dma_address_len(obj, n, len); \
+})
+
+/**
+ * __i915_gem_object_get_dma_address - helper to get bus addresses of
+ * targeted DMA mapped scatterlist from i915 GEM buffer object
+ * @obj: i915 GEM buffer object
+ * @n: page offset
+ *
+ * Returns:
+ * Bus addresses of targeted DMA mapped scatterlis
+ *
+ * Recommended to use wrapper macro: i915_gem_object_get_dma_address()
+ * See also __i915_gem_object_page_iter_get_sg() and __i915_gem_object_get_sg_dma()
+ */
 dma_addr_t
-i915_gem_object_get_dma_address(struct drm_i915_gem_object *obj,
-				unsigned long n);
+__i915_gem_object_get_dma_address(struct drm_i915_gem_object *obj, pgoff_t n);
+
+/**
+ * i915_gem_object_get_dma_address - wrapper macro for
+ * __i915_gem_object_get_dma_address
+ * @obj: i915 GEM buffer object
+ * @n: page offset
+ *
+ * Returns:
+ * Bus addresses of targeted DMA mapped scatterlist
+ *
+ * In order to avoid the truncation of the input parameter, it checks the page
+ * offset n's type from the input parameter before calling
+ * __i915_gem_object_get_dma_address().
+ * See also __i915_gem_object_page_iter_get_sg() and
+ * __i915_gem_object_get_dma_address()
+ */
+#define i915_gem_object_get_dma_address(obj, n) ({ \
+	exactly_pgoff_t(n); \
+	__i915_gem_object_get_dma_address(obj, n); \
+})
 
 void __i915_gem_object_set_pages(struct drm_i915_gem_object *obj,
 				 struct sg_table *pages,
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pages.c b/drivers/gpu/drm/i915/gem/i915_gem_pages.c
index 8357dbdcab5c..4d925202cae1 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_pages.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_pages.c
@@ -510,14 +510,16 @@ void __i915_gem_object_release_map(struct drm_i915_gem_object *obj)
 }
 
 struct scatterlist *
-__i915_gem_object_get_sg(struct drm_i915_gem_object *obj,
-			 struct i915_gem_object_page_iter *iter,
-			 unsigned int n,
-			 unsigned int *offset,
-			 bool dma)
+__i915_gem_object_page_iter_get_sg(struct drm_i915_gem_object *obj,
+				   struct i915_gem_object_page_iter *iter,
+				   pgoff_t n,
+				   unsigned int *offset)
+
 {
-	struct scatterlist *sg;
+	const bool dma = iter == &obj->mm.get_dma_page ||
+			 iter == &obj->ttm.get_io_page;
 	unsigned int idx, count;
+	struct scatterlist *sg;
 
 	might_sleep();
 	GEM_BUG_ON(n >= obj->base.size >> PAGE_SHIFT);
@@ -625,7 +627,7 @@ __i915_gem_object_get_sg(struct drm_i915_gem_object *obj,
 }
 
 struct page *
-i915_gem_object_get_page(struct drm_i915_gem_object *obj, unsigned int n)
+__i915_gem_object_get_page(struct drm_i915_gem_object *obj, pgoff_t n)
 {
 	struct scatterlist *sg;
 	unsigned int offset;
@@ -638,8 +640,7 @@ i915_gem_object_get_page(struct drm_i915_gem_object *obj, unsigned int n)
 
 /* Like i915_gem_object_get_page(), but mark the returned page dirty */
 struct page *
-i915_gem_object_get_dirty_page(struct drm_i915_gem_object *obj,
-			       unsigned int n)
+__i915_gem_object_get_dirty_page(struct drm_i915_gem_object *obj, pgoff_t n)
 {
 	struct page *page;
 
@@ -651,9 +652,8 @@ i915_gem_object_get_dirty_page(struct drm_i915_gem_object *obj,
 }
 
 dma_addr_t
-i915_gem_object_get_dma_address_len(struct drm_i915_gem_object *obj,
-				    unsigned long n,
-				    unsigned int *len)
+__i915_gem_object_get_dma_address_len(struct drm_i915_gem_object *obj,
+				      pgoff_t n, unsigned int *len)
 {
 	struct scatterlist *sg;
 	unsigned int offset;
@@ -667,8 +667,7 @@ i915_gem_object_get_dma_address_len(struct drm_i915_gem_object *obj,
 }
 
 dma_addr_t
-i915_gem_object_get_dma_address(struct drm_i915_gem_object *obj,
-				unsigned long n)
+__i915_gem_object_get_dma_address(struct drm_i915_gem_object *obj, pgoff_t n)
 {
 	return i915_gem_object_get_dma_address_len(obj, n, NULL);
 }
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
index bc9c432edffe..4148126e7b1b 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
@@ -685,7 +685,7 @@ static unsigned long i915_ttm_io_mem_pfn(struct ttm_buffer_object *bo,
 	GEM_WARN_ON(bo->ttm);
 
 	base = obj->mm.region->iomap.base - obj->mm.region->region.start;
-	sg = __i915_gem_object_get_sg(obj, &obj->ttm.get_io_page, page_offset, &ofs, true);
+	sg = i915_gem_object_page_iter_get_sg(obj, &obj->ttm.get_io_page, page_offset, &ofs);
 
 	return ((base + sg_dma_address(sg)) >> PAGE_SHIFT) + ofs;
 }
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
index c6ad67b90e8a..a18a890e681f 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_context.c
@@ -455,7 +455,8 @@ static int gpu_fill(struct intel_context *ce,
 static int cpu_fill(struct drm_i915_gem_object *obj, u32 value)
 {
 	const bool has_llc = HAS_LLC(to_i915(obj->base.dev));
-	unsigned int n, m, need_flush;
+	unsigned int need_flush;
+	unsigned long n, m;
 	int err;
 
 	i915_gem_object_lock(obj, NULL);
@@ -485,7 +486,8 @@ static int cpu_fill(struct drm_i915_gem_object *obj, u32 value)
 static noinline int cpu_check(struct drm_i915_gem_object *obj,
 			      unsigned int idx, unsigned int max)
 {
-	unsigned int n, m, needs_flush;
+	unsigned int needs_flush;
+	unsigned long n;
 	int err;
 
 	i915_gem_object_lock(obj, NULL);
@@ -494,7 +496,7 @@ static noinline int cpu_check(struct drm_i915_gem_object *obj,
 		goto out_unlock;
 
 	for (n = 0; n < real_page_count(obj); n++) {
-		u32 *map;
+		u32 *map, m;
 
 		map = kmap_atomic(i915_gem_object_get_page(obj, n));
 		if (needs_flush & CLFLUSH_BEFORE)
@@ -502,7 +504,7 @@ static noinline int cpu_check(struct drm_i915_gem_object *obj,
 
 		for (m = 0; m < max; m++) {
 			if (map[m] != m) {
-				pr_err("%pS: Invalid value at object %d page %d/%ld, offset %d/%d: found %x expected %x\n",
+				pr_err("%pS: Invalid value at object %d page %ld/%ld, offset %d/%d: found %x expected %x\n",
 				       __builtin_return_address(0), idx,
 				       n, real_page_count(obj), m, max,
 				       map[m], m);
@@ -513,7 +515,7 @@ static noinline int cpu_check(struct drm_i915_gem_object *obj,
 
 		for (; m < DW_PER_PAGE; m++) {
 			if (map[m] != STACK_MAGIC) {
-				pr_err("%pS: Invalid value at object %d page %d, offset %d: found %x expected %x (uninitialised)\n",
+				pr_err("%pS: Invalid value at object %d page %ld, offset %d: found %x expected %x (uninitialised)\n",
 				       __builtin_return_address(0), idx, n, m,
 				       map[m], STACK_MAGIC);
 				err = -EINVAL;
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
index 3cff08f04f6c..7512b21b6b4f 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_mman.c
@@ -95,11 +95,11 @@ static int check_partial_mapping(struct drm_i915_gem_object *obj,
 	struct drm_i915_private *i915 = to_i915(obj->base.dev);
 	struct i915_ggtt_view view;
 	struct i915_vma *vma;
+	unsigned long offset;
 	unsigned long page;
 	u32 __iomem *io;
 	struct page *p;
 	unsigned int n;
-	u64 offset;
 	u32 *cpu;
 	int err;
 
@@ -156,7 +156,7 @@ static int check_partial_mapping(struct drm_i915_gem_object *obj,
 	cpu = kmap(p) + offset_in_page(offset);
 	drm_clflush_virt_range(cpu, sizeof(*cpu));
 	if (*cpu != (u32)page) {
-		pr_err("Partial view for %lu [%u] (offset=%llu, size=%u [%llu, row size %u], fence=%d, tiling=%d, stride=%d) misalignment, expected write to page (%llu + %u [0x%llx]) of 0x%x, found 0x%x\n",
+		pr_err("Partial view for %lu [%u] (offset=%llu, size=%u [%llu, row size %u], fence=%d, tiling=%d, stride=%d) misalignment, expected write to page (%lu + %u [0x%lx]) of 0x%x, found 0x%x\n",
 		       page, n,
 		       view.partial.offset,
 		       view.partial.size,
@@ -212,10 +212,10 @@ static int check_partial_mappings(struct drm_i915_gem_object *obj,
 	for_each_prime_number_from(page, 1, npages) {
 		struct i915_ggtt_view view =
 			compute_partial_view(obj, page, MIN_CHUNK_PAGES);
+		unsigned long offset;
 		u32 __iomem *io;
 		struct page *p;
 		unsigned int n;
-		u64 offset;
 		u32 *cpu;
 
 		GEM_BUG_ON(view.partial.size > nreal);
@@ -252,7 +252,7 @@ static int check_partial_mappings(struct drm_i915_gem_object *obj,
 		cpu = kmap(p) + offset_in_page(offset);
 		drm_clflush_virt_range(cpu, sizeof(*cpu));
 		if (*cpu != (u32)page) {
-			pr_err("Partial view for %lu [%u] (offset=%llu, size=%u [%llu, row size %u], fence=%d, tiling=%d, stride=%d) misalignment, expected write to page (%llu + %u [0x%llx]) of 0x%x, found 0x%x\n",
+			pr_err("Partial view for %lu [%u] (offset=%llu, size=%u [%llu, row size %u], fence=%d, tiling=%d, stride=%d) misalignment, expected write to page (%lu + %u [0x%lx]) of 0x%x, found 0x%x\n",
 			       page, n,
 			       view.partial.offset,
 			       view.partial.size,
diff --git a/drivers/gpu/drm/i915/gem/selftests/i915_gem_object.c b/drivers/gpu/drm/i915/gem/selftests/i915_gem_object.c
index bdf5bb40ccf1..19e374f68ff7 100644
--- a/drivers/gpu/drm/i915/gem/selftests/i915_gem_object.c
+++ b/drivers/gpu/drm/i915/gem/selftests/i915_gem_object.c
@@ -33,10 +33,10 @@ static int igt_gem_object(void *arg)
 
 static int igt_gem_huge(void *arg)
 {
-	const unsigned int nreal = 509; /* just to be awkward */
+	const unsigned long nreal = 509; /* just to be awkward */
 	struct drm_i915_private *i915 = arg;
 	struct drm_i915_gem_object *obj;
-	unsigned int n;
+	unsigned long n;
 	int err;
 
 	/* Basic sanitycheck of our huge fake object allocation */
@@ -49,7 +49,7 @@ static int igt_gem_huge(void *arg)
 
 	err = i915_gem_object_pin_pages_unlocked(obj);
 	if (err) {
-		pr_err("Failed to allocate %u pages (%lu total), err=%d\n",
+		pr_err("Failed to allocate %lu pages (%lu total), err=%d\n",
 		       nreal, obj->base.size / PAGE_SIZE, err);
 		goto out;
 	}
@@ -57,7 +57,7 @@ static int igt_gem_huge(void *arg)
 	for (n = 0; n < obj->base.size / PAGE_SIZE; n++) {
 		if (i915_gem_object_get_page(obj, n) !=
 		    i915_gem_object_get_page(obj, n % nreal)) {
-			pr_err("Page lookup mismatch at index %u [%u]\n",
+			pr_err("Page lookup mismatch at index %lu [%lu]\n",
 			       n, n % nreal);
 			err = -EINVAL;
 			goto out_unpin;
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 702e5b89be22..dba58a3c3238 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -229,8 +229,9 @@ i915_gem_shmem_pread(struct drm_i915_gem_object *obj,
 		     struct drm_i915_gem_pread *args)
 {
 	unsigned int needs_clflush;
-	unsigned int idx, offset;
 	char __user *user_data;
+	unsigned long offset;
+	pgoff_t idx;
 	u64 remain;
 	int ret;
 
@@ -383,13 +384,17 @@ i915_gem_gtt_pread(struct drm_i915_gem_object *obj,
 {
 	struct drm_i915_private *i915 = to_i915(obj->base.dev);
 	struct i915_ggtt *ggtt = to_gt(i915)->ggtt;
+	unsigned long remain, offset;
 	intel_wakeref_t wakeref;
 	struct drm_mm_node node;
 	void __user *user_data;
 	struct i915_vma *vma;
-	u64 remain, offset;
 	int ret = 0;
 
+	if (overflows_type(args->size, remain) ||
+	    overflows_type(args->offset, offset))
+		return -EINVAL;
+
 	wakeref = intel_runtime_pm_get(&i915->runtime_pm);
 
 	vma = i915_gem_gtt_prepare(obj, &node, false);
@@ -540,13 +545,17 @@ i915_gem_gtt_pwrite_fast(struct drm_i915_gem_object *obj,
 	struct drm_i915_private *i915 = to_i915(obj->base.dev);
 	struct i915_ggtt *ggtt = to_gt(i915)->ggtt;
 	struct intel_runtime_pm *rpm = &i915->runtime_pm;
+	unsigned long remain, offset;
 	intel_wakeref_t wakeref;
 	struct drm_mm_node node;
 	struct i915_vma *vma;
-	u64 remain, offset;
 	void __user *user_data;
 	int ret = 0;
 
+	if (overflows_type(args->size, remain) ||
+	    overflows_type(args->offset, offset))
+		return -EINVAL;
+
 	if (i915_gem_object_has_struct_page(obj)) {
 		/*
 		 * Avoid waking the device up if we can fallback, as
@@ -654,8 +663,9 @@ i915_gem_shmem_pwrite(struct drm_i915_gem_object *obj,
 {
 	unsigned int partial_cacheline_write;
 	unsigned int needs_clflush;
-	unsigned int offset, idx;
 	void __user *user_data;
+	unsigned long offset;
+	pgoff_t idx;
 	u64 remain;
 	int ret;
 
diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h
index eb0ded23fa9c..b325b48a66ec 100644
--- a/drivers/gpu/drm/i915/i915_utils.h
+++ b/drivers/gpu/drm/i915/i915_utils.h
@@ -33,6 +33,7 @@
 #include <linux/workqueue.h>
 #include <linux/sched/clock.h>
 #include <linux/overflow.h>
+#include <linux/util_macros.h>
 
 #ifdef CONFIG_X86
 #include <asm/hypervisor.h>
diff --git a/drivers/gpu/drm/i915/i915_vma.c b/drivers/gpu/drm/i915/i915_vma.c
index 260371716490..1dd8a0f51aeb 100644
--- a/drivers/gpu/drm/i915/i915_vma.c
+++ b/drivers/gpu/drm/i915/i915_vma.c
@@ -909,7 +909,7 @@ rotate_pages(struct drm_i915_gem_object *obj, unsigned int offset,
 	     struct sg_table *st, struct scatterlist *sg)
 {
 	unsigned int column, row;
-	unsigned int src_idx;
+	pgoff_t src_idx;
 
 	for (column = 0; column < width; column++) {
 		unsigned int left;
@@ -1015,7 +1015,7 @@ add_padding_pages(unsigned int count,
 
 static struct scatterlist *
 remap_tiled_color_plane_pages(struct drm_i915_gem_object *obj,
-			      unsigned int offset, unsigned int alignment_pad,
+			      unsigned long offset, unsigned int alignment_pad,
 			      unsigned int width, unsigned int height,
 			      unsigned int src_stride, unsigned int dst_stride,
 			      struct sg_table *st, struct scatterlist *sg,
@@ -1074,7 +1074,7 @@ remap_tiled_color_plane_pages(struct drm_i915_gem_object *obj,
 
 static struct scatterlist *
 remap_contiguous_pages(struct drm_i915_gem_object *obj,
-		       unsigned int obj_offset,
+		       pgoff_t obj_offset,
 		       unsigned int count,
 		       struct sg_table *st, struct scatterlist *sg)
 {
@@ -1107,7 +1107,7 @@ remap_contiguous_pages(struct drm_i915_gem_object *obj,
 
 static struct scatterlist *
 remap_linear_color_plane_pages(struct drm_i915_gem_object *obj,
-			       unsigned int obj_offset, unsigned int alignment_pad,
+			       pgoff_t obj_offset, unsigned int alignment_pad,
 			       unsigned int size,
 			       struct sg_table *st, struct scatterlist *sg,
 			       unsigned int *gtt_offset)
-- 
2.37.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v9 4/8] drm/i915: Check for integer truncation on scatterlist creation
  2022-08-24  8:45 [PATCH v9 0/8] Fixes integer overflow or integer truncation issues in page lookups, ttm place configuration and scatterlist creation Gwan-gyeong Mun
                   ` (2 preceding siblings ...)
  2022-08-24  8:45 ` [PATCH v9 3/8] drm/i915/gem: Typecheck page lookups Gwan-gyeong Mun
@ 2022-08-24  8:45 ` Gwan-gyeong Mun
  2022-08-24  8:45 ` [PATCH v9 5/8] drm/i915: Check for integer truncation on the configuration of ttm place Gwan-gyeong Mun
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Gwan-gyeong Mun @ 2022-08-24  8:45 UTC (permalink / raw)
  To: intel-gfx
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, keescook,
	jani.nikula, linux-kernel, dri-devel, chris, airlied,
	andrzej.hajda, matthew.auld, intel-gfx-trybot, mchehab,
	nirmoy.das

From: Chris Wilson <chris@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)
v5: Fix macros to be enclosed in parentheses for complex values
    Fix too long line warning
v8: Replace safe_conversion() with check_assign() (Kees)

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>
Reviewed-by: Andrzej Hajda <andrzej.hajda@intel.com>
---
 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..53fa27e1c950 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 (check_assign(obj->base.size >> PAGE_SHIFT, &npages))
+		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..339b0a9cf2d0 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 (check_assign(obj->base.size >> PAGE_SHIFT, &page_count))
+		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 4148126e7b1b..d708c4c2a9bd 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
@@ -783,6 +783,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 (check_assign(obj->base.size >> PAGE_SHIFT, &num_pages))
+		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 8423df021b71..48237e443863 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 (check_assign(obj->base.size >> PAGE_SHIFT, &num_pages))
+		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..bc6e823584ad 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 (check_assign(obj->base.size >> PAGE_SHIFT, &page_num))
+		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.37.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v9 5/8] drm/i915: Check for integer truncation on the configuration of ttm place
  2022-08-24  8:45 [PATCH v9 0/8] Fixes integer overflow or integer truncation issues in page lookups, ttm place configuration and scatterlist creation Gwan-gyeong Mun
                   ` (3 preceding siblings ...)
  2022-08-24  8:45 ` [PATCH v9 4/8] drm/i915: Check for integer truncation on scatterlist creation Gwan-gyeong Mun
@ 2022-08-24  8:45 ` Gwan-gyeong Mun
  2022-08-24  8:45 ` [PATCH v9 6/8] drm/i915: Check if the size is too big while creating shmem file Gwan-gyeong Mun
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 17+ messages in thread
From: Gwan-gyeong Mun @ 2022-08-24  8:45 UTC (permalink / raw)
  To: intel-gfx
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, keescook,
	jani.nikula, linux-kernel, dri-devel, chris, airlied,
	andrzej.hajda, matthew.auld, intel-gfx-trybot, mchehab,
	nirmoy.das

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)
v7: Fix to use WARN_ON() macro where GEM_BUG_ON() macro was used. (Jani)
v8: Replace safe_conversion() with check_assign() (Kees)

Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
Cc: Jani Nikula <jani.nikula@intel.com>
Reviewed-by: Nirmoy Das <nirmoy.das@intel.com> (v2)
Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org> (v3)
Reported-by: kernel test robot <lkp@intel.com>
Reviewed-by: Andrzej Hajda <andrzej.hajda@intel.com> (v5)
---
 drivers/gpu/drm/i915/gem/i915_gem_ttm.c |  6 +++---
 drivers/gpu/drm/i915/intel_region_ttm.c | 17 ++++++++++++++---
 2 files changed, 17 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 d708c4c2a9bd..615541b650fa 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);
+		WARN_ON(check_assign(offset >> PAGE_SHIFT, &place->fpfn));
+		WARN_ON(check_assign(place->fpfn + (size >> PAGE_SHIFT), &place->lpfn));
 	} 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;
+			WARN_ON(check_assign(mr->io_size >> PAGE_SHIFT, &place->lpfn));
 		}
 	}
 }
diff --git a/drivers/gpu/drm/i915/intel_region_ttm.c b/drivers/gpu/drm/i915/intel_region_ttm.c
index 575d67bc6ffe..37a964b20b36 100644
--- a/drivers/gpu/drm/i915/intel_region_ttm.c
+++ b/drivers/gpu/drm/i915/intel_region_ttm.c
@@ -209,14 +209,23 @@ 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 (WARN_ON(check_assign(offset >> PAGE_SHIFT, &place.fpfn))) {
+			ret = -E2BIG;
+			goto out;
+		}
+		if (WARN_ON(check_assign(place.fpfn + (size >> PAGE_SHIFT), &place.lpfn))) {
+			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 (WARN_ON(check_assign(mem->io_size >> PAGE_SHIFT, &place.lpfn))) {
+				ret = -E2BIG;
+				goto out;
+			}
 		}
 	}
 
@@ -224,6 +233,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.37.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v9 6/8] drm/i915: Check if the size is too big while creating shmem file
  2022-08-24  8:45 [PATCH v9 0/8] Fixes integer overflow or integer truncation issues in page lookups, ttm place configuration and scatterlist creation Gwan-gyeong Mun
                   ` (4 preceding siblings ...)
  2022-08-24  8:45 ` [PATCH v9 5/8] drm/i915: Check for integer truncation on the configuration of ttm place Gwan-gyeong Mun
@ 2022-08-24  8:45 ` Gwan-gyeong Mun
  2022-08-24  8:45 ` [PATCH v9 7/8] drm/i915: Use error code as -E2BIG when the size of gem ttm object is too large Gwan-gyeong Mun
  2022-08-24  8:45 ` [PATCH v9 8/8] drm/i915: Remove truncation warning for large objects Gwan-gyeong Mun
  7 siblings, 0 replies; 17+ messages in thread
From: Gwan-gyeong Mun @ 2022-08-24  8:45 UTC (permalink / raw)
  To: intel-gfx
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, keescook,
	jani.nikula, linux-kernel, dri-devel, chris, airlied,
	andrzej.hajda, matthew.auld, intel-gfx-trybot, mchehab,
	nirmoy.das

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 <gwan-gyeong.mun@intel.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
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>
Reported-by: kernel test robot <lkp@intel.com>
Reviewed-by: Andrzej Hajda <andrzej.hajda@intel.com>
---
 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 339b0a9cf2d0..ca30060e34ab 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.37.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v9 7/8] drm/i915: Use error code as -E2BIG when the size of gem ttm object is too large
  2022-08-24  8:45 [PATCH v9 0/8] Fixes integer overflow or integer truncation issues in page lookups, ttm place configuration and scatterlist creation Gwan-gyeong Mun
                   ` (5 preceding siblings ...)
  2022-08-24  8:45 ` [PATCH v9 6/8] drm/i915: Check if the size is too big while creating shmem file Gwan-gyeong Mun
@ 2022-08-24  8:45 ` Gwan-gyeong Mun
  2022-08-24  8:45 ` [PATCH v9 8/8] drm/i915: Remove truncation warning for large objects Gwan-gyeong Mun
  7 siblings, 0 replies; 17+ messages in thread
From: Gwan-gyeong Mun @ 2022-08-24  8:45 UTC (permalink / raw)
  To: intel-gfx
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, keescook,
	jani.nikula, linux-kernel, dri-devel, chris, airlied,
	andrzej.hajda, matthew.auld, intel-gfx-trybot, mchehab,
	nirmoy.das

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 <gwan-gyeong.mun@intel.com>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
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>
Reviewed-by: Andrzej Hajda <andrzej.hajda@intel.com>
---
 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 615541b650fa..e3046e02b47a 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
@@ -1210,6 +1210,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.37.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* [PATCH v9 8/8] drm/i915: Remove truncation warning for large objects
  2022-08-24  8:45 [PATCH v9 0/8] Fixes integer overflow or integer truncation issues in page lookups, ttm place configuration and scatterlist creation Gwan-gyeong Mun
                   ` (6 preceding siblings ...)
  2022-08-24  8:45 ` [PATCH v9 7/8] drm/i915: Use error code as -E2BIG when the size of gem ttm object is too large Gwan-gyeong Mun
@ 2022-08-24  8:45 ` Gwan-gyeong Mun
  7 siblings, 0 replies; 17+ messages in thread
From: Gwan-gyeong Mun @ 2022-08-24  8:45 UTC (permalink / raw)
  To: intel-gfx
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, keescook,
	jani.nikula, linux-kernel, dri-devel, chris, airlied,
	andrzej.hajda, matthew.auld, intel-gfx-trybot, mchehab,
	nirmoy.das

From: Chris Wilson <chris@chris-wilson.co.uk>

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 <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>
Testcase: igt@gem_create@create-massive
Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/4991
Reviewed-by: Nirmoy Das <nirmoy.das@intel.com>
Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Reviewed-by: Andrzej Hajda <andrzej.hajda@intel.com>
---
 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.37.1


^ permalink raw reply related	[flat|nested] 17+ messages in thread

* Re: [PATCH v9 1/8] overflow: Move and add few utility macros into overflow
  2022-08-24  8:45 ` [PATCH v9 1/8] overflow: Move and add few utility macros into overflow Gwan-gyeong Mun
@ 2022-08-24 10:06   ` Andrzej Hajda
  2022-08-25 16:47   ` Kees Cook
  1 sibling, 0 replies; 17+ messages in thread
From: Andrzej Hajda @ 2022-08-24 10:06 UTC (permalink / raw)
  To: Gwan-gyeong Mun, intel-gfx
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, keescook,
	jani.nikula, linux-kernel, dri-devel, chris, airlied,
	matthew.auld, intel-gfx-trybot, mchehab, nirmoy.das



On 24.08.2022 10:45, Gwan-gyeong Mun wrote:
> It moves overflows_type utility macro into overflow header from i915_utils
> header. The overflows_type can be used to catch the truncaion (overflow)
> between different data types. And it adds check_assign() macro which
> performs an assigning source value into destination ptr along with an
> overflow check. overflow_type macro has been improved to handle the signbit
> by gcc's built-in overflow check function. And it adds overflows_ptr()
> helper macro for checking the overflows between a value and a pointer
> type/value.
>
> 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
> v8: Add check_assign() and remove safe_conversion() (Kees)
>      Fix overflows_type() to use gcc's built-in overflow function (Andrzej)
>      Add overflows_ptr() to allow overflow checking when assigning a value
>      into a pointer variable (G.G.)
> v9: Fix overflows_type() to use __builtin_add_overflow() instead of
>      __builtin_add_overflow_p() (Andrzej)
>      Fix overflows_ptr() to use overflows_type() with the unsigned long type
>      (Andrzej)
>
> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
> Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
> Cc: Matthew Auld <matthew.auld@intel.com>
> Cc: Nirmoy Das <nirmoy.das@intel.com>
> Cc: Jani Nikula <jani.nikula@intel.com>
> Cc: Andi Shyti <andi.shyti@linux.intel.com>
> Cc: Andrzej Hajda <andrzej.hajda@intel.com>
> Cc: Mauro Carvalho Chehab <mauro.chehab@linux.intel.com>
> Cc: Kees Cook <keescook@chromium.org>
> Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org> (v5)
> ---
>
Reviewed-by: Andrzej Hajda <andrzej.hajda@intel.com>

Regards
Andrzej

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Intel-gfx] [PATCH v9 2/8] util_macros: Add exact_type macro to catch type mis-match while compiling
  2022-08-24  8:45 ` [PATCH v9 2/8] util_macros: Add exact_type macro to catch type mis-match while compiling Gwan-gyeong Mun
@ 2022-08-25  8:04   ` Gwan-gyeong Mun
  2022-08-25 17:19   ` Kees Cook
  1 sibling, 0 replies; 17+ messages in thread
From: Gwan-gyeong Mun @ 2022-08-25  8:04 UTC (permalink / raw)
  To: bgolaszewski
  Cc: thomas.hellstrom, Kees Cook, jani.nikula, intel-gfx,
	linux-kernel, dri-devel, chris, airlied, matthew.auld,
	andrzej.hajda, akpm, mchehab, nirmoy.das

Hi Bartosz Golaszewski,

would you mind taking a look at this patch?

Thanks,
G.G.

On 8/24/22 5:45 PM, Gwan-gyeong Mun wrote:
> It adds exact_type and exactly_pgoff_t macro to catch type mis-match while
> compiling. The existing typecheck() macro outputs build warnings, but the
> newly added exact_type() macro uses the BUILD_BUG_ON() macro to generate
> a build break when the types are different and can be used to detect
> explicit build errors.
> 
> v6: Move macro addition location so that it can be used by other than drm
>      subsystem (Jani, Mauro, Andi)
> 
> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
> Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
> Cc: Matthew Auld <matthew.auld@intel.com>
> Cc: Nirmoy Das <nirmoy.das@intel.com>
> Cc: Jani Nikula <jani.nikula@intel.com>
> Cc: Andi Shyti <andi.shyti@linux.intel.com>
> Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
> ---
>   include/linux/util_macros.h | 25 +++++++++++++++++++++++++
>   1 file changed, 25 insertions(+)
> 
> diff --git a/include/linux/util_macros.h b/include/linux/util_macros.h
> index 72299f261b25..b6624b275257 100644
> --- a/include/linux/util_macros.h
> +++ b/include/linux/util_macros.h
> @@ -2,6 +2,9 @@
>   #ifndef _LINUX_HELPER_MACROS_H_
>   #define _LINUX_HELPER_MACROS_H_
>   
> +#include <linux/types.h>
> +#include <linux/bug.h>
> +
>   #define __find_closest(x, a, as, op)					\
>   ({									\
>   	typeof(as) __fc_i, __fc_as = (as) - 1;				\
> @@ -38,4 +41,26 @@
>    */
>   #define find_closest_descending(x, a, as) __find_closest(x, a, as, >=)
>   
> +/**
> + * exact_type - break compile if source type and destination value's type are
> + * not the same
> + * @T: Source type
> + * @n: Destination value
> + *
> + * It is a helper macro for a poor man's -Wconversion: only allow variables of
> + * an exact type. It determines whether the source type and destination value's
> + * type are the same while compiling, and it breaks compile if two types are
> + * not the same
> + */
> +#define exact_type(T, n) \
> +	BUILD_BUG_ON(!__builtin_constant_p(n) && !__builtin_types_compatible_p(T, typeof(n)))
> +
> +/**
> + * exactly_pgoff_t - helper to check if the type of a value is pgoff_t
> + * @n: value to compare pgoff_t type
> + *
> + * It breaks compile if the argument value's type is not pgoff_t type.
> + */
> +#define exactly_pgoff_t(n) exact_type(pgoff_t, n)
> +
>   #endif

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v9 1/8] overflow: Move and add few utility macros into overflow
  2022-08-24  8:45 ` [PATCH v9 1/8] overflow: Move and add few utility macros into overflow Gwan-gyeong Mun
  2022-08-24 10:06   ` Andrzej Hajda
@ 2022-08-25 16:47   ` Kees Cook
  2022-08-26 13:44     ` [Intel-gfx] " Andrzej Hajda
  2022-09-09 10:50     ` Gwan-gyeong Mun
  1 sibling, 2 replies; 17+ messages in thread
From: Kees Cook @ 2022-08-25 16:47 UTC (permalink / raw)
  To: Gwan-gyeong Mun
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, jani.nikula,
	Nick Desaulniers, intel-gfx, Rasmus Villemoes, linux-kernel,
	dri-devel, chris, airlied, andrzej.hajda, matthew.auld,
	intel-gfx-trybot, mchehab, nirmoy.das

On Wed, Aug 24, 2022 at 05:45:07PM +0900, Gwan-gyeong Mun wrote:
> It moves overflows_type utility macro into overflow header from i915_utils
> header. The overflows_type can be used to catch the truncaion (overflow)
> between different data types. And it adds check_assign() macro which
> performs an assigning source value into destination ptr along with an
> overflow check. overflow_type macro has been improved to handle the signbit
> by gcc's built-in overflow check function. And it adds overflows_ptr()
> helper macro for checking the overflows between a value and a pointer
> type/value.
> 
> 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
> v8: Add check_assign() and remove safe_conversion() (Kees)
>     Fix overflows_type() to use gcc's built-in overflow function (Andrzej)
>     Add overflows_ptr() to allow overflow checking when assigning a value
>     into a pointer variable (G.G.)
> v9: Fix overflows_type() to use __builtin_add_overflow() instead of
>     __builtin_add_overflow_p() (Andrzej)
>     Fix overflows_ptr() to use overflows_type() with the unsigned long type
>     (Andrzej)
> 
> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
> Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
> Cc: Matthew Auld <matthew.auld@intel.com>
> Cc: Nirmoy Das <nirmoy.das@intel.com>
> Cc: Jani Nikula <jani.nikula@intel.com>
> Cc: Andi Shyti <andi.shyti@linux.intel.com>
> Cc: Andrzej Hajda <andrzej.hajda@intel.com>
> Cc: Mauro Carvalho Chehab <mauro.chehab@linux.intel.com>
> Cc: Kees Cook <keescook@chromium.org>
> Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org> (v5)
> ---
>  drivers/gpu/drm/i915/i915_user_extensions.c |  3 +-
>  drivers/gpu/drm/i915/i915_utils.h           |  5 +-
>  include/linux/overflow.h                    | 62 +++++++++++++++++++++
>  3 files changed, 64 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/i915_user_extensions.c b/drivers/gpu/drm/i915/i915_user_extensions.c
> index c822d0aafd2d..6f6b5b910968 100644
> --- a/drivers/gpu/drm/i915/i915_user_extensions.c
> +++ b/drivers/gpu/drm/i915/i915_user_extensions.c
> @@ -50,8 +50,7 @@ int i915_user_extensions(struct i915_user_extension __user *ext,
>  		if (err)
>  			return err;
>  
> -		if (get_user(next, &ext->next_extension) ||
> -		    overflows_type(next, ext))
> +		if (get_user(next, &ext->next_extension) || overflows_ptr(next))
>  			return -EFAULT;
>  
>  		ext = u64_to_user_ptr(next);

I continue to dislike the layers of macros and specialization here.
This is just a fancy version of check_assign():

	if (get_user(next, &ext->next_extension) || check_assign(next, &ext))
		return -EFAULT;

However, the __builtin_*_overflow() family only wants to work on
integral types, so this needs to be slightly expanded:

	uintptr_t kptr;
	...
	if (get_user(next, &ext->next_extension) || check_assign(next, &kptr))
		return -EFAULT;

	ext = (void * __user)kptr;

But, it does seem like the actual problem here is that u64_to_user_ptr()
is not performing the checking? It's used heavily in the drm code.

Is a check_assign_user_ptr() needed?

> 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..6af9df1d67a1 100644
> --- a/include/linux/overflow.h
> +++ b/include/linux/overflow.h
> @@ -52,6 +52,68 @@ static inline bool __must_check __must_check_overflow(bool overflow)
>  	return unlikely(overflow);
>  }
>  
> +/**
> + * overflows_type - helper for checking the overflows between data types or
> + *                  values
> + *
> + * @x: Source value or data type for overflow check
> + * @T: Destination value or data type for overflow check
> + *
> + * It compares the values or data type between the first and second argument to
> + * check whether overflow can occur when assigning the first argument to the
> + * variable of the second argument. Source and Destination can be singned or
> + * unsigned data types. Source and Destination can be different data types.
> + *
> + * Returns:
> + * True if overflow can occur, false otherwise.
> + */
> +#define overflows_type(x, T) __must_check_overflow(({	\
> +	typeof(T) v = 0;				\
> +	__builtin_add_overflow((x), v, &v);		\
> +}))

I'd like to avoid "externalizing" these kinds of checks when the better
path is to catch the issue at operation type (add, sub, mul, assign).
Looking at existing users, I see stuff like:

                if (overflows_type(item.query_id - 1, unsigned long))
                        return -EINVAL;

                func_idx = item.query_id - 1;

This requires too much open-coded checking, IMO. It's better as:

		if (check_assign(item.query_id - 1, &func_idx))
			return -EINVAL;

or other similar:

        if (overflows_type(user->slice_mask, context->slice_mask) ||
	...
        context->slice_mask = user->slice_mask;

and some that don't make sense to me. Why check argument types? Is this
trying to avoid implicit type conversions?

So, if it's _really_ needed, I can live with adding overflows_type().

> +
> +/**
> + * overflows_ptr - helper for checking the occurrence of overflows when a value
> + *                 assigns to  the pointer data type
> + *
> + * @x: Source value for overflow check
> + *
> + * gcc's built-in overflow check functions don't support checking between the
> + * pointer type and non-pointer type. And ILP32 and LP64 have the same bit size
> + * between long and pointer. Therefore it internally compares the source value
> + * and unsigned long data type for checking overflow.
> + *
> + * Returns:
> + * True if overflow can occur, false otherwise.
> + */
> +#define overflows_ptr(x) __must_check_overflow(overflows_type(x, unsigned long))

I'd rather not have this -- it's just a specialized use of
overflows_type(), and only used in 1 place.

> +
> +/**
> + * check_assign - perform an assigning source value into destination ptr along
> + *                with an overflow check.
> + *
> + * @value: Source value
> + * @ptr: Destination pointer address, If the pointer type is not used,
> + *       a warning message is output during build.
> + *
> + * It checks internally the ptr is a pointer type. And it uses gcc's built-in
> + * overflow check function.
> + * It does not use the check_*() wrapper functions, but directly uses gcc's
> + * built-in overflow check function so that it can be used even when
> + * the type of value and the type pointed to by ptr are different without build
> + * warning messages.

This is a good point: the check_{add,sub,mul}_overflow() helpers
currently require all the params be the same type, which rather limits
their usage. Perhaps this can be weakened now that we're not using[1]
the fallback logic any more? (Separate patch.)

> + *
> + * Returns:
> + * If the value would overflow the destination, it returns true. If not return
> + * false. When overflow does not occur, the assigning into ptr from value
> + * succeeds. It follows the return policy as other check_*_overflow() functions
> + * return non-zero as a failure.
> + */
> +#define check_assign(value, ptr) __must_check_overflow(({	\
> +	typecheck_pointer(ptr); 		\
> +	__builtin_add_overflow(0, value, ptr);	\
> +}))

But yes, this looks correct. I really like it. :)

> +
>  /*
>   * For simplicity and code hygiene, the fallback code below insists on
>   * a, b and *d having the same type (similar to the min() and max()
> -- 
> 2.37.1
> 

-Kees

[1] 4eb6bd55cfb2 ("compiler.h: drop fallback overflow checkers")

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v9 2/8] util_macros: Add exact_type macro to catch type mis-match while compiling
  2022-08-24  8:45 ` [PATCH v9 2/8] util_macros: Add exact_type macro to catch type mis-match while compiling Gwan-gyeong Mun
  2022-08-25  8:04   ` [Intel-gfx] " Gwan-gyeong Mun
@ 2022-08-25 17:19   ` Kees Cook
  2022-09-09 10:58     ` Gwan-gyeong Mun
  1 sibling, 1 reply; 17+ messages in thread
From: Kees Cook @ 2022-08-25 17:19 UTC (permalink / raw)
  To: Gwan-gyeong Mun
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, jani.nikula,
	intel-gfx, linux-kernel, dri-devel, chris, airlied,
	andrzej.hajda, matthew.auld, intel-gfx-trybot, mchehab,
	nirmoy.das

On Wed, Aug 24, 2022 at 05:45:08PM +0900, Gwan-gyeong Mun wrote:
> It adds exact_type and exactly_pgoff_t macro to catch type mis-match while
> compiling. The existing typecheck() macro outputs build warnings, but the
> newly added exact_type() macro uses the BUILD_BUG_ON() macro to generate
> a build break when the types are different and can be used to detect
> explicit build errors.
> 
> v6: Move macro addition location so that it can be used by other than drm
>     subsystem (Jani, Mauro, Andi)
> 
> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
> Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
> Cc: Matthew Auld <matthew.auld@intel.com>
> Cc: Nirmoy Das <nirmoy.das@intel.com>
> Cc: Jani Nikula <jani.nikula@intel.com>
> Cc: Andi Shyti <andi.shyti@linux.intel.com>
> Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
> ---
>  include/linux/util_macros.h | 25 +++++++++++++++++++++++++
>  1 file changed, 25 insertions(+)
> 
> diff --git a/include/linux/util_macros.h b/include/linux/util_macros.h
> index 72299f261b25..b6624b275257 100644
> --- a/include/linux/util_macros.h
> +++ b/include/linux/util_macros.h
> @@ -2,6 +2,9 @@
>  #ifndef _LINUX_HELPER_MACROS_H_
>  #define _LINUX_HELPER_MACROS_H_
>  
> +#include <linux/types.h>
> +#include <linux/bug.h>
> +
>  #define __find_closest(x, a, as, op)					\
>  ({									\
>  	typeof(as) __fc_i, __fc_as = (as) - 1;				\
> @@ -38,4 +41,26 @@
>   */
>  #define find_closest_descending(x, a, as) __find_closest(x, a, as, >=)
>  
> +/**
> + * exact_type - break compile if source type and destination value's type are
> + * not the same
> + * @T: Source type
> + * @n: Destination value
> + *
> + * It is a helper macro for a poor man's -Wconversion: only allow variables of
> + * an exact type. It determines whether the source type and destination value's
> + * type are the same while compiling, and it breaks compile if two types are
> + * not the same
> + */
> +#define exact_type(T, n) \
> +	BUILD_BUG_ON(!__builtin_constant_p(n) && !__builtin_types_compatible_p(T, typeof(n)))

Maybe use __same_type() here instead of open-coded
__builtin_types_compatible_p()? Also, IIUC, currently coding style
advise is to use _Static_assert when possible over BUILD_BUG_ON for
error message readability.

This macro has a trap-door for literals, yes?
i.e.  exact_type(pgoff_t, 5) will pass?

I also note that this is very close to the really common (and open-coded)
test scattered around the kernel already (BUILD_BUG_ON(__same_type(a,
b))), so I think it's good to get a macro defined for it, though I'm not
sure about the trap door test. Regardless, I'd like to bikeshed the name
a bit; I think this should be named something a bit more clear about
what happens on failure. Perhaps: assert_type()? Or to capture the
trapdoor idea, assert_typable()?

#define assert_type(t1, t2)	_Static_assert(__same_type(t1, t2))
#define assert_typable(t, n)	_Static_assert(__builtin_constant_p(n) ||
					       __same_type(t, typeof(n))

> +
> +/**
> + * exactly_pgoff_t - helper to check if the type of a value is pgoff_t
> + * @n: value to compare pgoff_t type
> + *
> + * It breaks compile if the argument value's type is not pgoff_t type.
> + */
> +#define exactly_pgoff_t(n) exact_type(pgoff_t, n)

Why specialize this? Just use assert_typable(pgoff_t, n) in the other
patches? It's almost the same amount to write. :)

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Intel-gfx] [PATCH v9 1/8] overflow: Move and add few utility macros into overflow
  2022-08-25 16:47   ` Kees Cook
@ 2022-08-26 13:44     ` Andrzej Hajda
  2022-09-09 10:52       ` Gwan-gyeong Mun
  2022-09-09 10:50     ` Gwan-gyeong Mun
  1 sibling, 1 reply; 17+ messages in thread
From: Andrzej Hajda @ 2022-08-26 13:44 UTC (permalink / raw)
  To: Kees Cook, Gwan-gyeong Mun
  Cc: thomas.hellstrom, jani.nikula, Rasmus Villemoes, intel-gfx,
	Nick Desaulniers, linux-kernel, dri-devel, chris, airlied,
	matthew.auld, intel-gfx-trybot, mchehab, nirmoy.das

On 25.08.2022 18:47, Kees Cook wrote:
> On Wed, Aug 24, 2022 at 05:45:07PM +0900, Gwan-gyeong Mun wrote:
>> It moves overflows_type utility macro into overflow header from i915_utils
>> header. The overflows_type can be used to catch the truncaion (overflow)
>> between different data types. And it adds check_assign() macro which
>> performs an assigning source value into destination ptr along with an
>> overflow check. overflow_type macro has been improved to handle the signbit
>> by gcc's built-in overflow check function. And it adds overflows_ptr()
>> helper macro for checking the overflows between a value and a pointer
>> type/value.
>>
>> 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
>> v8: Add check_assign() and remove safe_conversion() (Kees)
>>      Fix overflows_type() to use gcc's built-in overflow function (Andrzej)
>>      Add overflows_ptr() to allow overflow checking when assigning a value
>>      into a pointer variable (G.G.)
>> v9: Fix overflows_type() to use __builtin_add_overflow() instead of
>>      __builtin_add_overflow_p() (Andrzej)
>>      Fix overflows_ptr() to use overflows_type() with the unsigned long type
>>      (Andrzej)
>>
>> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
>> Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
>> Cc: Matthew Auld <matthew.auld@intel.com>
>> Cc: Nirmoy Das <nirmoy.das@intel.com>
>> Cc: Jani Nikula <jani.nikula@intel.com>
>> Cc: Andi Shyti <andi.shyti@linux.intel.com>
>> Cc: Andrzej Hajda <andrzej.hajda@intel.com>
>> Cc: Mauro Carvalho Chehab <mauro.chehab@linux.intel.com>
>> Cc: Kees Cook <keescook@chromium.org>
>> Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org> (v5)
>> ---
>>   drivers/gpu/drm/i915/i915_user_extensions.c |  3 +-
>>   drivers/gpu/drm/i915/i915_utils.h           |  5 +-
>>   include/linux/overflow.h                    | 62 +++++++++++++++++++++
>>   3 files changed, 64 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i915/i915_user_extensions.c b/drivers/gpu/drm/i915/i915_user_extensions.c
>> index c822d0aafd2d..6f6b5b910968 100644
>> --- a/drivers/gpu/drm/i915/i915_user_extensions.c
>> +++ b/drivers/gpu/drm/i915/i915_user_extensions.c
>> @@ -50,8 +50,7 @@ int i915_user_extensions(struct i915_user_extension __user *ext,
>>   		if (err)
>>   			return err;
>>   
>> -		if (get_user(next, &ext->next_extension) ||
>> -		    overflows_type(next, ext))
>> +		if (get_user(next, &ext->next_extension) || overflows_ptr(next))
>>   			return -EFAULT;
>>   
>>   		ext = u64_to_user_ptr(next);
> 
> I continue to dislike the layers of macros and specialization here.
> This is just a fancy version of check_assign():
> 
> 	if (get_user(next, &ext->next_extension) || check_assign(next, &ext))
> 		return -EFAULT;
> 
> However, the __builtin_*_overflow() family only wants to work on
> integral types, so this needs to be slightly expanded:
> 
> 	uintptr_t kptr;
> 	...
> 	if (get_user(next, &ext->next_extension) || check_assign(next, &kptr))
> 		return -EFAULT;
> 
> 	ext = (void * __user)kptr;
> 
> But, it does seem like the actual problem here is that u64_to_user_ptr()
> is not performing the checking? It's used heavily in the drm code.
> 
> Is a check_assign_user_ptr() needed?
> 
>> 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..6af9df1d67a1 100644
>> --- a/include/linux/overflow.h
>> +++ b/include/linux/overflow.h
>> @@ -52,6 +52,68 @@ static inline bool __must_check __must_check_overflow(bool overflow)
>>   	return unlikely(overflow);
>>   }
>>   
>> +/**
>> + * overflows_type - helper for checking the overflows between data types or
>> + *                  values
>> + *
>> + * @x: Source value or data type for overflow check
>> + * @T: Destination value or data type for overflow check
>> + *
>> + * It compares the values or data type between the first and second argument to
>> + * check whether overflow can occur when assigning the first argument to the
>> + * variable of the second argument. Source and Destination can be singned or
>> + * unsigned data types. Source and Destination can be different data types.
>> + *
>> + * Returns:
>> + * True if overflow can occur, false otherwise.
>> + */
>> +#define overflows_type(x, T) __must_check_overflow(({	\
>> +	typeof(T) v = 0;				\
>> +	__builtin_add_overflow((x), v, &v);		\
>> +}))
> 
> I'd like to avoid "externalizing" these kinds of checks when the better
> path is to catch the issue at operation type (add, sub, mul, assign).
> Looking at existing users, I see stuff like:
> 
>                  if (overflows_type(item.query_id - 1, unsigned long))
>                          return -EINVAL;
> 
>                  func_idx = item.query_id - 1;
> 
> This requires too much open-coded checking, IMO. It's better as:
> 
> 		if (check_assign(item.query_id - 1, &func_idx))
> 			return -EINVAL;
> 
> or other similar:
> 
>          if (overflows_type(user->slice_mask, context->slice_mask) ||
> 	...
>          context->slice_mask = user->slice_mask;
> 
> and some that don't make sense to me. Why check argument types? Is this
> trying to avoid implicit type conversions?
> 
> So, if it's _really_ needed, I can live with adding overflows_type().
> 
>> +
>> +/**
>> + * overflows_ptr - helper for checking the occurrence of overflows when a value
>> + *                 assigns to  the pointer data type
>> + *
>> + * @x: Source value for overflow check
>> + *
>> + * gcc's built-in overflow check functions don't support checking between the
>> + * pointer type and non-pointer type. And ILP32 and LP64 have the same bit size
>> + * between long and pointer. Therefore it internally compares the source value
>> + * and unsigned long data type for checking overflow.
>> + *
>> + * Returns:
>> + * True if overflow can occur, false otherwise.
>> + */
>> +#define overflows_ptr(x) __must_check_overflow(overflows_type(x, unsigned long))
> 
> I'd rather not have this -- it's just a specialized use of
> overflows_type(), and only used in 1 place.
> 
>> +
>> +/**
>> + * check_assign - perform an assigning source value into destination ptr along
>> + *                with an overflow check.
>> + *
>> + * @value: Source value
>> + * @ptr: Destination pointer address, If the pointer type is not used,
>> + *       a warning message is output during build.
>> + *
>> + * It checks internally the ptr is a pointer type. And it uses gcc's built-in
>> + * overflow check function.
>> + * It does not use the check_*() wrapper functions, but directly uses gcc's
>> + * built-in overflow check function so that it can be used even when
>> + * the type of value and the type pointed to by ptr are different without build
>> + * warning messages.
> 
> This is a good point: the check_{add,sub,mul}_overflow() helpers
> currently require all the params be the same type, which rather limits
> their usage. Perhaps this can be weakened now that we're not using[1]
> the fallback logic any more? (Separate patch.)
> 
>> + *
>> + * Returns:
>> + * If the value would overflow the destination, it returns true. If not return
>> + * false. When overflow does not occur, the assigning into ptr from value
>> + * succeeds. It follows the return policy as other check_*_overflow() functions
>> + * return non-zero as a failure.
>> + */
>> +#define check_assign(value, ptr) __must_check_overflow(({	\
>> +	typecheck_pointer(ptr); 		\
>> +	__builtin_add_overflow(0, value, ptr);	\
>> +}))
> 
> But yes, this looks correct. I really like it. :)


One more thing, I suspect __builtin_add_overflow checks already if ptr 
is pointer, so typecheck_pointer seems redundant.

Regards
Andrzej


> 
>> +
>>   /*
>>    * For simplicity and code hygiene, the fallback code below insists on
>>    * a, b and *d having the same type (similar to the min() and max()
>> -- 
>> 2.37.1
>>
> 
> -Kees
> 
> [1] 4eb6bd55cfb2 ("compiler.h: drop fallback overflow checkers")
> 


^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v9 1/8] overflow: Move and add few utility macros into overflow
  2022-08-25 16:47   ` Kees Cook
  2022-08-26 13:44     ` [Intel-gfx] " Andrzej Hajda
@ 2022-09-09 10:50     ` Gwan-gyeong Mun
  1 sibling, 0 replies; 17+ messages in thread
From: Gwan-gyeong Mun @ 2022-09-09 10:50 UTC (permalink / raw)
  To: Kees Cook
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, jani.nikula,
	Nick Desaulniers, intel-gfx, Rasmus Villemoes, linux-kernel,
	dri-devel, chris, andrzej.hajda, matthew.auld, intel-gfx-trybot,
	mchehab, nirmoy.das



On 8/26/22 1:47 AM, Kees Cook wrote:
> On Wed, Aug 24, 2022 at 05:45:07PM +0900, Gwan-gyeong Mun wrote:
>> It moves overflows_type utility macro into overflow header from i915_utils
>> header. The overflows_type can be used to catch the truncaion (overflow)
>> between different data types. And it adds check_assign() macro which
>> performs an assigning source value into destination ptr along with an
>> overflow check. overflow_type macro has been improved to handle the signbit
>> by gcc's built-in overflow check function. And it adds overflows_ptr()
>> helper macro for checking the overflows between a value and a pointer
>> type/value.
>>
>> 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
>> v8: Add check_assign() and remove safe_conversion() (Kees)
>>      Fix overflows_type() to use gcc's built-in overflow function (Andrzej)
>>      Add overflows_ptr() to allow overflow checking when assigning a value
>>      into a pointer variable (G.G.)
>> v9: Fix overflows_type() to use __builtin_add_overflow() instead of
>>      __builtin_add_overflow_p() (Andrzej)
>>      Fix overflows_ptr() to use overflows_type() with the unsigned long type
>>      (Andrzej)
>>
>> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
>> Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
>> Cc: Matthew Auld <matthew.auld@intel.com>
>> Cc: Nirmoy Das <nirmoy.das@intel.com>
>> Cc: Jani Nikula <jani.nikula@intel.com>
>> Cc: Andi Shyti <andi.shyti@linux.intel.com>
>> Cc: Andrzej Hajda <andrzej.hajda@intel.com>
>> Cc: Mauro Carvalho Chehab <mauro.chehab@linux.intel.com>
>> Cc: Kees Cook <keescook@chromium.org>
>> Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org> (v5)
>> ---
>>   drivers/gpu/drm/i915/i915_user_extensions.c |  3 +-
>>   drivers/gpu/drm/i915/i915_utils.h           |  5 +-
>>   include/linux/overflow.h                    | 62 +++++++++++++++++++++
>>   3 files changed, 64 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i915/i915_user_extensions.c b/drivers/gpu/drm/i915/i915_user_extensions.c
>> index c822d0aafd2d..6f6b5b910968 100644
>> --- a/drivers/gpu/drm/i915/i915_user_extensions.c
>> +++ b/drivers/gpu/drm/i915/i915_user_extensions.c
>> @@ -50,8 +50,7 @@ int i915_user_extensions(struct i915_user_extension __user *ext,
>>   		if (err)
>>   			return err;
>>   
>> -		if (get_user(next, &ext->next_extension) ||
>> -		    overflows_type(next, ext))
>> +		if (get_user(next, &ext->next_extension) || overflows_ptr(next))
>>   			return -EFAULT;
>>   
>>   		ext = u64_to_user_ptr(next);
> 
> I continue to dislike the layers of macros and specialization here.
> This is just a fancy version of check_assign():
> 
> 	if (get_user(next, &ext->next_extension) || check_assign(next, &ext))
> 		return -EFAULT;
> 
> However, the __builtin_*_overflow() family only wants to work on
> integral types, so this needs to be slightly expanded:
> 
> 	uintptr_t kptr;
> 	...
> 	if (get_user(next, &ext->next_extension) || check_assign(next, &kptr))
> 		return -EFAULT;
> 
> 	ext = (void * __user)kptr;
> 
> But, it does seem like the actual problem here is that u64_to_user_ptr()
> is not performing the checking? It's used heavily in the drm code.
> 
> Is a check_assign_user_ptr() needed?
Hi Kees,
Yes, the reason that an additional overflow check is performed when 
assigning a pointer type is that no overflow check is provided when 
using u64_to_user_ptr())
I also agree with the explicit overflow check when assigning to pointer 
type variables.
I'll add check_assign_user_ptr() to the new version of the patch and use it.
> 
>> 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..6af9df1d67a1 100644
>> --- a/include/linux/overflow.h
>> +++ b/include/linux/overflow.h
>> @@ -52,6 +52,68 @@ static inline bool __must_check __must_check_overflow(bool overflow)
>>   	return unlikely(overflow);
>>   }
>>   
>> +/**
>> + * overflows_type - helper for checking the overflows between data types or
>> + *                  values
>> + *
>> + * @x: Source value or data type for overflow check
>> + * @T: Destination value or data type for overflow check
>> + *
>> + * It compares the values or data type between the first and second argument to
>> + * check whether overflow can occur when assigning the first argument to the
>> + * variable of the second argument. Source and Destination can be singned or
>> + * unsigned data types. Source and Destination can be different data types.
>> + *
>> + * Returns:
>> + * True if overflow can occur, false otherwise.
>> + */
>> +#define overflows_type(x, T) __must_check_overflow(({	\
>> +	typeof(T) v = 0;				\
>> +	__builtin_add_overflow((x), v, &v);		\
>> +}))
> 
> I'd like to avoid "externalizing" these kinds of checks when the better
> path is to catch the issue at operation type (add, sub, mul, assign).
> Looking at existing users, I see stuff like:
> 
I'll update overflows_type() with updated  check_add_overflow()
( https://lore.kernel.org/all/202208311040.C6CA8253@keescook )
>                  if (overflows_type(item.query_id - 1, unsigned long))
>                          return -EINVAL;
> 
>                  func_idx = item.query_id - 1;
> 
> This requires too much open-coded checking, IMO. It's better as:
> 
> 		if (check_assign(item.query_id - 1, &func_idx))
> 			return -EINVAL;
> 
> or other similar:
> 
>          if (overflows_type(user->slice_mask, context->slice_mask) ||
> 	...
>          context->slice_mask = user->slice_mask;
> 
> and some that don't make sense to me. Why check argument types? Is this
> trying to avoid implicit type conversions?
> 
> So, if it's _really_ needed, I can live with adding overflows_type().
> 
thanks for checking in detail.
Yes, it is thought that the above-mentioned codes can be modified using 
check_assign(). And it seems that it can be applied as an additional 
patch after check_assign() is applied.

And the code in the form below

if (overflows_type(size, obj->base.size))
		return ERR_PTR(-E2BIG);

It seems that it can be fixed by updating type_max/type_min/__type_half_max.

#define __type_half_max(x) (((typeof(x))1) << (BITS_PER_TYPE(x) - 1 - 
is_signed_type(x)))
#define type_max(x) ((typeof(x))((__type_half_max(x) - 1) + 
__type_half_max(x)))
#define type_min(x) ((typeof(x))((typeof(x))-type_max(x)-(typeof(x))1))

   if (size > type_max(obj->base.size))
	return ERR_PTR(-E2BIG);

And when a type is used as an argument, it is used for comparison with 
the size used internally (not directly exposed outside the data 
structure or accessible through pointer referencing in multiple steps). 
It seems that max_type can be used in these cases as well, but when it 
is used to check before calling a function that actually allocates 
memory using overflows_type(), it seems that readability( or intuitively 
understood)can be good.

And in the alloc_table(), the function calls 
"GEM_BUG_ON(overflows_type(count * PAGE_SIZE, sg->length));", it is a 
case of checking and creating while looping. In this case, 
overflows_type() seems convenient.

In all the confirmed cases by me, it is possible to avoid using 
overflows_type() by changing or modifying the code in a different form.

In all the confirmed cases by me, it is possible to avoid using 
overflows_type() by changing or modifying the code in a different form. 
but there are also codes that can be easily or intuitively understood 
when added, so in my opinion, it would be good to add overflows_type() 
macro.
>> +
>> +/**
>> + * overflows_ptr - helper for checking the occurrence of overflows when a value
>> + *                 assigns to  the pointer data type
>> + *
>> + * @x: Source value for overflow check
>> + *
>> + * gcc's built-in overflow check functions don't support checking between the
>> + * pointer type and non-pointer type. And ILP32 and LP64 have the same bit size
>> + * between long and pointer. Therefore it internally compares the source value
>> + * and unsigned long data type for checking overflow.
>> + *
>> + * Returns:
>> + * True if overflow can occur, false otherwise.
>> + */
>> +#define overflows_ptr(x) __must_check_overflow(overflows_type(x, unsigned long))
> 
> I'd rather not have this -- it's just a specialized use of
> overflows_type(), and only used in 1 place.
> 
okay I'll drop it.
>> +
>> +/**
>> + * check_assign - perform an assigning source value into destination ptr along
>> + *                with an overflow check.
>> + *
>> + * @value: Source value
>> + * @ptr: Destination pointer address, If the pointer type is not used,
>> + *       a warning message is output during build.
>> + *
>> + * It checks internally the ptr is a pointer type. And it uses gcc's built-in
>> + * overflow check function.
>> + * It does not use the check_*() wrapper functions, but directly uses gcc's
>> + * built-in overflow check function so that it can be used even when
>> + * the type of value and the type pointed to by ptr are different without build
>> + * warning messages.
> 
> This is a good point: the check_{add,sub,mul}_overflow() helpers
> currently require all the params be the same type, which rather limits
> their usage. Perhaps this can be weakened now that we're not using[1]
> the fallback logic any more? (Separate patch.)
> 
>> + *
>> + * Returns:
>> + * If the value would overflow the destination, it returns true. If not return
>> + * false. When overflow does not occur, the assigning into ptr from value
>> + * succeeds. It follows the return policy as other check_*_overflow() functions
>> + * return non-zero as a failure.
>> + */
>> +#define check_assign(value, ptr) __must_check_overflow(({	\
>> +	typecheck_pointer(ptr); 		\
>> +	__builtin_add_overflow(0, value, ptr);	\
>> +}))
> 
> But yes, this looks correct. I really like it. :)
> 
>> +
>>   /*
>>    * For simplicity and code hygiene, the fallback code below insists on
>>    * a, b and *d having the same type (similar to the min() and max()
>> -- 
>> 2.37.1
>>
> 
> -Kees
> 
> [1] 4eb6bd55cfb2 ("compiler.h: drop fallback overflow checkers")
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [Intel-gfx] [PATCH v9 1/8] overflow: Move and add few utility macros into overflow
  2022-08-26 13:44     ` [Intel-gfx] " Andrzej Hajda
@ 2022-09-09 10:52       ` Gwan-gyeong Mun
  0 siblings, 0 replies; 17+ messages in thread
From: Gwan-gyeong Mun @ 2022-09-09 10:52 UTC (permalink / raw)
  To: Andrzej Hajda, Kees Cook
  Cc: thomas.hellstrom, jani.nikula, Rasmus Villemoes, intel-gfx,
	Nick Desaulniers, linux-kernel, dri-devel, chris, airlied,
	matthew.auld, intel-gfx-trybot, mchehab, nirmoy.das



On 8/26/22 10:44 PM, Andrzej Hajda wrote:
> On 25.08.2022 18:47, Kees Cook wrote:
>> On Wed, Aug 24, 2022 at 05:45:07PM +0900, Gwan-gyeong Mun wrote:
>>> It moves overflows_type utility macro into overflow header from 
>>> i915_utils
>>> header. The overflows_type can be used to catch the truncaion (overflow)
>>> between different data types. And it adds check_assign() macro which
>>> performs an assigning source value into destination ptr along with an
>>> overflow check. overflow_type macro has been improved to handle the 
>>> signbit
>>> by gcc's built-in overflow check function. And it adds overflows_ptr()
>>> helper macro for checking the overflows between a value and a pointer
>>> type/value.
>>>
>>> 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
>>> v8: Add check_assign() and remove safe_conversion() (Kees)
>>>      Fix overflows_type() to use gcc's built-in overflow function 
>>> (Andrzej)
>>>      Add overflows_ptr() to allow overflow checking when assigning a 
>>> value
>>>      into a pointer variable (G.G.)
>>> v9: Fix overflows_type() to use __builtin_add_overflow() instead of
>>>      __builtin_add_overflow_p() (Andrzej)
>>>      Fix overflows_ptr() to use overflows_type() with the unsigned 
>>> long type
>>>      (Andrzej)
>>>
>>> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
>>> Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
>>> Cc: Matthew Auld <matthew.auld@intel.com>
>>> Cc: Nirmoy Das <nirmoy.das@intel.com>
>>> Cc: Jani Nikula <jani.nikula@intel.com>
>>> Cc: Andi Shyti <andi.shyti@linux.intel.com>
>>> Cc: Andrzej Hajda <andrzej.hajda@intel.com>
>>> Cc: Mauro Carvalho Chehab <mauro.chehab@linux.intel.com>
>>> Cc: Kees Cook <keescook@chromium.org>
>>> Reviewed-by: Mauro Carvalho Chehab <mchehab@kernel.org> (v5)
>>> ---
>>>   drivers/gpu/drm/i915/i915_user_extensions.c |  3 +-
>>>   drivers/gpu/drm/i915/i915_utils.h           |  5 +-
>>>   include/linux/overflow.h                    | 62 +++++++++++++++++++++
>>>   3 files changed, 64 insertions(+), 6 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/i915/i915_user_extensions.c 
>>> b/drivers/gpu/drm/i915/i915_user_extensions.c
>>> index c822d0aafd2d..6f6b5b910968 100644
>>> --- a/drivers/gpu/drm/i915/i915_user_extensions.c
>>> +++ b/drivers/gpu/drm/i915/i915_user_extensions.c
>>> @@ -50,8 +50,7 @@ int i915_user_extensions(struct i915_user_extension 
>>> __user *ext,
>>>           if (err)
>>>               return err;
>>> -        if (get_user(next, &ext->next_extension) ||
>>> -            overflows_type(next, ext))
>>> +        if (get_user(next, &ext->next_extension) || 
>>> overflows_ptr(next))
>>>               return -EFAULT;
>>>           ext = u64_to_user_ptr(next);
>>
>> I continue to dislike the layers of macros and specialization here.
>> This is just a fancy version of check_assign():
>>
>>     if (get_user(next, &ext->next_extension) || check_assign(next, &ext))
>>         return -EFAULT;
>>
>> However, the __builtin_*_overflow() family only wants to work on
>> integral types, so this needs to be slightly expanded:
>>
>>     uintptr_t kptr;
>>     ...
>>     if (get_user(next, &ext->next_extension) || check_assign(next, 
>> &kptr))
>>         return -EFAULT;
>>
>>     ext = (void * __user)kptr;
>>
>> But, it does seem like the actual problem here is that u64_to_user_ptr()
>> is not performing the checking? It's used heavily in the drm code.
>>
>> Is a check_assign_user_ptr() needed?
>>
>>> 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..6af9df1d67a1 100644
>>> --- a/include/linux/overflow.h
>>> +++ b/include/linux/overflow.h
>>> @@ -52,6 +52,68 @@ static inline bool __must_check 
>>> __must_check_overflow(bool overflow)
>>>       return unlikely(overflow);
>>>   }
>>> +/**
>>> + * overflows_type - helper for checking the overflows between data 
>>> types or
>>> + *                  values
>>> + *
>>> + * @x: Source value or data type for overflow check
>>> + * @T: Destination value or data type for overflow check
>>> + *
>>> + * It compares the values or data type between the first and second 
>>> argument to
>>> + * check whether overflow can occur when assigning the first 
>>> argument to the
>>> + * variable of the second argument. Source and Destination can be 
>>> singned or
>>> + * unsigned data types. Source and Destination can be different data 
>>> types.
>>> + *
>>> + * Returns:
>>> + * True if overflow can occur, false otherwise.
>>> + */
>>> +#define overflows_type(x, T) __must_check_overflow(({    \
>>> +    typeof(T) v = 0;                \
>>> +    __builtin_add_overflow((x), v, &v);        \
>>> +}))
>>
>> I'd like to avoid "externalizing" these kinds of checks when the better
>> path is to catch the issue at operation type (add, sub, mul, assign).
>> Looking at existing users, I see stuff like:
>>
>>                  if (overflows_type(item.query_id - 1, unsigned long))
>>                          return -EINVAL;
>>
>>                  func_idx = item.query_id - 1;
>>
>> This requires too much open-coded checking, IMO. It's better as:
>>
>>         if (check_assign(item.query_id - 1, &func_idx))
>>             return -EINVAL;
>>
>> or other similar:
>>
>>          if (overflows_type(user->slice_mask, context->slice_mask) ||
>>     ...
>>          context->slice_mask = user->slice_mask;
>>
>> and some that don't make sense to me. Why check argument types? Is this
>> trying to avoid implicit type conversions?
>>
>> So, if it's _really_ needed, I can live with adding overflows_type().
>>
>>> +
>>> +/**
>>> + * overflows_ptr - helper for checking the occurrence of overflows 
>>> when a value
>>> + *                 assigns to  the pointer data type
>>> + *
>>> + * @x: Source value for overflow check
>>> + *
>>> + * gcc's built-in overflow check functions don't support checking 
>>> between the
>>> + * pointer type and non-pointer type. And ILP32 and LP64 have the 
>>> same bit size
>>> + * between long and pointer. Therefore it internally compares the 
>>> source value
>>> + * and unsigned long data type for checking overflow.
>>> + *
>>> + * Returns:
>>> + * True if overflow can occur, false otherwise.
>>> + */
>>> +#define overflows_ptr(x) __must_check_overflow(overflows_type(x, 
>>> unsigned long))
>>
>> I'd rather not have this -- it's just a specialized use of
>> overflows_type(), and only used in 1 place.
>>
>>> +
>>> +/**
>>> + * check_assign - perform an assigning source value into destination 
>>> ptr along
>>> + *                with an overflow check.
>>> + *
>>> + * @value: Source value
>>> + * @ptr: Destination pointer address, If the pointer type is not used,
>>> + *       a warning message is output during build.
>>> + *
>>> + * It checks internally the ptr is a pointer type. And it uses gcc's 
>>> built-in
>>> + * overflow check function.
>>> + * It does not use the check_*() wrapper functions, but directly 
>>> uses gcc's
>>> + * built-in overflow check function so that it can be used even when
>>> + * the type of value and the type pointed to by ptr are different 
>>> without build
>>> + * warning messages.
>>
>> This is a good point: the check_{add,sub,mul}_overflow() helpers
>> currently require all the params be the same type, which rather limits
>> their usage. Perhaps this can be weakened now that we're not using[1]
>> the fallback logic any more? (Separate patch.)
>>
>>> + *
>>> + * Returns:
>>> + * If the value would overflow the destination, it returns true. If 
>>> not return
>>> + * false. When overflow does not occur, the assigning into ptr from 
>>> value
>>> + * succeeds. It follows the return policy as other 
>>> check_*_overflow() functions
>>> + * return non-zero as a failure.
>>> + */
>>> +#define check_assign(value, ptr) __must_check_overflow(({    \
>>> +    typecheck_pointer(ptr);         \
>>> +    __builtin_add_overflow(0, value, ptr);    \
>>> +}))
>>
>> But yes, this looks correct. I really like it. :)
> 
> 
> One more thing, I suspect __builtin_add_overflow checks already if ptr 
> is pointer, so typecheck_pointer seems redundant.
> 
thanks for check in detail.
I'll remove redundant code and send new version.

> Regards
> Andrzej
> 
> 
>>
>>> +
>>>   /*
>>>    * For simplicity and code hygiene, the fallback code below insists on
>>>    * a, b and *d having the same type (similar to the min() and max()
>>> -- 
>>> 2.37.1
>>>
>>
>> -Kees
>>
>> [1] 4eb6bd55cfb2 ("compiler.h: drop fallback overflow checkers")
>>
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: [PATCH v9 2/8] util_macros: Add exact_type macro to catch type mis-match while compiling
  2022-08-25 17:19   ` Kees Cook
@ 2022-09-09 10:58     ` Gwan-gyeong Mun
  0 siblings, 0 replies; 17+ messages in thread
From: Gwan-gyeong Mun @ 2022-09-09 10:58 UTC (permalink / raw)
  To: Kees Cook
  Cc: thomas.hellstrom, mauro.chehab, andi.shyti, jani.nikula,
	intel-gfx, linux-kernel, dri-devel, chris, airlied,
	andrzej.hajda, matthew.auld, intel-gfx-trybot, mchehab,
	nirmoy.das



On 8/26/22 2:19 AM, Kees Cook wrote:
> On Wed, Aug 24, 2022 at 05:45:08PM +0900, Gwan-gyeong Mun wrote:
>> It adds exact_type and exactly_pgoff_t macro to catch type mis-match while
>> compiling. The existing typecheck() macro outputs build warnings, but the
>> newly added exact_type() macro uses the BUILD_BUG_ON() macro to generate
>> a build break when the types are different and can be used to detect
>> explicit build errors.
>>
>> v6: Move macro addition location so that it can be used by other than drm
>>      subsystem (Jani, Mauro, Andi)
>>
>> Signed-off-by: Gwan-gyeong Mun <gwan-gyeong.mun@intel.com>
>> Cc: Thomas Hellström <thomas.hellstrom@linux.intel.com>
>> Cc: Matthew Auld <matthew.auld@intel.com>
>> Cc: Nirmoy Das <nirmoy.das@intel.com>
>> Cc: Jani Nikula <jani.nikula@intel.com>
>> Cc: Andi Shyti <andi.shyti@linux.intel.com>
>> Cc: Mauro Carvalho Chehab <mchehab@kernel.org>
>> ---
>>   include/linux/util_macros.h | 25 +++++++++++++++++++++++++
>>   1 file changed, 25 insertions(+)
>>
>> diff --git a/include/linux/util_macros.h b/include/linux/util_macros.h
>> index 72299f261b25..b6624b275257 100644
>> --- a/include/linux/util_macros.h
>> +++ b/include/linux/util_macros.h
>> @@ -2,6 +2,9 @@
>>   #ifndef _LINUX_HELPER_MACROS_H_
>>   #define _LINUX_HELPER_MACROS_H_
>>   
>> +#include <linux/types.h>
>> +#include <linux/bug.h>
>> +
>>   #define __find_closest(x, a, as, op)					\
>>   ({									\
>>   	typeof(as) __fc_i, __fc_as = (as) - 1;				\
>> @@ -38,4 +41,26 @@
>>    */
>>   #define find_closest_descending(x, a, as) __find_closest(x, a, as, >=)
>>   
>> +/**
>> + * exact_type - break compile if source type and destination value's type are
>> + * not the same
>> + * @T: Source type
>> + * @n: Destination value
>> + *
>> + * It is a helper macro for a poor man's -Wconversion: only allow variables of
>> + * an exact type. It determines whether the source type and destination value's
>> + * type are the same while compiling, and it breaks compile if two types are
>> + * not the same
>> + */
>> +#define exact_type(T, n) \
>> +	BUILD_BUG_ON(!__builtin_constant_p(n) && !__builtin_types_compatible_p(T, typeof(n)))
> 
> Maybe use __same_type() here instead of open-coded
> __builtin_types_compatible_p()? Also, IIUC, currently coding style
> advise is to use _Static_assert when possible over BUILD_BUG_ON for
> error message readability.
> 
> This macro has a trap-door for literals, yes?
> i.e.  exact_type(pgoff_t, 5) will pass?
> 
yes, I will update in detail comments about trap-door that may occur 
when using constant value.

> I also note that this is very close to the really common (and open-coded)
> test scattered around the kernel already (BUILD_BUG_ON(__same_type(a,
> b))), so I think it's good to get a macro defined for it, though I'm not
> sure about the trap door test. Regardless, I'd like to bikeshed the name
> a bit; I think this should be named something a bit more clear about
> what happens on failure. Perhaps: assert_type()? Or to capture the
> trapdoor idea, assert_typable()?
> 
> #define assert_type(t1, t2)	_Static_assert(__same_type(t1, t2))
> #define assert_typable(t, n)	_Static_assert(__builtin_constant_p(n) ||
> 					       __same_type(t, typeof(n))

The form of the assert_type() / assert_typable() macros you suggested 
looks better to me, so I will add these macros to the header where 
__same_type() is defined and will send a new version of the patch.

many thanks
> 
>> +
>> +/**
>> + * exactly_pgoff_t - helper to check if the type of a value is pgoff_t
>> + * @n: value to compare pgoff_t type
>> + *
>> + * It breaks compile if the argument value's type is not pgoff_t type.
>> + */
>> +#define exactly_pgoff_t(n) exact_type(pgoff_t, n)
> 
> Why specialize this? Just use assert_typable(pgoff_t, n) in the other
> patches? It's almost the same amount to write. :)
> 

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2022-09-09 10:59 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-24  8:45 [PATCH v9 0/8] Fixes integer overflow or integer truncation issues in page lookups, ttm place configuration and scatterlist creation Gwan-gyeong Mun
2022-08-24  8:45 ` [PATCH v9 1/8] overflow: Move and add few utility macros into overflow Gwan-gyeong Mun
2022-08-24 10:06   ` Andrzej Hajda
2022-08-25 16:47   ` Kees Cook
2022-08-26 13:44     ` [Intel-gfx] " Andrzej Hajda
2022-09-09 10:52       ` Gwan-gyeong Mun
2022-09-09 10:50     ` Gwan-gyeong Mun
2022-08-24  8:45 ` [PATCH v9 2/8] util_macros: Add exact_type macro to catch type mis-match while compiling Gwan-gyeong Mun
2022-08-25  8:04   ` [Intel-gfx] " Gwan-gyeong Mun
2022-08-25 17:19   ` Kees Cook
2022-09-09 10:58     ` Gwan-gyeong Mun
2022-08-24  8:45 ` [PATCH v9 3/8] drm/i915/gem: Typecheck page lookups Gwan-gyeong Mun
2022-08-24  8:45 ` [PATCH v9 4/8] drm/i915: Check for integer truncation on scatterlist creation Gwan-gyeong Mun
2022-08-24  8:45 ` [PATCH v9 5/8] drm/i915: Check for integer truncation on the configuration of ttm place Gwan-gyeong Mun
2022-08-24  8:45 ` [PATCH v9 6/8] drm/i915: Check if the size is too big while creating shmem file Gwan-gyeong Mun
2022-08-24  8:45 ` [PATCH v9 7/8] drm/i915: Use error code as -E2BIG when the size of gem ttm object is too large Gwan-gyeong Mun
2022-08-24  8:45 ` [PATCH v9 8/8] drm/i915: Remove truncation warning for large objects Gwan-gyeong Mun

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).