overflow.h: Add allocation size calculation helpers
authorKees Cook <keescook@chromium.org>
Mon, 7 May 2018 23:47:02 +0000 (16:47 -0700)
committerKees Cook <keescook@chromium.org>
Tue, 5 Jun 2018 19:16:51 +0000 (12:16 -0700)
In preparation for replacing unchecked overflows for memory allocations,
this creates helpers for the 3 most common calculations:

array_size(a, b): 2-dimensional array
array3_size(a, b, c): 3-dimensional array
struct_size(ptr, member, n): struct followed by n-many trailing members

Each of these return SIZE_MAX on overflow instead of wrapping around.

(Additionally renames a variable named "array_size" to avoid future
collision.)

Co-developed-by: Matthew Wilcox <mawilcox@microsoft.com>
Signed-off-by: Kees Cook <keescook@chromium.org>
drivers/md/dm-table.c
include/linux/overflow.h

index 0589a4da12bbed66420052174d3818d3c2b4ff6c..caa51dd351b668cef9735b1f64f317faa126ff26 100644 (file)
@@ -548,14 +548,14 @@ static int adjoin(struct dm_table *table, struct dm_target *ti)
  * On the other hand, dm-switch needs to process bulk data using messages and
  * excessive use of GFP_NOIO could cause trouble.
  */
-static char **realloc_argv(unsigned *array_size, char **old_argv)
+static char **realloc_argv(unsigned *size, char **old_argv)
 {
        char **argv;
        unsigned new_size;
        gfp_t gfp;
 
-       if (*array_size) {
-               new_size = *array_size * 2;
+       if (*size) {
+               new_size = *size * 2;
                gfp = GFP_KERNEL;
        } else {
                new_size = 8;
@@ -563,8 +563,8 @@ static char **realloc_argv(unsigned *array_size, char **old_argv)
        }
        argv = kmalloc(new_size * sizeof(*argv), gfp);
        if (argv) {
-               memcpy(argv, old_argv, *array_size * sizeof(*argv));
-               *array_size = new_size;
+               memcpy(argv, old_argv, *size * sizeof(*argv));
+               *size = new_size;
        }
 
        kfree(old_argv);
index c8890ec358a714033373d6a3a3710fb92abcf958..8712ff70995f42e4357eddb8bd9601bc42b246c0 100644 (file)
 
 #endif /* COMPILER_HAS_GENERIC_BUILTIN_OVERFLOW */
 
+/**
+ * array_size() - Calculate size of 2-dimensional array.
+ *
+ * @a: dimension one
+ * @b: dimension two
+ *
+ * Calculates size of 2-dimensional array: @a * @b.
+ *
+ * Returns: number of bytes needed to represent the array or SIZE_MAX on
+ * overflow.
+ */
+static inline __must_check size_t array_size(size_t a, size_t b)
+{
+       size_t bytes;
+
+       if (check_mul_overflow(a, b, &bytes))
+               return SIZE_MAX;
+
+       return bytes;
+}
+
+/**
+ * array3_size() - Calculate size of 3-dimensional array.
+ *
+ * @a: dimension one
+ * @b: dimension two
+ * @c: dimension three
+ *
+ * Calculates size of 3-dimensional array: @a * @b * @c.
+ *
+ * Returns: number of bytes needed to represent the array or SIZE_MAX on
+ * overflow.
+ */
+static inline __must_check size_t array3_size(size_t a, size_t b, size_t c)
+{
+       size_t bytes;
+
+       if (check_mul_overflow(a, b, &bytes))
+               return SIZE_MAX;
+       if (check_mul_overflow(bytes, c, &bytes))
+               return SIZE_MAX;
+
+       return bytes;
+}
+
+static inline __must_check size_t __ab_c_size(size_t n, size_t size, size_t c)
+{
+       size_t bytes;
+
+       if (check_mul_overflow(n, size, &bytes))
+               return SIZE_MAX;
+       if (check_add_overflow(bytes, c, &bytes))
+               return SIZE_MAX;
+
+       return bytes;
+}
+
+/**
+ * struct_size() - Calculate size of structure with trailing array.
+ * @p: Pointer to the structure.
+ * @member: Name of the array member.
+ * @n: Number of elements in the array.
+ *
+ * Calculates size of memory needed for structure @p followed by an
+ * array of @n @member elements.
+ *
+ * Return: number of bytes needed or SIZE_MAX on overflow.
+ */
+#define struct_size(p, member, n)                                      \
+       __ab_c_size(n,                                                  \
+                   sizeof(*(p)->member) + __must_be_array((p)->member),\
+                   sizeof(*(p)))
+
 #endif /* __LINUX_OVERFLOW_H */