2022-08-16 10:23:19

by Gwan-gyeong Mun

[permalink] [raw]
Subject: [PATCH v7 0/8] Fixes integer overflow or integer truncation issues in page lookups, ttm place configuration and scatterlist creation

This patch series fixes integer overflow or integer truncation issues in
page lookups, ttm place configuration and scatterlist creation, etc.
We need to check that we avoid integer overflows when looking up a page,
and so fix all the instances where we have mistakenly used a plain integer
instead of a more suitable long.
And there is an impedance mismatch between the scatterlist API using
unsigned int and our memory/page accounting in unsigned long. That is we
may try to create a scatterlist for a large object that overflows returning
a small table into which we try to fit very many pages. As the object size
is under the control of userspace, we have to be prudent and catch the
conversion errors. To catch the implicit truncation as we switch from
unsigned long into the scatterlist's unsigned int, we use our overflows_type
check and report E2BIG prior to the operation. This is already used in
our create ioctls to indicate if the uABI request is simply too large for
the backing store.
And ttm place also has the same problem with scatterlist creation,
and we fix the integer truncation problem with the way approached by
scatterlist creation.
And It corrects the error code to return -E2BIG when creating gem objects
using ttm or shmem, if the size is too large in each case.
In order to provide a common macro, it moves and adds a few utility macros
into overflow/util_macros header

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 <[email protected]>)
Add kernel-doc markups to the kAPI functions and macros (Mauoro)
v3: Modify overflows_type() macro to consider signed data types and
add is_type_unsigned() macro (Mauro)
Make not use the same macro name on a function. (Mauro)
For kernel-doc, macros and functions are handled in the same namespace,
the same macro name on a function prevents ever adding documentation for it.
Not to change execution inside a macro. (Mauro)
Fix the problem that safe_conversion() macro always returns true (G.G)
Add safe_conversion_gem_bug_on() macro and remove temporal SAFE_CONVERSION() macro. (G.G.)

Chris Wilson (3):
drm/i915/gem: Typecheck page lookups
drm/i915: Check for integer truncation on scatterlist creation
drm/i915: Remove truncation warning for large objects

Gwan-gyeong Mun (5):
overflow: Move and add few utility macros into overflow
util_macros: Add exact_type macro to catch type mis-match while
compiling
drm/i915: Check for integer truncation on the configuration of ttm
place
drm/i915: Check if the size is too big while creating shmem file
drm/i915: Use error code as -E2BIG when the size of gem ttm object is
too large

drivers/gpu/drm/i915/gem/i915_gem_internal.c | 6 +-
drivers/gpu/drm/i915/gem/i915_gem_object.c | 7 +-
drivers/gpu/drm/i915/gem/i915_gem_object.h | 303 +++++++++++++++---
drivers/gpu/drm/i915/gem/i915_gem_pages.c | 27 +-
drivers/gpu/drm/i915/gem/i915_gem_phys.c | 4 +
drivers/gpu/drm/i915/gem/i915_gem_shmem.c | 19 +-
drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 23 +-
drivers/gpu/drm/i915/gem/i915_gem_userptr.c | 5 +-
.../drm/i915/gem/selftests/i915_gem_context.c | 12 +-
.../drm/i915/gem/selftests/i915_gem_mman.c | 8 +-
.../drm/i915/gem/selftests/i915_gem_object.c | 8 +-
drivers/gpu/drm/i915/gvt/dmabuf.c | 9 +-
drivers/gpu/drm/i915/i915_gem.c | 18 +-
drivers/gpu/drm/i915/i915_scatterlist.h | 11 +
drivers/gpu/drm/i915/i915_utils.h | 6 +-
drivers/gpu/drm/i915/i915_vma.c | 8 +-
drivers/gpu/drm/i915/intel_region_ttm.c | 17 +-
include/linux/overflow.h | 54 ++++
include/linux/util_macros.h | 25 ++
19 files changed, 477 insertions(+), 93 deletions(-)

--
2.37.1


2022-08-16 10:49:40

by Gwan-gyeong Mun

[permalink] [raw]
Subject: [PATCH v7 3/8] drm/i915/gem: Typecheck page lookups

From: Chris Wilson <[email protected]>

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 <[email protected]>
Signed-off-by: Gwan-gyeong Mun <[email protected]>
Cc: Tvrtko Ursulin <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Thomas Hellström <[email protected]>
Reviewed-by: Nirmoy Das <[email protected]>
Reviewed-by: Mauro Carvalho Chehab <[email protected]>
Reviewed-by: Andrzej Hajda <[email protected]>
---
drivers/gpu/drm/i915/gem/i915_gem_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 5a5cf332d8a5..52c4c1fac7f9 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
@@ -724,7 +724,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 3ced9948a331..86e435d42546 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 fe0a890775e2..bf30763ee6bc 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

2022-08-16 10:51:18

by Gwan-gyeong Mun

[permalink] [raw]
Subject: [PATCH v7 8/8] drm/i915: Remove truncation warning for large objects

From: Chris Wilson <[email protected]>

Having addressed the issues surrounding incorrect types for local
variables and potential integer truncation in using the scatterlist API,
we have closed all the loop holes we had previously identified with
dangerously large object creation. As such, we can eliminate the warning
put in place to remind us to complete the review.

Signed-off-by: Chris Wilson <[email protected]>
Signed-off-by: Gwan-gyeong Mun <[email protected]>
Cc: Tvrtko Ursulin <[email protected]>
Cc: Brian Welty <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Thomas Hellström <[email protected]>
Testcase: igt@gem_create@create-massive
Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/4991
Reviewed-by: Nirmoy Das <[email protected]>
Reviewed-by: Mauro Carvalho Chehab <[email protected]>
Reviewed-by: Andrzej Hajda <[email protected]>
---
drivers/gpu/drm/i915/gem/i915_gem_object.h | 15 ---------------
1 file changed, 15 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
index 0cf31adbfd41..dd2762da332f 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
@@ -20,25 +20,10 @@

enum intel_region_id;

-/*
- * XXX: There is a prevalence of the assumption that we fit the
- * object's page count inside a 32bit _signed_ variable. Let's document
- * this and catch if we ever need to fix it. In the meantime, if you do
- * spot such a local variable, please consider fixing!
- *
- * We can check for invalidly typed locals with typecheck(), see for example
- * i915_gem_object_get_sg().
- */
-#define GEM_CHECK_SIZE_OVERFLOW(sz) \
- GEM_WARN_ON((sz) >> PAGE_SHIFT > INT_MAX)
-
static inline bool i915_gem_object_size_2big(u64 size)
{
struct drm_i915_gem_object *obj;

- if (GEM_CHECK_SIZE_OVERFLOW(size))
- return true;
-
if (overflows_type(size, obj->base.size))
return true;

--
2.37.1

2022-08-16 10:53:31

by Gwan-gyeong Mun

[permalink] [raw]
Subject: [PATCH v7 5/8] drm/i915: Check for integer truncation on the configuration of ttm place

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)

