summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorKees Cook <keescook@chromium.org>2021-09-18 15:17:53 -0700
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2024-09-04 13:14:51 +0200
commit1ab137a90eeaea265fa205538e28f3a54f7fd730 (patch)
tree4bc60fdd365ea363236b553fb2d7b6ae94d5479c /lib
parent60f05081e09791370ef5c456febec5bd3afeb9e9 (diff)
downloadlinux-stable-1ab137a90eeaea265fa205538e28f3a54f7fd730.tar.gz
linux-stable-1ab137a90eeaea265fa205538e28f3a54f7fd730.tar.bz2
linux-stable-1ab137a90eeaea265fa205538e28f3a54f7fd730.zip
overflow: Implement size_t saturating arithmetic helpers
commit e1be43d9b5d0d1310dbd90185a8e5c7145dde40f upstream. In order to perform more open-coded replacements of common allocation size arithmetic, the kernel needs saturating (SIZE_MAX) helpers for multiplication, addition, and subtraction. For example, it is common in allocators, especially on realloc, to add to an existing size: p = krealloc(map->patch, sizeof(struct reg_sequence) * (map->patch_regs + num_regs), GFP_KERNEL); There is no existing saturating replacement for this calculation, and just leaving the addition open coded inside array_size() could potentially overflow as well. For example, an overflow in an expression for a size_t argument might wrap to zero: array_size(anything, something_at_size_max + 1) == 0 Introduce size_mul(), size_add(), and size_sub() helpers that implicitly promote arguments to size_t and saturated calculations for use in allocations. With these helpers it is also possible to redefine array_size(), array3_size(), flex_array_size(), and struct_size() in terms of the new helpers. As with the check_*_overflow() helpers, the new helpers use __must_check, though what is really desired is a way to make sure that assignment is only to a size_t lvalue. Without this, it's still possible to introduce overflow/underflow via type conversion (i.e. from size_t to int). Enforcing this will currently need to be left to static analysis or future use of -Wconversion. Additionally update the overflow unit tests to force runtime evaluation for the pathological cases. Cc: Rasmus Villemoes <linux@rasmusvillemoes.dk> Cc: Gustavo A. R. Silva <gustavoars@kernel.org> Cc: Nathan Chancellor <nathan@kernel.org> Cc: Jason Gunthorpe <jgg@ziepe.ca> Cc: Nick Desaulniers <ndesaulniers@google.com> Cc: Leon Romanovsky <leon@kernel.org> Cc: Keith Busch <kbusch@kernel.org> Cc: Len Baker <len.baker@gmx.com> Signed-off-by: Kees Cook <keescook@chromium.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'lib')
-rw-r--r--lib/test_overflow.c98
1 files changed, 98 insertions, 0 deletions
diff --git a/lib/test_overflow.c b/lib/test_overflow.c
index 7a4b6f6c5473..7a5a5738d2d2 100644
--- a/lib/test_overflow.c
+++ b/lib/test_overflow.c
@@ -588,12 +588,110 @@ static int __init test_overflow_allocation(void)
return err;
}
+struct __test_flex_array {
+ unsigned long flags;
+ size_t count;
+ unsigned long data[];
+};
+
+static int __init test_overflow_size_helpers(void)
+{
+ struct __test_flex_array *obj;
+ int count = 0;
+ int err = 0;
+ int var;
+
+#define check_one_size_helper(expected, func, args...) ({ \
+ bool __failure = false; \
+ size_t _r; \
+ \
+ _r = func(args); \
+ if (_r != (expected)) { \
+ pr_warn("expected " #func "(" #args ") " \
+ "to return %zu but got %zu instead\n", \
+ (size_t)(expected), _r); \
+ __failure = true; \
+ } \
+ count++; \
+ __failure; \
+})
+
+ var = 4;
+ err |= check_one_size_helper(20, size_mul, var++, 5);
+ err |= check_one_size_helper(20, size_mul, 4, var++);
+ err |= check_one_size_helper(0, size_mul, 0, 3);
+ err |= check_one_size_helper(0, size_mul, 3, 0);
+ err |= check_one_size_helper(6, size_mul, 2, 3);
+ err |= check_one_size_helper(SIZE_MAX, size_mul, SIZE_MAX, 1);
+ err |= check_one_size_helper(SIZE_MAX, size_mul, SIZE_MAX, 3);
+ err |= check_one_size_helper(SIZE_MAX, size_mul, SIZE_MAX, -3);
+
+ var = 4;
+ err |= check_one_size_helper(9, size_add, var++, 5);
+ err |= check_one_size_helper(9, size_add, 4, var++);
+ err |= check_one_size_helper(9, size_add, 9, 0);
+ err |= check_one_size_helper(9, size_add, 0, 9);
+ err |= check_one_size_helper(5, size_add, 2, 3);
+ err |= check_one_size_helper(SIZE_MAX, size_add, SIZE_MAX, 1);
+ err |= check_one_size_helper(SIZE_MAX, size_add, SIZE_MAX, 3);
+ err |= check_one_size_helper(SIZE_MAX, size_add, SIZE_MAX, -3);
+
+ var = 4;
+ err |= check_one_size_helper(1, size_sub, var--, 3);
+ err |= check_one_size_helper(1, size_sub, 4, var--);
+ err |= check_one_size_helper(1, size_sub, 3, 2);
+ err |= check_one_size_helper(9, size_sub, 9, 0);
+ err |= check_one_size_helper(SIZE_MAX, size_sub, 9, -3);
+ err |= check_one_size_helper(SIZE_MAX, size_sub, 0, 9);
+ err |= check_one_size_helper(SIZE_MAX, size_sub, 2, 3);
+ err |= check_one_size_helper(SIZE_MAX, size_sub, SIZE_MAX, 0);
+ err |= check_one_size_helper(SIZE_MAX, size_sub, SIZE_MAX, 10);
+ err |= check_one_size_helper(SIZE_MAX, size_sub, 0, SIZE_MAX);
+ err |= check_one_size_helper(SIZE_MAX, size_sub, 14, SIZE_MAX);
+ err |= check_one_size_helper(SIZE_MAX - 2, size_sub, SIZE_MAX - 1, 1);
+ err |= check_one_size_helper(SIZE_MAX - 4, size_sub, SIZE_MAX - 1, 3);
+ err |= check_one_size_helper(1, size_sub, SIZE_MAX - 1, -3);
+
+ var = 4;
+ err |= check_one_size_helper(4 * sizeof(*obj->data),
+ flex_array_size, obj, data, var++);
+ err |= check_one_size_helper(5 * sizeof(*obj->data),
+ flex_array_size, obj, data, var++);
+ err |= check_one_size_helper(0, flex_array_size, obj, data, 0);
+ err |= check_one_size_helper(sizeof(*obj->data),
+ flex_array_size, obj, data, 1);
+ err |= check_one_size_helper(7 * sizeof(*obj->data),
+ flex_array_size, obj, data, 7);
+ err |= check_one_size_helper(SIZE_MAX,
+ flex_array_size, obj, data, -1);
+ err |= check_one_size_helper(SIZE_MAX,
+ flex_array_size, obj, data, SIZE_MAX - 4);
+
+ var = 4;
+ err |= check_one_size_helper(sizeof(*obj) + (4 * sizeof(*obj->data)),
+ struct_size, obj, data, var++);
+ err |= check_one_size_helper(sizeof(*obj) + (5 * sizeof(*obj->data)),
+ struct_size, obj, data, var++);
+ err |= check_one_size_helper(sizeof(*obj), struct_size, obj, data, 0);
+ err |= check_one_size_helper(sizeof(*obj) + sizeof(*obj->data),
+ struct_size, obj, data, 1);
+ err |= check_one_size_helper(SIZE_MAX,
+ struct_size, obj, data, -3);
+ err |= check_one_size_helper(SIZE_MAX,
+ struct_size, obj, data, SIZE_MAX - 3);
+
+ pr_info("%d overflow size helper tests finished\n", count);
+
+ return err;
+}
+
static int __init test_module_init(void)
{
int err = 0;
err |= test_overflow_calculation();
err |= test_overflow_shift();
+ err |= test_overflow_size_helpers();
err |= test_overflow_allocation();
if (err) {