| From 610b15c50e86eb1e4b77274fabcaea29ac72d6a8 Mon Sep 17 00:00:00 2001 |
| From: Kees Cook <keescook@chromium.org> |
| Date: Mon, 7 May 2018 16:47:02 -0700 |
| Subject: overflow.h: Add allocation size calculation helpers |
| |
| From: Kees Cook <keescook@chromium.org> |
| |
| commit 610b15c50e86eb1e4b77274fabcaea29ac72d6a8 upstream. |
| |
| 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> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/md/dm-table.c | 10 +++--- |
| include/linux/overflow.h | 73 +++++++++++++++++++++++++++++++++++++++++++++++ |
| 2 files changed, 78 insertions(+), 5 deletions(-) |
| |
| --- a/drivers/md/dm-table.c |
| +++ b/drivers/md/dm-table.c |
| @@ -510,14 +510,14 @@ static int adjoin(struct dm_table *table |
| * 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; |
| @@ -525,8 +525,8 @@ static char **realloc_argv(unsigned *arr |
| } |
| 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); |
| --- a/include/linux/overflow.h |
| +++ b/include/linux/overflow.h |
| @@ -233,4 +233,77 @@ |
| (*_d >> _to_shift) != _a); \ |
| }) |
| |
| +/** |
| + * 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 */ |