Signed-off-by: Gwan-gyeong Mun <[email protected]>
Cc: Chris Wilson <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Thomas Hellström <[email protected]>
Cc: Jani Nikula <[email protected]>
Reviewed-by: Nirmoy Das <[email protected]> (v2)
Reviewed-by: Mauro Carvalho Chehab <[email protected]> (v3)
Reported-by: kernel test robot <[email protected]>
Reviewed-by: Andrzej Hajda <[email protected]> (v5)
---
drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 6 +++---
drivers/gpu/drm/i915/intel_region_ttm.c | 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 9f2be1892b6c..69805cbe850c 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(!safe_conversion(&place->fpfn, offset >> PAGE_SHIFT));
+ WARN_ON(!safe_conversion(&place->lpfn, place->fpfn + (size >> PAGE_SHIFT)));
} else if (mr->io_size && mr->io_size < mr->total) {
if (flags & I915_BO_ALLOC_GPU_ONLY) {
place->flags |= TTM_PL_FLAG_TOPDOWN;
} else {
place->fpfn = 0;
- place->lpfn = mr->io_size >> PAGE_SHIFT;
+ WARN_ON(!safe_conversion(&place->lpfn, mr->io_size >> PAGE_SHIFT));
}
}
}
diff --git a/drivers/gpu/drm/i915/intel_region_ttm.c b/drivers/gpu/drm/i915/intel_region_ttm.c
index 575d67bc6ffe..c8c6c2e22e01 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(!safe_conversion(&place.fpfn, offset >> PAGE_SHIFT))) {
+ ret = -E2BIG;
+ goto out;
+ }
+ if (WARN_ON(!safe_conversion(&place.lpfn, place.fpfn + (size >> PAGE_SHIFT)))) {
+ ret = -E2BIG;
+ goto out;
+ }
} else if (mem->io_size && mem->io_size < mem->total) {
if (flags & I915_BO_ALLOC_GPU_ONLY) {
place.flags |= TTM_PL_FLAG_TOPDOWN;
} else {
place.fpfn = 0;
- place.lpfn = mem->io_size >> PAGE_SHIFT;
+ if (WARN_ON(!safe_conversion(&place.lpfn, mem->io_size >> PAGE_SHIFT))) {
+ 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

2022-08-16 10:55:10

by Gwan-gyeong Mun

[permalink] [raw]
Subject: [PATCH v7 2/8] util_macros: Add exact_type macro to catch type mis-match while compiling

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 <[email protected]>
Cc: Thomas Hellström <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Nirmoy Das <[email protected]>
Cc: Jani Nikula <[email protected]>
Cc: Andi Shyti <[email protected]>
Cc: Mauro Carvalho Chehab <[email protected]>
---
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

2022-08-16 10:55:11

by Gwan-gyeong Mun

[permalink] [raw]
Subject: [PATCH v7 7/8] drm/i915: Use error code as -E2BIG when the size of gem ttm object is too large

The ttm_bo_init_reserved() functions returns -ENOSPC if the size is too big
to add vma. The direct function that returns -ENOSPC is drm_mm_insert_node_in_range().
To handle the same error as other code returning -E2BIG when the size is
too large, it converts return value to -E2BIG.

Signed-off-by: Gwan-gyeong Mun <[email protected]>
Cc: Chris Wilson <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Thomas Hellström <[email protected]>
Reviewed-by: Nirmoy Das <[email protected]>
Reviewed-by: Mauro Carvalho Chehab <[email protected]>
Reviewed-by: Andrzej Hajda <[email protected]>
---
drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 11 +++++++++++
1 file changed, 11 insertions(+)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
index 69805cbe850c..8a5589accdb2 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
@@ -1249,6 +1249,17 @@ int __i915_gem_ttm_object_init(struct intel_memory_region *mem,
ret = ttm_bo_init_reserved(&i915->bdev, i915_gem_to_ttm(obj), bo_type,
&i915_sys_placement, page_size >> PAGE_SHIFT,
&ctx, NULL, NULL, i915_ttm_bo_destroy);
+
+ /*
+ * XXX: The ttm_bo_init_reserved() functions returns -ENOSPC if the size
+ * is too big to add vma. The direct function that returns -ENOSPC is
+ * drm_mm_insert_node_in_range(). To handle the same error as other code
+ * that returns -E2BIG when the size is too large, it converts -ENOSPC to
+ * -E2BIG.
+ */
+ if (size >> PAGE_SHIFT > INT_MAX && ret == -ENOSPC)
+ ret = -E2BIG;
+
if (ret)
return i915_ttm_err_to_gem(ret);

--
2.37.1

2022-08-16 11:23:06

by Gwan-gyeong Mun

[permalink] [raw]
Subject: [PATCH v7 6/8] drm/i915: Check if the size is too big while creating shmem file

The __shmem_file_setup() function returns -EINVAL if size is greater than
MAX_LFS_FILESIZE. To handle the same error as other code that returns
-E2BIG when the size is too large, it add a code that returns -E2BIG when
the size is larger than the size that can be handled.

v4: If BITS_PER_LONG is 32, size > MAX_LFS_FILESIZE is always false, so it
checks only when BITS_PER_LONG is 64.

Signed-off-by: Gwan-gyeong Mun <[email protected]>
Cc: Chris Wilson <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Thomas Hellström <[email protected]>
Reviewed-by: Nirmoy Das <[email protected]>
Reviewed-by: Mauro Carvalho Chehab <[email protected]>
Reported-by: kernel test robot <[email protected]>
Reviewed-by: Andrzej Hajda <[email protected]>
---
drivers/gpu/drm/i915/gem/i915_gem_shmem.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
index 4cb35808e431..4a7a6d65fc7a 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
@@ -541,6 +541,20 @@ static int __create_shmem(struct drm_i915_private *i915,

drm_gem_private_object_init(&i915->drm, obj, size);

+ /* XXX: The __shmem_file_setup() function returns -EINVAL if size is
+ * greater than MAX_LFS_FILESIZE.
+ * To handle the same error as other code that returns -E2BIG when
+ * the size is too large, we add a code that returns -E2BIG when the
+ * size is larger than the size that can be handled.
+ * If BITS_PER_LONG is 32, size > MAX_LFS_FILESIZE is always false,
+ * so we only needs to check when BITS_PER_LONG is 64.
+ * If BITS_PER_LONG is 32, E2BIG checks are processed when
+ * i915_gem_object_size_2big() is called before init_object() callback
+ * is called.
+ */
+ if (BITS_PER_LONG == 64 && size > MAX_LFS_FILESIZE)
+ return -E2BIG;
+
if (i915->mm.gemfs)
filp = shmem_file_setup_with_mnt(i915->mm.gemfs, "i915", size,
flags);
--
2.37.1

2022-08-16 11:27:46

by Gwan-gyeong Mun

[permalink] [raw]
Subject: [PATCH v7 4/8] drm/i915: Check for integer truncation on scatterlist creation

From: Chris Wilson <[email protected]>

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

To catch the implicit truncation as we switch from unsigned long into the
scatterlist's unsigned int, we use overflows_type check and report
E2BIG prior to the operation. This is already used in our create ioctls to
indicate if the uABI request is simply too large for the backing store.
Failing that type check, we have a second check at sg_alloc_table time
to make sure the values we are passing into the scatterlist API are not
truncated.

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

v2: Move added i915_utils's macro into drm_util header (Jani N)
v5: Fix macros to be enclosed in parentheses for complex values
Fix too long line warning

Signed-off-by: Chris Wilson <[email protected]>
Signed-off-by: Gwan-gyeong Mun <[email protected]>
Cc: Tvrtko Ursulin <[email protected]>
Cc: Brian Welty <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Thomas Hellström <[email protected]>
Reviewed-by: Nirmoy Das <[email protected]>
Reviewed-by: Mauro Carvalho Chehab <[email protected]>
Reviewed-by: Andrzej Hajda <[email protected]>
---
drivers/gpu/drm/i915/gem/i915_gem_internal.c | 6 ++++--
drivers/gpu/drm/i915/gem/i915_gem_object.h | 3 ---
drivers/gpu/drm/i915/gem/i915_gem_phys.c | 4 ++++
drivers/gpu/drm/i915/gem/i915_gem_shmem.c | 5 ++++-
drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 4 ++++
drivers/gpu/drm/i915/gem/i915_gem_userptr.c | 5 ++++-
drivers/gpu/drm/i915/gvt/dmabuf.c | 9 +++++----
drivers/gpu/drm/i915/i915_scatterlist.h | 11 +++++++++++
8 files changed, 36 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_internal.c b/drivers/gpu/drm/i915/gem/i915_gem_internal.c
index c698f95af15f..ff2e6e780631 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_internal.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_internal.c
@@ -37,10 +37,13 @@ static int i915_gem_object_get_pages_internal(struct drm_i915_gem_object *obj)
struct sg_table *st;
struct scatterlist *sg;
unsigned int sg_page_sizes;
- unsigned int npages;
+ pgoff_t npages; /* restricted by sg_alloc_table */
int max_order;
gfp_t gfp;

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

- npages = obj->base.size / PAGE_SIZE;
if (sg_alloc_table(st, npages, GFP_KERNEL)) {
kfree(st);
return -ENOMEM;
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h
index 5da872afc4ba..0cf31adbfd41 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_object.h
+++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h
@@ -26,9 +26,6 @@ enum intel_region_id;
* this and catch if we ever need to fix it. In the meantime, if you do
* spot such a local variable, please consider fixing!
*
- * Aside from our own locals (for which we have no excuse!):
- * - sg_table embeds unsigned int for nents
- *
* We can check for invalidly typed locals with typecheck(), see for example
* i915_gem_object_get_sg().
*/
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_phys.c b/drivers/gpu/drm/i915/gem/i915_gem_phys.c
index 0d0e46dae559..88ba7266a3a5 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_phys.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_phys.c
@@ -28,6 +28,10 @@ static int i915_gem_object_get_pages_phys(struct drm_i915_gem_object *obj)
void *dst;
int i;

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

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
index f42ca1179f37..4cb35808e431 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c
@@ -193,13 +193,16 @@ static int shmem_get_pages(struct drm_i915_gem_object *obj)
struct drm_i915_private *i915 = to_i915(obj->base.dev);
struct intel_memory_region *mem = obj->mm.region;
struct address_space *mapping = obj->base.filp->f_mapping;
- const unsigned long page_count = obj->base.size / PAGE_SIZE;
unsigned int max_segment = i915_sg_segment_size();
struct sg_table *st;
struct sgt_iter sgt_iter;
+ pgoff_t page_count;
struct page *page;
int ret;

+ if (!safe_conversion(&page_count, obj->base.size >> PAGE_SHIFT))
+ return -E2BIG;
+
/*
* Assert that the object is not currently in any GPU domain. As it
* wasn't in the GTT, there shouldn't be any way it could have been in
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
index 52c4c1fac7f9..9f2be1892b6c 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
@@ -822,6 +822,10 @@ static int i915_ttm_get_pages(struct drm_i915_gem_object *obj)
{
struct ttm_place requested, busy[I915_TTM_MAX_PLACEMENTS];
struct ttm_placement placement;
+ pgoff_t num_pages;
+
+ if (!safe_conversion(&num_pages, obj->base.size >> PAGE_SHIFT))
+ return -E2BIG;

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

diff --git a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
index 094f06b4ce33..25785c3a0083 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_userptr.c
@@ -128,13 +128,16 @@ static void i915_gem_object_userptr_drop_ref(struct drm_i915_gem_object *obj)

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

+ if (!safe_conversion(&num_pages, obj->base.size >> PAGE_SHIFT))
+ return -E2BIG;
+
st = kmalloc(sizeof(*st), GFP_KERNEL);
if (!st)
return -ENOMEM;
diff --git a/drivers/gpu/drm/i915/gvt/dmabuf.c b/drivers/gpu/drm/i915/gvt/dmabuf.c
index 01e54b45c5c1..795270cb4ec2 100644
--- a/drivers/gpu/drm/i915/gvt/dmabuf.c
+++ b/drivers/gpu/drm/i915/gvt/dmabuf.c
@@ -42,8 +42,7 @@

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

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

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

- page_num = obj->base.size >> PAGE_SHIFT;
ret = sg_alloc_table(st, page_num, GFP_KERNEL);
if (ret) {
kfree(st);
diff --git a/drivers/gpu/drm/i915/i915_scatterlist.h b/drivers/gpu/drm/i915/i915_scatterlist.h
index 9ddb3e743a3e..1d1802beb42b 100644
--- a/drivers/gpu/drm/i915/i915_scatterlist.h
+++ b/drivers/gpu/drm/i915/i915_scatterlist.h
@@ -220,4 +220,15 @@ struct i915_refct_sgt *i915_rsgt_from_buddy_resource(struct ttm_resource *res,
u64 region_start,
u32 page_alignment);

+/* Wrap scatterlist.h to sanity check for integer truncation */
+typedef unsigned int __sg_size_t; /* see linux/scatterlist.h */
+#define sg_alloc_table(sgt, nents, gfp) \
+ overflows_type(nents, __sg_size_t) ? -E2BIG \
+ : ((sg_alloc_table)(sgt, (__sg_size_t)(nents), gfp))
+
+#define sg_alloc_table_from_pages_segment(sgt, pages, npages, offset, size, max_segment, gfp) \
+ overflows_type(npages, __sg_size_t) ? -E2BIG \
+ : ((sg_alloc_table_from_pages_segment)(sgt, pages, (__sg_size_t)(npages), offset, \
+ size, max_segment, gfp))
+
#endif
--
2.37.1

2022-08-16 12:02:20

by Gwan-gyeong Mun

[permalink] [raw]
Subject: [PATCH v7 1/8] overflow: Move and add few utility macros into overflow

It moves overflows_type utility macro into overflow header from i915_utils
header. The overflows_type can be used to catch the truncation between data
types. And it adds safe_conversion() macro which performs a type conversion
(cast) of an source value into a new variable, checking that the
destination is large enough to hold the source value. And the functionality
of overflows_type has been improved to handle the signbit.
The is_unsigned_type macro has been added to check the sign bit of the
built-in type.

v3: Add is_type_unsigned() macro (Mauro)
Modify overflows_type() macro to consider signed data types (Mauro)
Fix the problem that safe_conversion() macro always returns true
v4: Fix kernel-doc markups
v6: Move macro addition location so that it can be used by other than drm
subsystem (Jani, Mauro, Andi)
Change is_type_unsigned to is_unsigned_type to have the same name form
as is_signed_type macro

Signed-off-by: Gwan-gyeong Mun <[email protected]>
Cc: Thomas Hellström <[email protected]>
Cc: Matthew Auld <[email protected]>
Cc: Nirmoy Das <[email protected]>
Cc: Jani Nikula <[email protected]>
Cc: Andi Shyti <[email protected]>
Reviewed-by: Mauro Carvalho Chehab <[email protected]> (v5)
---
drivers/gpu/drm/i915/i915_utils.h | 5 +--
include/linux/overflow.h | 54 +++++++++++++++++++++++++++++++
2 files changed, 55 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h
index c10d68cdc3ca..eb0ded23fa9c 100644
--- a/drivers/gpu/drm/i915/i915_utils.h
+++ b/drivers/gpu/drm/i915/i915_utils.h
@@ -32,6 +32,7 @@
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/sched/clock.h>
+#include <linux/overflow.h>

#ifdef CONFIG_X86
#include <asm/hypervisor.h>
@@ -111,10 +112,6 @@ bool i915_error_injected(void);
#define range_overflows_end_t(type, start, size, max) \
range_overflows_end((type)(start), (type)(size), (type)(max))

-/* Note we don't consider signbits :| */
-#define overflows_type(x, T) \
- (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T))
-
#define ptr_mask_bits(ptr, n) ({ \
unsigned long __v = (unsigned long)(ptr); \
(typeof(ptr))(__v & -BIT(n)); \
diff --git a/include/linux/overflow.h b/include/linux/overflow.h
index f1221d11f8e5..462a03454377 100644
--- a/include/linux/overflow.h
+++ b/include/linux/overflow.h
@@ -35,6 +35,60 @@
#define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
#define type_min(T) ((T)((T)-type_max(T)-(T)1))

+/**
+ * is_unsigned_type - helper for checking data type which is an unsigned data
+ * type or not
+ * @x: The data type to check
+ *
+ * Returns:
+ * True if the data type is an unsigned data type, false otherwise.
+ */
+#define is_unsigned_type(x) ((typeof(x))-1 >= (typeof(x))0)
+
+/**
+ * overflows_type - helper for checking the truncation between data types
+ * @x: Source for overflow type comparison
+ * @T: Destination for overflow type comparison
+ *
+ * It compares the values and size of each data type between the first and
+ * second argument to check whether truncation can occur when assigning the
+ * first argument to the variable of the second argument.
+ * Source and Destination can be used with or without sign bit.
+ * Composite data structures such as union and structure are not considered.
+ * Enum data types are not considered.
+ * Floating point data types are not considered.
+ *
+ * Returns:
+ * True if truncation can occur, false otherwise.
+ */
+#define overflows_type(x, T) \
+ (is_unsigned_type(x) ? \
+ is_unsigned_type(T) ? \
+ (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
+ : (sizeof(x) >= sizeof(T) && (x) >> (BITS_PER_TYPE(T) - 1)) ? 1 : 0 \
+ : is_unsigned_type(T) ? \
+ ((x) < 0) ? 1 : (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
+ : (sizeof(x) > sizeof(T)) ? \
+ ((x) < 0) ? (((x) * -1) >> BITS_PER_TYPE(T)) ? 1 : 0 \
+ : ((x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
+ : 0)
+
+/**
+ * safe_conversion - perform a type conversion (cast) of an source value into
+ * a new variable, checking that the destination is large enough to hold the
+ * source value.
+ * @ptr: Destination pointer address
+ * @value: Source value
+ *
+ * Returns:
+ * If the value would overflow the destination, it returns false.
+ */
+#define safe_conversion(ptr, value) ({ \
+ typeof(value) __v = (value); \
+ typeof(ptr) __ptr = (ptr); \
+ overflows_type(__v, *__ptr) ? 0 : ((*__ptr = (typeof(*__ptr))__v), 1); \
+})
+
/*
* Avoids triggering -Wtype-limits compilation warning,
* while using unsigned data types to check a < 0.
--
2.37.1

2022-08-17 23:15:36

by Andi Shyti

[permalink] [raw]
Subject: Re: [PATCH v7 1/8] overflow: Move and add few utility macros into overflow

Hi Kees,

would you mind taking a look at this patch?

Thanks,
Andi

On Tue, Aug 16, 2022 at 06:35:18PM +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 truncation between data
> types. And it adds safe_conversion() macro which performs a type conversion
> (cast) of an source value into a new variable, checking that the
> destination is large enough to hold the source value. And the functionality
> of overflows_type has been improved to handle the signbit.
> The is_unsigned_type macro has been added to check the sign bit of the
> built-in type.
>
> v3: Add is_type_unsigned() macro (Mauro)
> Modify overflows_type() macro to consider signed data types (Mauro)
> Fix the problem that safe_conversion() macro always returns true
> v4: Fix kernel-doc markups
> v6: Move macro addition location so that it can be used by other than drm
> subsystem (Jani, Mauro, Andi)
> Change is_type_unsigned to is_unsigned_type to have the same name form
> as is_signed_type macro
>
> Signed-off-by: Gwan-gyeong Mun <[email protected]>
> Cc: Thomas Hellstr?m <[email protected]>
> Cc: Matthew Auld <[email protected]>
> Cc: Nirmoy Das <[email protected]>
> Cc: Jani Nikula <[email protected]>
> Cc: Andi Shyti <[email protected]>
> Reviewed-by: Mauro Carvalho Chehab <[email protected]> (v5)
> ---
> drivers/gpu/drm/i915/i915_utils.h | 5 +--
> include/linux/overflow.h | 54 +++++++++++++++++++++++++++++++
> 2 files changed, 55 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h
> index c10d68cdc3ca..eb0ded23fa9c 100644
> --- a/drivers/gpu/drm/i915/i915_utils.h
> +++ b/drivers/gpu/drm/i915/i915_utils.h
> @@ -32,6 +32,7 @@
> #include <linux/types.h>
> #include <linux/workqueue.h>
> #include <linux/sched/clock.h>
> +#include <linux/overflow.h>
>
> #ifdef CONFIG_X86
> #include <asm/hypervisor.h>
> @@ -111,10 +112,6 @@ bool i915_error_injected(void);
> #define range_overflows_end_t(type, start, size, max) \
> range_overflows_end((type)(start), (type)(size), (type)(max))
>
> -/* Note we don't consider signbits :| */
> -#define overflows_type(x, T) \
> - (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T))
> -
> #define ptr_mask_bits(ptr, n) ({ \
> unsigned long __v = (unsigned long)(ptr); \
> (typeof(ptr))(__v & -BIT(n)); \
> diff --git a/include/linux/overflow.h b/include/linux/overflow.h
> index f1221d11f8e5..462a03454377 100644
> --- a/include/linux/overflow.h
> +++ b/include/linux/overflow.h
> @@ -35,6 +35,60 @@
> #define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
> #define type_min(T) ((T)((T)-type_max(T)-(T)1))
>
> +/**
> + * is_unsigned_type - helper for checking data type which is an unsigned data
> + * type or not
> + * @x: The data type to check
> + *
> + * Returns:
> + * True if the data type is an unsigned data type, false otherwise.
> + */
> +#define is_unsigned_type(x) ((typeof(x))-1 >= (typeof(x))0)
> +
> +/**
> + * overflows_type - helper for checking the truncation between data types
> + * @x: Source for overflow type comparison
> + * @T: Destination for overflow type comparison
> + *
> + * It compares the values and size of each data type between the first and
> + * second argument to check whether truncation can occur when assigning the
> + * first argument to the variable of the second argument.
> + * Source and Destination can be used with or without sign bit.
> + * Composite data structures such as union and structure are not considered.
> + * Enum data types are not considered.
> + * Floating point data types are not considered.
> + *
> + * Returns:
> + * True if truncation can occur, false otherwise.
> + */
> +#define overflows_type(x, T) \
> + (is_unsigned_type(x) ? \
> + is_unsigned_type(T) ? \
> + (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
> + : (sizeof(x) >= sizeof(T) && (x) >> (BITS_PER_TYPE(T) - 1)) ? 1 : 0 \
> + : is_unsigned_type(T) ? \
> + ((x) < 0) ? 1 : (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
> + : (sizeof(x) > sizeof(T)) ? \
> + ((x) < 0) ? (((x) * -1) >> BITS_PER_TYPE(T)) ? 1 : 0 \
> + : ((x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
> + : 0)
> +
> +/**
> + * safe_conversion - perform a type conversion (cast) of an source value into
> + * a new variable, checking that the destination is large enough to hold the
> + * source value.
> + * @ptr: Destination pointer address
> + * @value: Source value
> + *
> + * Returns:
> + * If the value would overflow the destination, it returns false.
> + */
> +#define safe_conversion(ptr, value) ({ \
> + typeof(value) __v = (value); \
> + typeof(ptr) __ptr = (ptr); \
> + overflows_type(__v, *__ptr) ? 0 : ((*__ptr = (typeof(*__ptr))__v), 1); \
> +})
> +
> /*
> * Avoids triggering -Wtype-limits compilation warning,
> * while using unsigned data types to check a < 0.
> --
> 2.37.1

2022-08-18 00:23:14

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v7 1/8] overflow: Move and add few utility macros into overflow

On Thu, Aug 18, 2022 at 01:07:29AM +0200, Andi Shyti wrote:
> Hi Kees,
>
> would you mind taking a look at this patch?

Hi! Thanks for the heads-up!

>
> Thanks,
> Andi
>
> On Tue, Aug 16, 2022 at 06:35:18PM +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 truncation between data
> > types. And it adds safe_conversion() macro which performs a type conversion
> > (cast) of an source value into a new variable, checking that the
> > destination is large enough to hold the source value. And the functionality
> > of overflows_type has been improved to handle the signbit.
> > The is_unsigned_type macro has been added to check the sign bit of the
> > built-in type.
> >
> > v3: Add is_type_unsigned() macro (Mauro)
> > Modify overflows_type() macro to consider signed data types (Mauro)
> > Fix the problem that safe_conversion() macro always returns true
> > v4: Fix kernel-doc markups
> > v6: Move macro addition location so that it can be used by other than drm
> > subsystem (Jani, Mauro, Andi)
> > Change is_type_unsigned to is_unsigned_type to have the same name form
> > as is_signed_type macro
> >
> > Signed-off-by: Gwan-gyeong Mun <[email protected]>
> > Cc: Thomas Hellstr?m <[email protected]>
> > Cc: Matthew Auld <[email protected]>
> > Cc: Nirmoy Das <[email protected]>
> > Cc: Jani Nikula <[email protected]>
> > Cc: Andi Shyti <[email protected]>
> > Reviewed-by: Mauro Carvalho Chehab <[email protected]> (v5)
> > ---
> > drivers/gpu/drm/i915/i915_utils.h | 5 +--
> > include/linux/overflow.h | 54 +++++++++++++++++++++++++++++++
> > 2 files changed, 55 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h
> > index c10d68cdc3ca..eb0ded23fa9c 100644
> > --- a/drivers/gpu/drm/i915/i915_utils.h
> > +++ b/drivers/gpu/drm/i915/i915_utils.h
> > @@ -32,6 +32,7 @@
> > #include <linux/types.h>
> > #include <linux/workqueue.h>
> > #include <linux/sched/clock.h>
> > +#include <linux/overflow.h>
> >
> > #ifdef CONFIG_X86
> > #include <asm/hypervisor.h>
> > @@ -111,10 +112,6 @@ bool i915_error_injected(void);
> > #define range_overflows_end_t(type, start, size, max) \
> > range_overflows_end((type)(start), (type)(size), (type)(max))
> >
> > -/* Note we don't consider signbits :| */
> > -#define overflows_type(x, T) \
> > - (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T))
> > -
> > #define ptr_mask_bits(ptr, n) ({ \
> > unsigned long __v = (unsigned long)(ptr); \
> > (typeof(ptr))(__v & -BIT(n)); \
> > diff --git a/include/linux/overflow.h b/include/linux/overflow.h
> > index f1221d11f8e5..462a03454377 100644
> > --- a/include/linux/overflow.h
> > +++ b/include/linux/overflow.h
> > @@ -35,6 +35,60 @@
> > #define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
> > #define type_min(T) ((T)((T)-type_max(T)-(T)1))
> >
> > +/**
> > + * is_unsigned_type - helper for checking data type which is an unsigned data
> > + * type or not
> > + * @x: The data type to check
> > + *
> > + * Returns:
> > + * True if the data type is an unsigned data type, false otherwise.
> > + */
> > +#define is_unsigned_type(x) ((typeof(x))-1 >= (typeof(x))0)

I'd rather not have separate logic for this. Instead, I'd like it to be:

#define is_unsigned_type(x) (!is_signed_type(x))

> > +
> > +/**
> > + * overflows_type - helper for checking the truncation between data types
> > + * @x: Source for overflow type comparison
> > + * @T: Destination for overflow type comparison
> > + *
> > + * It compares the values and size of each data type between the first and
> > + * second argument to check whether truncation can occur when assigning the
> > + * first argument to the variable of the second argument.
> > + * Source and Destination can be used with or without sign bit.
> > + * Composite data structures such as union and structure are not considered.
> > + * Enum data types are not considered.
> > + * Floating point data types are not considered.
> > + *
> > + * Returns:
> > + * True if truncation can occur, false otherwise.
> > + */
> > +#define overflows_type(x, T) \
> > + (is_unsigned_type(x) ? \
> > + is_unsigned_type(T) ? \
> > + (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
> > + : (sizeof(x) >= sizeof(T) && (x) >> (BITS_PER_TYPE(T) - 1)) ? 1 : 0 \
> > + : is_unsigned_type(T) ? \
> > + ((x) < 0) ? 1 : (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
> > + : (sizeof(x) > sizeof(T)) ? \
> > + ((x) < 0) ? (((x) * -1) >> BITS_PER_TYPE(T)) ? 1 : 0 \
> > + : ((x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
> > + : 0)

Like the other, I'd much rather this was rephrased in terms of the
existing macros (e.g. type_min()/type_max().)

> > +
> > +/**
> > + * safe_conversion - perform a type conversion (cast) of an source value into
> > + * a new variable, checking that the destination is large enough to hold the
> > + * source value.
> > + * @ptr: Destination pointer address
> > + * @value: Source value
> > + *
> > + * Returns:
> > + * If the value would overflow the destination, it returns false.
> > + */
> > +#define safe_conversion(ptr, value) ({ \
> > + typeof(value) __v = (value); \
> > + typeof(ptr) __ptr = (ptr); \
> > + overflows_type(__v, *__ptr) ? 0 : ((*__ptr = (typeof(*__ptr))__v), 1); \
> > +})

I try to avoid "safe" as an adjective for interface names, since it
doesn't really answer "safe from what?" This looks more like "assign, but
zero when out of bounds". And it can be built from existing macros here:

if (check_add_overflow(0, value, ptr))
*ptr = 0;

I actually want to push back on this a bit, because there can still be
logic bugs built around this kind of primitive. Shouldn't out-of-bounds
assignments be seen as a direct failure? I would think this would be
sufficient:

#define check_assign(value, ptr) check_add_overflow(0, value, ptr)

And callers would do:

if (check_assign(value, &var))
return -EINVAL;

etc.


--
Kees Cook

2022-08-22 11:44:46

by Jani Nikula

[permalink] [raw]
Subject: Re: [PATCH v7 1/8] overflow: Move and add few utility macros into overflow

On Wed, 17 Aug 2022, Kees Cook <[email protected]> wrote:
> On Thu, Aug 18, 2022 at 01:07:29AM +0200, Andi Shyti wrote:
>> Hi Kees,
>>
>> would you mind taking a look at this patch?
>
> Hi! Thanks for the heads-up!

Thanks for your review. This actually reaffirms my belief that we need
to get these macros out of i915_utils.h and into the common headers,
where we can get more eyes on them.

BR,
Jani.


>
>>
>> Thanks,
>> Andi
>>
>> On Tue, Aug 16, 2022 at 06:35:18PM +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 truncation between data
>> > types. And it adds safe_conversion() macro which performs a type conversion
>> > (cast) of an source value into a new variable, checking that the
>> > destination is large enough to hold the source value. And the functionality
>> > of overflows_type has been improved to handle the signbit.
>> > The is_unsigned_type macro has been added to check the sign bit of the
>> > built-in type.
>> >
>> > v3: Add is_type_unsigned() macro (Mauro)
>> > Modify overflows_type() macro to consider signed data types (Mauro)
>> > Fix the problem that safe_conversion() macro always returns true
>> > v4: Fix kernel-doc markups
>> > v6: Move macro addition location so that it can be used by other than drm
>> > subsystem (Jani, Mauro, Andi)
>> > Change is_type_unsigned to is_unsigned_type to have the same name form
>> > as is_signed_type macro
>> >
>> > Signed-off-by: Gwan-gyeong Mun <[email protected]>
>> > Cc: Thomas Hellström <[email protected]>
>> > Cc: Matthew Auld <[email protected]>
>> > Cc: Nirmoy Das <[email protected]>
>> > Cc: Jani Nikula <[email protected]>
>> > Cc: Andi Shyti <[email protected]>
>> > Reviewed-by: Mauro Carvalho Chehab <[email protected]> (v5)
>> > ---
>> > drivers/gpu/drm/i915/i915_utils.h | 5 +--
>> > include/linux/overflow.h | 54 +++++++++++++++++++++++++++++++
>> > 2 files changed, 55 insertions(+), 4 deletions(-)
>> >
>> > diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h
>> > index c10d68cdc3ca..eb0ded23fa9c 100644
>> > --- a/drivers/gpu/drm/i915/i915_utils.h
>> > +++ b/drivers/gpu/drm/i915/i915_utils.h
>> > @@ -32,6 +32,7 @@
>> > #include <linux/types.h>
>> > #include <linux/workqueue.h>
>> > #include <linux/sched/clock.h>
>> > +#include <linux/overflow.h>
>> >
>> > #ifdef CONFIG_X86
>> > #include <asm/hypervisor.h>
>> > @@ -111,10 +112,6 @@ bool i915_error_injected(void);
>> > #define range_overflows_end_t(type, start, size, max) \
>> > range_overflows_end((type)(start), (type)(size), (type)(max))
>> >
>> > -/* Note we don't consider signbits :| */
>> > -#define overflows_type(x, T) \
>> > - (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T))
>> > -
>> > #define ptr_mask_bits(ptr, n) ({ \
>> > unsigned long __v = (unsigned long)(ptr); \
>> > (typeof(ptr))(__v & -BIT(n)); \
>> > diff --git a/include/linux/overflow.h b/include/linux/overflow.h
>> > index f1221d11f8e5..462a03454377 100644
>> > --- a/include/linux/overflow.h
>> > +++ b/include/linux/overflow.h
>> > @@ -35,6 +35,60 @@
>> > #define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
>> > #define type_min(T) ((T)((T)-type_max(T)-(T)1))
>> >
>> > +/**
>> > + * is_unsigned_type - helper for checking data type which is an unsigned data
>> > + * type or not
>> > + * @x: The data type to check
>> > + *
>> > + * Returns:
>> > + * True if the data type is an unsigned data type, false otherwise.
>> > + */
>> > +#define is_unsigned_type(x) ((typeof(x))-1 >= (typeof(x))0)
>
> I'd rather not have separate logic for this. Instead, I'd like it to be:
>
> #define is_unsigned_type(x) (!is_signed_type(x))
>
>> > +
>> > +/**
>> > + * overflows_type - helper for checking the truncation between data types
>> > + * @x: Source for overflow type comparison
>> > + * @T: Destination for overflow type comparison
>> > + *
>> > + * It compares the values and size of each data type between the first and
>> > + * second argument to check whether truncation can occur when assigning the
>> > + * first argument to the variable of the second argument.
>> > + * Source and Destination can be used with or without sign bit.
>> > + * Composite data structures such as union and structure are not considered.
>> > + * Enum data types are not considered.
>> > + * Floating point data types are not considered.
>> > + *
>> > + * Returns:
>> > + * True if truncation can occur, false otherwise.
>> > + */
>> > +#define overflows_type(x, T) \
>> > + (is_unsigned_type(x) ? \
>> > + is_unsigned_type(T) ? \
>> > + (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
>> > + : (sizeof(x) >= sizeof(T) && (x) >> (BITS_PER_TYPE(T) - 1)) ? 1 : 0 \
>> > + : is_unsigned_type(T) ? \
>> > + ((x) < 0) ? 1 : (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
>> > + : (sizeof(x) > sizeof(T)) ? \
>> > + ((x) < 0) ? (((x) * -1) >> BITS_PER_TYPE(T)) ? 1 : 0 \
>> > + : ((x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
>> > + : 0)
>
> Like the other, I'd much rather this was rephrased in terms of the
> existing macros (e.g. type_min()/type_max().)
>
>> > +
>> > +/**
>> > + * safe_conversion - perform a type conversion (cast) of an source value into
>> > + * a new variable, checking that the destination is large enough to hold the
>> > + * source value.
>> > + * @ptr: Destination pointer address
>> > + * @value: Source value
>> > + *
>> > + * Returns:
>> > + * If the value would overflow the destination, it returns false.
>> > + */
>> > +#define safe_conversion(ptr, value) ({ \
>> > + typeof(value) __v = (value); \
>> > + typeof(ptr) __ptr = (ptr); \
>> > + overflows_type(__v, *__ptr) ? 0 : ((*__ptr = (typeof(*__ptr))__v), 1); \
>> > +})
>
> I try to avoid "safe" as an adjective for interface names, since it
> doesn't really answer "safe from what?" This looks more like "assign, but
> zero when out of bounds". And it can be built from existing macros here:
>
> if (check_add_overflow(0, value, ptr))
> *ptr = 0;
>
> I actually want to push back on this a bit, because there can still be
> logic bugs built around this kind of primitive. Shouldn't out-of-bounds
> assignments be seen as a direct failure? I would think this would be
> sufficient:
>
> #define check_assign(value, ptr) check_add_overflow(0, value, ptr)
>
> And callers would do:
>
> if (check_assign(value, &var))
> return -EINVAL;
>
> etc.

--
Jani Nikula, Intel Open Source Graphics Center

2022-08-22 14:40:16

by Andrzej Hajda

[permalink] [raw]
Subject: Re: [PATCH v7 1/8] overflow: Move and add few utility macros into overflow

On 18.08.2022 02:12, Kees Cook wrote:
> On Thu, Aug 18, 2022 at 01:07:29AM +0200, Andi Shyti wrote:
>> Hi Kees,
>>
>> would you mind taking a look at this patch?
>
> Hi! Thanks for the heads-up!
>
>>
>> Thanks,
>> Andi
>>
>> On Tue, Aug 16, 2022 at 06:35:18PM +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 truncation between data
>>> types. And it adds safe_conversion() macro which performs a type conversion
>>> (cast) of an source value into a new variable, checking that the
>>> destination is large enough to hold the source value. And the functionality
>>> of overflows_type has been improved to handle the signbit.
>>> The is_unsigned_type macro has been added to check the sign bit of the
>>> built-in type.
>>>
>>> v3: Add is_type_unsigned() macro (Mauro)
>>> Modify overflows_type() macro to consider signed data types (Mauro)
>>> Fix the problem that safe_conversion() macro always returns true
>>> v4: Fix kernel-doc markups
>>> v6: Move macro addition location so that it can be used by other than drm
>>> subsystem (Jani, Mauro, Andi)
>>> Change is_type_unsigned to is_unsigned_type to have the same name form
>>> as is_signed_type macro
>>>
>>> Signed-off-by: Gwan-gyeong Mun <[email protected]>
>>> Cc: Thomas Hellström <[email protected]>
>>> Cc: Matthew Auld <[email protected]>
>>> Cc: Nirmoy Das <[email protected]>
>>> Cc: Jani Nikula <[email protected]>
>>> Cc: Andi Shyti <[email protected]>
>>> Reviewed-by: Mauro Carvalho Chehab <[email protected]> (v5)
>>> ---
>>> drivers/gpu/drm/i915/i915_utils.h | 5 +--
>>> include/linux/overflow.h | 54 +++++++++++++++++++++++++++++++
>>> 2 files changed, 55 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/drivers/gpu/drm/i915/i915_utils.h b/drivers/gpu/drm/i915/i915_utils.h
>>> index c10d68cdc3ca..eb0ded23fa9c 100644
>>> --- a/drivers/gpu/drm/i915/i915_utils.h
>>> +++ b/drivers/gpu/drm/i915/i915_utils.h
>>> @@ -32,6 +32,7 @@
>>> #include <linux/types.h>
>>> #include <linux/workqueue.h>
>>> #include <linux/sched/clock.h>
>>> +#include <linux/overflow.h>
>>>
>>> #ifdef CONFIG_X86
>>> #include <asm/hypervisor.h>
>>> @@ -111,10 +112,6 @@ bool i915_error_injected(void);
>>> #define range_overflows_end_t(type, start, size, max) \
>>> range_overflows_end((type)(start), (type)(size), (type)(max))
>>>
>>> -/* Note we don't consider signbits :| */
>>> -#define overflows_type(x, T) \
>>> - (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T))
>>> -
>>> #define ptr_mask_bits(ptr, n) ({ \
>>> unsigned long __v = (unsigned long)(ptr); \
>>> (typeof(ptr))(__v & -BIT(n)); \
>>> diff --git a/include/linux/overflow.h b/include/linux/overflow.h
>>> index f1221d11f8e5..462a03454377 100644
>>> --- a/include/linux/overflow.h
>>> +++ b/include/linux/overflow.h
>>> @@ -35,6 +35,60 @@
>>> #define type_max(T) ((T)((__type_half_max(T) - 1) + __type_half_max(T)))
>>> #define type_min(T) ((T)((T)-type_max(T)-(T)1))
>>>
>>> +/**
>>> + * is_unsigned_type - helper for checking data type which is an unsigned data
>>> + * type or not
>>> + * @x: The data type to check
>>> + *
>>> + * Returns:
>>> + * True if the data type is an unsigned data type, false otherwise.
>>> + */
>>> +#define is_unsigned_type(x) ((typeof(x))-1 >= (typeof(x))0)
>
> I'd rather not have separate logic for this. Instead, I'd like it to be:
>
> #define is_unsigned_type(x) (!is_signed_type(x))
>
>>> +
>>> +/**
>>> + * overflows_type - helper for checking the truncation between data types
>>> + * @x: Source for overflow type comparison
>>> + * @T: Destination for overflow type comparison
>>> + *
>>> + * It compares the values and size of each data type between the first and
>>> + * second argument to check whether truncation can occur when assigning the
>>> + * first argument to the variable of the second argument.
>>> + * Source and Destination can be used with or without sign bit.
>>> + * Composite data structures such as union and structure are not considered.
>>> + * Enum data types are not considered.
>>> + * Floating point data types are not considered.
>>> + *
>>> + * Returns:
>>> + * True if truncation can occur, false otherwise.
>>> + */
>>> +#define overflows_type(x, T) \
>>> + (is_unsigned_type(x) ? \
>>> + is_unsigned_type(T) ? \
>>> + (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
>>> + : (sizeof(x) >= sizeof(T) && (x) >> (BITS_PER_TYPE(T) - 1)) ? 1 : 0 \
>>> + : is_unsigned_type(T) ? \
>>> + ((x) < 0) ? 1 : (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
>>> + : (sizeof(x) > sizeof(T)) ? \
>>> + ((x) < 0) ? (((x) * -1) >> BITS_PER_TYPE(T)) ? 1 : 0 \
>>> + : ((x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
>>> + : 0)
>
> Like the other, I'd much rather this was rephrased in terms of the
> existing macros (e.g. type_min()/type_max().)


I am not sure how it could be rephrased with type_(min|max), but I guess
the shortest could be sth like:

#define overflows_type(x, T) __builtin_add_overflow_p(x, (typeof(T))0,
(typeof(T))0)

Regards
Andrzej


>
>>> +
>>> +/**
>>> + * safe_conversion - perform a type conversion (cast) of an source value into
>>> + * a new variable, checking that the destination is large enough to hold the
>>> + * source value.
>>> + * @ptr: Destination pointer address
>>> + * @value: Source value
>>> + *
>>> + * Returns:
>>> + * If the value would overflow the destination, it returns false.
>>> + */
>>> +#define safe_conversion(ptr, value) ({ \
>>> + typeof(value) __v = (value); \
>>> + typeof(ptr) __ptr = (ptr); \
>>> + overflows_type(__v, *__ptr) ? 0 : ((*__ptr = (typeof(*__ptr))__v), 1); \
>>> +})
>
> I try to avoid "safe" as an adjective for interface names, since it
> doesn't really answer "safe from what?" This looks more like "assign, but
> zero when out of bounds". And it can be built from existing macros here:
>
> if (check_add_overflow(0, value, ptr))
> *ptr = 0;
>
> I actually want to push back on this a bit, because there can still be
> logic bugs built around this kind of primitive. Shouldn't out-of-bounds
> assignments be seen as a direct failure? I would think this would be
> sufficient:
>
> #define check_assign(value, ptr) check_add_overflow(0, value, ptr)
>
> And callers would do:
>
> if (check_assign(value, &var))
> return -EINVAL;
>
> etc.
>
>

2022-08-22 14:42:40

by Andrzej Hajda

[permalink] [raw]
Subject: Re: [Intel-gfx] [PATCH v7 1/8] overflow: Move and add few utility macros into overflow



On 22.08.2022 16:05, Andrzej Hajda wrote:
> On 18.08.2022 02:12, Kees Cook wrote:
>> On Thu, Aug 18, 2022 at 01:07:29AM +0200, Andi Shyti wrote:
>>> Hi Kees,
>>>
>>> would you mind taking a look at this patch?
>>
>> Hi! Thanks for the heads-up!
>>
>>>
>>> Thanks,
>>> Andi
>>>
>>> On Tue, Aug 16, 2022 at 06:35:18PM +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 truncation
>>>> between data
>>>> types. And it adds safe_conversion() macro which performs a type
>>>> conversion
>>>> (cast) of an source value into a new variable, checking that the
>>>> destination is large enough to hold the source value. And the
>>>> functionality
>>>> of overflows_type has been improved to handle the signbit.
>>>> The is_unsigned_type macro has been added to check the sign bit of the
>>>> built-in type.
>>>>
>>>> v3: Add is_type_unsigned() macro (Mauro)
>>>>      Modify overflows_type() macro to consider signed data types
>>>> (Mauro)
>>>>      Fix the problem that safe_conversion() macro always returns true
>>>> v4: Fix kernel-doc markups
>>>> v6: Move macro addition location so that it can be used by other
>>>> than drm
>>>>      subsystem (Jani, Mauro, Andi)
>>>>      Change is_type_unsigned to is_unsigned_type to have the same
>>>> name form
>>>>      as is_signed_type macro
>>>>
>>>> Signed-off-by: Gwan-gyeong Mun <[email protected]>
>>>> Cc: Thomas Hellström <[email protected]>
>>>> Cc: Matthew Auld <[email protected]>
>>>> Cc: Nirmoy Das <[email protected]>
>>>> Cc: Jani Nikula <[email protected]>
>>>> Cc: Andi Shyti <[email protected]>
>>>> Reviewed-by: Mauro Carvalho Chehab <[email protected]> (v5)
>>>> ---

(...)

>>>> +
>>>> +/**
>>>> + * overflows_type - helper for checking the truncation between data
>>>> types
>>>> + * @x: Source for overflow type comparison
>>>> + * @T: Destination for overflow type comparison
>>>> + *
>>>> + * It compares the values and size of each data type between the
>>>> first and
>>>> + * second argument to check whether truncation can occur when
>>>> assigning the
>>>> + * first argument to the variable of the second argument.
>>>> + * Source and Destination can be used with or without sign bit.
>>>> + * Composite data structures such as union and structure are not
>>>> considered.
>>>> + * Enum data types are not considered.
>>>> + * Floating point data types are not considered.
>>>> + *
>>>> + * Returns:
>>>> + * True if truncation can occur, false otherwise.
>>>> + */
>>>> +#define overflows_type(x, T) \
>>>> +    (is_unsigned_type(x) ? \
>>>> +        is_unsigned_type(T) ? \
>>>> +            (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1
>>>> : 0 \
>>>> +            : (sizeof(x) >= sizeof(T) && (x) >> (BITS_PER_TYPE(T) -
>>>> 1)) ? 1 : 0 \
>>>> +    : is_unsigned_type(T) ? \
>>>> +        ((x) < 0) ? 1 : (sizeof(x) > sizeof(T) && (x) >>
>>>> BITS_PER_TYPE(T)) ? 1 : 0 \
>>>> +        : (sizeof(x) > sizeof(T)) ? \
>>>> +            ((x) < 0) ? (((x) * -1) >> BITS_PER_TYPE(T)) ? 1 : 0 \
>>>> +                : ((x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
>>>> +            : 0)
>>
>> Like the other, I'd much rather this was rephrased in terms of the
>> existing macros (e.g. type_min()/type_max().)
>
>
> I am not sure how it could be rephrased with type_(min|max), but I guess
> the shortest could be sth like:
>
> #define overflows_type(x, T) __builtin_add_overflow_p(x, (typeof(T))0,
> (typeof(T))0)

Except this macro is available since gcc 7, but apparently
__builtin_add_overflow is supported since gcc 5, which should be OK:
#define overflows_type(x, T) ({ typeof(T) r = 0;
__builtin_add_overflow_p((x), r, r); })

Regards
Andrzej

>
> Regards
> Andrzej
>
>
>>
>>>> +
>>>> +/**
>>>> + * safe_conversion - perform a type conversion (cast) of an source
>>>> value into
>>>> + * a new variable, checking that the destination is large enough to
>>>> hold the
>>>> + * source value.
>>>> + * @ptr: Destination pointer address
>>>> + * @value: Source value
>>>> + *
>>>> + * Returns:
>>>> + * If the value would overflow the destination, it returns false.
>>>> + */
>>>> +#define safe_conversion(ptr, value) ({ \
>>>> +    typeof(value) __v = (value); \
>>>> +    typeof(ptr) __ptr = (ptr); \
>>>> +    overflows_type(__v, *__ptr) ? 0 : ((*__ptr =
>>>> (typeof(*__ptr))__v), 1); \
>>>> +})
>>
>> I try to avoid "safe" as an adjective for interface names, since it
>> doesn't really answer "safe from what?" This looks more like "assign, but
>> zero when out of bounds". And it can be built from existing macros here:
>>
>>     if (check_add_overflow(0, value, ptr))
>>         *ptr = 0;
>>
>> I actually want to push back on this a bit, because there can still be
>> logic bugs built around this kind of primitive. Shouldn't out-of-bounds
>> assignments be seen as a direct failure? I would think this would be
>> sufficient:
>>
>> #define check_assign(value, ptr)    check_add_overflow(0, value, ptr)
>>
>> And callers would do:
>>
>>     if (check_assign(value, &var))
>>         return -EINVAL;
>>
>> etc.
>>
>>
>

2022-08-22 20:04:56

by Gwan-gyeong Mun

[permalink] [raw]
Subject: Re: [PATCH v7 1/8] overflow: Move and add few utility macros into overflow



On 8/22/22 11:05 PM, Andrzej Hajda wrote:
> On 18.08.2022 02:12, Kees Cook wrote:
>> On Thu, Aug 18, 2022 at 01:07:29AM +0200, Andi Shyti wrote:
>>> Hi Kees,
>>>
>>> would you mind taking a look at this patch?
>>
>> Hi! Thanks for the heads-up!
>>
>>>
>>> Thanks,
>>> Andi
>>>
>>> On Tue, Aug 16, 2022 at 06:35:18PM +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 truncation
>>>> between data
>>>> types. And it adds safe_conversion() macro which performs a type
>>>> conversion
>>>> (cast) of an source value into a new variable, checking that the
>>>> destination is large enough to hold the source value. And the
>>>> functionality
>>>> of overflows_type has been improved to handle the signbit.
>>>> The is_unsigned_type macro has been added to check the sign bit of the
>>>> built-in type.
>>>>
>>>> v3: Add is_type_unsigned() macro (Mauro)
>>>>      Modify overflows_type() macro to consider signed data types
>>>> (Mauro)
>>>>      Fix the problem that safe_conversion() macro always returns true
>>>> v4: Fix kernel-doc markups
>>>> v6: Move macro addition location so that it can be used by other
>>>> than drm
>>>>      subsystem (Jani, Mauro, Andi)
>>>>      Change is_type_unsigned to is_unsigned_type to have the same
>>>> name form
>>>>      as is_signed_type macro
>>>>
>>>> Signed-off-by: Gwan-gyeong Mun <[email protected]>
>>>> Cc: Thomas Hellström <[email protected]>
>>>> Cc: Matthew Auld <[email protected]>
>>>> Cc: Nirmoy Das <[email protected]>
>>>> Cc: Jani Nikula <[email protected]>
>>>> Cc: Andi Shyti <[email protected]>
>>>> Reviewed-by: Mauro Carvalho Chehab <[email protected]> (v5)
>>>> ---
>>>>   drivers/gpu/drm/i915/i915_utils.h |  5 +--
>>>>   include/linux/overflow.h          | 54
>>>> +++++++++++++++++++++++++++++++
>>>>   2 files changed, 55 insertions(+), 4 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/i915/i915_utils.h
>>>> b/drivers/gpu/drm/i915/i915_utils.h
>>>> index c10d68cdc3ca..eb0ded23fa9c 100644
>>>> --- a/drivers/gpu/drm/i915/i915_utils.h
>>>> +++ b/drivers/gpu/drm/i915/i915_utils.h
>>>> @@ -32,6 +32,7 @@
>>>>   #include <linux/types.h>
>>>>   #include <linux/workqueue.h>
>>>>   #include <linux/sched/clock.h>
>>>> +#include <linux/overflow.h>
>>>>   #ifdef CONFIG_X86
>>>>   #include <asm/hypervisor.h>
>>>> @@ -111,10 +112,6 @@ bool i915_error_injected(void);
>>>>   #define range_overflows_end_t(type, start, size, max) \
>>>>       range_overflows_end((type)(start), (type)(size), (type)(max))
>>>> -/* Note we don't consider signbits :| */
>>>> -#define overflows_type(x, T) \
>>>> -    (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T))
>>>> -
>>>>   #define ptr_mask_bits(ptr, n) ({                    \
>>>>       unsigned long __v = (unsigned long)(ptr);            \
>>>>       (typeof(ptr))(__v & -BIT(n));                    \
>>>> diff --git a/include/linux/overflow.h b/include/linux/overflow.h
>>>> index f1221d11f8e5..462a03454377 100644
>>>> --- a/include/linux/overflow.h
>>>> +++ b/include/linux/overflow.h
>>>> @@ -35,6 +35,60 @@
>>>>   #define type_max(T) ((T)((__type_half_max(T) - 1) +
>>>> __type_half_max(T)))
>>>>   #define type_min(T) ((T)((T)-type_max(T)-(T)1))
>>>> +/**
>>>> + * is_unsigned_type - helper for checking data type which is an
>>>> unsigned data
>>>> + * type or not
>>>> + * @x: The data type to check
>>>> + *
>>>> + * Returns:
>>>> + * True if the data type is an unsigned data type, false otherwise.
>>>> + */
>>>> +#define is_unsigned_type(x) ((typeof(x))-1 >= (typeof(x))0)
>>
>> I'd rather not have separate logic for this. Instead, I'd like it to be:
>>
>> #define is_unsigned_type(x) (!is_signed_type(x))
>>
>>>> +
>>>> +/**
>>>> + * overflows_type - helper for checking the truncation between data
>>>> types
>>>> + * @x: Source for overflow type comparison
>>>> + * @T: Destination for overflow type comparison
>>>> + *
>>>> + * It compares the values and size of each data type between the
>>>> first and
>>>> + * second argument to check whether truncation can occur when
>>>> assigning the
>>>> + * first argument to the variable of the second argument.
>>>> + * Source and Destination can be used with or without sign bit.
>>>> + * Composite data structures such as union and structure are not
>>>> considered.
>>>> + * Enum data types are not considered.
>>>> + * Floating point data types are not considered.
>>>> + *
>>>> + * Returns:
>>>> + * True if truncation can occur, false otherwise.
>>>> + */
>>>> +#define overflows_type(x, T) \
>>>> +    (is_unsigned_type(x) ? \
>>>> +        is_unsigned_type(T) ? \
>>>> +            (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1
>>>> : 0 \
>>>> +            : (sizeof(x) >= sizeof(T) && (x) >> (BITS_PER_TYPE(T) -
>>>> 1)) ? 1 : 0 \
>>>> +    : is_unsigned_type(T) ? \
>>>> +        ((x) < 0) ? 1 : (sizeof(x) > sizeof(T) && (x) >>
>>>> BITS_PER_TYPE(T)) ? 1 : 0 \
>>>> +        : (sizeof(x) > sizeof(T)) ? \
>>>> +            ((x) < 0) ? (((x) * -1) >> BITS_PER_TYPE(T)) ? 1 : 0 \
>>>> +                : ((x) >> BITS_PER_TYPE(T)) ? 1 : 0 \
>>>> +            : 0)
>>
>> Like the other, I'd much rather this was rephrased in terms of the
>> existing macros (e.g. type_min()/type_max().)
>
>
Thanks for all of your comments.

The version that implements overflows_type() using type_min() and
type_max() includes modifications to the following macros.
In implementations of is_signed_type(), __type_half_max(), type_max(),
type_min(), where types are used as variables, the addition of typeof()
is necessary.
And the operation was confirmed through previously shared test cases.
https://patchwork.freedesktop.org/patch/492374/?series=104704&rev=3

#define is_signed_type(x) (((typeof(x))(-1)) < (typeof(x))1)
#define is_unsigned_type(x) (!is_signed_type(x))
#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))


#define overflows_type(x, T) __must_check_overflow( \
is_unsigned_type(x) ? \
x > type_max(T) ? 1 : 0 \
: is_unsigned_type(T) ? \
x < 0 || x > type_max(T) ? 1 : 0 \
: x < type_min(T) || x > type_max(T) ? 1 : 0 )


> I am not sure how it could be rephrased with type_(min|max), but I guess
> the shortest could be sth like:
>
> #define overflows_type(x, T) __builtin_add_overflow_p(x, (typeof(T))0,
> (typeof(T))0)
>
And it was confirmed that the method using the gcc built-in functions
suggested by Andrzej works the same in all cases where it is used.

#define overflows_type(x, T) __must_check_overflow(({ \
typeof(T) r = 0; \
__builtin_add_overflow_p((x), r, r); \
}))

And if you refer to this link
(https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html), it
is explained like this.
The compiler will attempt to use hardware instructions to implement
these built-in functions where possible, like conditional jump on
overflow after addition, conditional jump on carry etc.

Andrzej's suggested way seems better to me. What do you think? Kees
Cook, can I ask for your feedback?

Additionally, unlike the first implemented method (v7's overflows_type()
macro), the macros tested above generate errors at build time for
pointer types.
__type_half_max() throws error "error: invalid operands to binary <<"
or
For __builtin_add_overflow_p() I get the error
"__builtin_add_overflow_p' does not have integral type".

So, overflow check for pointer type was confirmed by adding the
following macro.

#define overflows_ptr(x, T) __must_check_overflow(({ \
typecheck_pointer(T); \
((x) < 0) ? 1 : (sizeof(x) > sizeof(T) && (x) >> BITS_PER_TYPE(T)) ? 1
: 0; \
}))

> Regards
> Andrzej
>
>
>>
>>>> +
>>>> +/**
>>>> + * safe_conversion - perform a type conversion (cast) of an source
>>>> value into
>>>> + * a new variable, checking that the destination is large enough to
>>>> hold the
>>>> + * source value.
>>>> + * @ptr: Destination pointer address
>>>> + * @value: Source value
>>>> + *
>>>> + * Returns:
>>>> + * If the value would overflow the destination, it returns false.
>>>> + */
>>>> +#define safe_conversion(ptr, value) ({ \
>>>> +    typeof(value) __v = (value); \
>>>> +    typeof(ptr) __ptr = (ptr); \
>>>> +    overflows_type(__v, *__ptr) ? 0 : ((*__ptr =
>>>> (typeof(*__ptr))__v), 1); \
>>>> +})
>>
>> I try to avoid "safe" as an adjective for interface names, since it
>> doesn't really answer "safe from what?" This looks more like "assign, but
>> zero when out of bounds". And it can be built from existing macros here:
>>
>>     if (check_add_overflow(0, value, ptr))
>>         *ptr = 0;
>>
>> I actually want to push back on this a bit, because there can still be
>> logic bugs built around this kind of primitive. Shouldn't out-of-bounds
>> assignments be seen as a direct failure? I would think this would be
>> sufficient:
>>
>> #define check_assign(value, ptr)    check_add_overflow(0, value, ptr)
>>
>> And callers would do:
>>
>>     if (check_assign(value, &var))
>>         return -EINVAL;
>>
Yes, I also like check_assign() you suggested more than safe_conversion.
As shown below, it would be more readable to return true when assign
succeeds and false when it fails. What do you think?
/**
* check_assign - perform a type conversion (cast) of an source value into
* a new variable, checking that the destination is large enough to
hold the
* source value.
*
* @value: Source value
* @ptr: Destination pointer address, If the pointer type is not used,
a warning message is output during build.
*
* Returns:
* If the value would overflow the destination, it returns false. If
not return true.
*/
#define check_assign(value, ptr) __must_check_overflow(({ \
typecheck_pointer(ptr); \
!__builtin_add_overflow(0, value, ptr); \
}))

Br,
G.G.
>> etc.
>>
>>
>

2022-08-22 20:30:32

by Kees Cook

[permalink] [raw]
Subject: Re: [PATCH v7 1/8] overflow: Move and add few utility macros into overflow

On Tue, Aug 23, 2022 at 04:32:10AM +0900, Gwan-gyeong Mun wrote:
> On 8/22/22 11:05 PM, Andrzej Hajda wrote:
> > On 18.08.2022 02:12, Kees Cook wrote:
> > > On Thu, Aug 18, 2022 at 01:07:29AM +0200, Andi Shyti wrote:
> > > > [...]
> > > > > +#define safe_conversion(ptr, value) ({ \
> > > > > +??? typeof(value) __v = (value); \
> > > > > +??? typeof(ptr) __ptr = (ptr); \
> > > > > +??? overflows_type(__v, *__ptr) ? 0 : ((*__ptr =
> > > > > (typeof(*__ptr))__v), 1); \
> > > > > +})
> > >
> > > I try to avoid "safe" as an adjective for interface names, since it
> > > doesn't really answer "safe from what?" This looks more like "assign, but
> > > zero when out of bounds". And it can be built from existing macros here:
> > >
> > > ????if (check_add_overflow(0, value, ptr))
> > > ??????? *ptr = 0;
> > >
> > > I actually want to push back on this a bit, because there can still be
> > > logic bugs built around this kind of primitive. Shouldn't out-of-bounds
> > > assignments be seen as a direct failure? I would think this would be
> > > sufficient:
> > >
> > > #define check_assign(value, ptr)??? check_add_overflow(0, value, ptr)
> > >
> > > And callers would do:
> > >
> > > ????if (check_assign(value, &var))
> > > ??????? return -EINVAL;
> > >
> Yes, I also like check_assign() you suggested more than safe_conversion.
> As shown below, it would be more readable to return true when assign
> succeeds and false when it fails. What do you think?

No, this inverts the style of all the other check_*() functions, so it
should remain "non-zero is failure".

> /**
> * check_assign - perform a type conversion (cast) of an source value into
> * a new variable, checking that the destination is large enough to hold the
> * source value.
> *
> * @value: Source value
> * @ptr: Destination pointer address, If the pointer type is not used, a
> warning message is output during build.
> *
> * Returns:
> * If the value would overflow the destination, it returns false. If not
> return true.
> */
> #define check_assign(value, ptr) __must_check_overflow(({ \
> typecheck_pointer(ptr); \
> !__builtin_add_overflow(0, value, ptr); \
> }))

Please don't use the __builtin*s, instead stick to the check_* family,
as they correctly wrap the builtins and perform type checking, etc. As
mentioned, check_assign() should just be:

#define check_assign(value, ptr)??? check_add_overflow(0, value, ptr)

I don't think any of the other code is needed? What's the use-case for
the other stuff? i.e. Why does anything need overflows_type()?

-Kees

--
Kees Cook

2022-08-23 02:40:40

by Gwan-gyeong Mun

[permalink] [raw]
Subject: Re: [PATCH v7 1/8] overflow: Move and add few utility macros into overflow



On 8/23/22 5:12 AM, Kees Cook wrote:
> On Tue, Aug 23, 2022 at 04:32:10AM +0900, Gwan-gyeong Mun wrote:
>> On 8/22/22 11:05 PM, Andrzej Hajda wrote:
>>> On 18.08.2022 02:12, Kees Cook wrote:
>>>> On Thu, Aug 18, 2022 at 01:07:29AM +0200, Andi Shyti wrote:
>>>>> [...]
>>>>>> +#define safe_conversion(ptr, value) ({ \
>>>>>> +    typeof(value) __v = (value); \
>>>>>> +    typeof(ptr) __ptr = (ptr); \
>>>>>> +    overflows_type(__v, *__ptr) ? 0 : ((*__ptr =
>>>>>> (typeof(*__ptr))__v), 1); \
>>>>>> +})
>>>>
>>>> I try to avoid "safe" as an adjective for interface names, since it
>>>> doesn't really answer "safe from what?" This looks more like "assign, but
>>>> zero when out of bounds". And it can be built from existing macros here:
>>>>
>>>>     if (check_add_overflow(0, value, ptr))
>>>>         *ptr = 0;
>>>>
>>>> I actually want to push back on this a bit, because there can still be
>>>> logic bugs built around this kind of primitive. Shouldn't out-of-bounds
>>>> assignments be seen as a direct failure? I would think this would be
>>>> sufficient:
>>>>
>>>> #define check_assign(value, ptr)    check_add_overflow(0, value, ptr)
>>>>
>>>> And callers would do:
>>>>
>>>>     if (check_assign(value, &var))
>>>>         return -EINVAL;
>>>>
>> Yes, I also like check_assign() you suggested more than safe_conversion.
>> As shown below, it would be more readable to return true when assign
>> succeeds and false when it fails. What do you think?
>
> No, this inverts the style of all the other check_*() functions, so it
> should remain "non-zero is failure".
>
Hi Kees,
Yes, I will not invert this part as you commented.
>> /**
>> * check_assign - perform a type conversion (cast) of an source value into
>> * a new variable, checking that the destination is large enough to hold the
>> * source value.
>> *
>> * @value: Source value
>> * @ptr: Destination pointer address, If the pointer type is not used, a
>> warning message is output during build.
>> *
>> * Returns:
>> * If the value would overflow the destination, it returns false. If not
>> return true.
>> */
>> #define check_assign(value, ptr) __must_check_overflow(({ \
>> typecheck_pointer(ptr); \
>> !__builtin_add_overflow(0, value, ptr); \
>> }))
>
> Please don't use the __builtin*s, instead stick to the check_* family,
> as they correctly wrap the builtins and perform type checking, etc. As
> mentioned, check_assign() should just be:
>
> #define check_assign(value, ptr)    check_add_overflow(0, value, ptr)
>
> I don't think any of the other code is needed? What's the use-case for
> the other stuff? i.e. Why does anything need overflows_type()?
>
And, the reason for using the __builtin_add_overflow() built-in function
directly instead of using the check_add_overflow() function is ,

#define check_add_overflow(a, b, d) __must_check_overflow(({ \
typeof(a) __a = (a); \
typeof(b) __b = (b); \
typeof(d) __d = (d); \
(void) (&__a == &__b); \
(void) (&__a == __d); \
__builtin_add_overflow(__a, __b, __d); \
}))

In this part of the implementation of check_add_overflow()
(void) (&__a == &__b);
(void) (&__a == __d);


When comparing the pointer types of a, b, and d, if the pointer types of
source and ptr in check_assign() are different, a warning may occur when
building, I used the __builtin_add_overflow() built-in function directly.

Br,

G.G.
> -Kees
>