* [PATCH 0/5] overflow: Introduce wrapping helpers
@ 2024-01-29 18:34 Kees Cook
2024-01-29 18:34 ` [PATCH 1/5] overflow: Adjust check_*_overflow() kern-doc to reflect results Kees Cook
` (4 more replies)
0 siblings, 5 replies; 10+ messages in thread
From: Kees Cook @ 2024-01-29 18:34 UTC (permalink / raw)
To: Rasmus Villemoes
Cc: Kees Cook, Gustavo A. R. Silva, Nathan Chancellor, Bill Wendling,
Justin Stitt, Mark Rutland, Miguel Ojeda, Marco Elver,
linux-kernel, linux-hardening, llvm
Hi,
In preparation for gaining instrumentation for signed[1], unsigned[2], and
pointer[3] wrap-around, expand the overflow header to include wrap-around
helpers that can be used to annotate arithmetic where wrapped calculations
are expected (e.g. atomics).
After spending time getting the unsigned integer wrap-around sanitizer
running warning-free on a basic x86_64 boot[4], I think the *_wrap()
helpers first argument being the output type makes the most sense (as
suggested by Rasmus).
-Kees
Link: https://github.com/KSPP/linux/issues/26 [1]
Link: https://github.com/KSPP/linux/issues/27 [2]
Link: https://github.com/KSPP/linux/issues/344 [3]
Link: https://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git/log/?h=devel/overflow/enable-unsigned-sanitizer [4]
Kees Cook (5):
overflow: Adjust check_*_overflow() kern-doc to reflect results
overflow: Expand check_add_overflow() for pointer addition
overflow: Introduce add_would_overflow()
overflow: Introduce add_wrap(), sub_wrap(), and mul_wrap()
overflow: Introduce inc_wrap() and dec_wrap()
include/linux/compiler_types.h | 10 ++
include/linux/overflow.h | 164 ++++++++++++++++++++++++++++++---
lib/overflow_kunit.c | 77 ++++++++++++++--
3 files changed, 228 insertions(+), 23 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH 1/5] overflow: Adjust check_*_overflow() kern-doc to reflect results 2024-01-29 18:34 [PATCH 0/5] overflow: Introduce wrapping helpers Kees Cook @ 2024-01-29 18:34 ` Kees Cook 2024-01-29 18:34 ` [PATCH 2/5] overflow: Expand check_add_overflow() for pointer addition Kees Cook ` (3 subsequent siblings) 4 siblings, 0 replies; 10+ messages in thread From: Kees Cook @ 2024-01-29 18:34 UTC (permalink / raw) To: Rasmus Villemoes Cc: Kees Cook, Gustavo A. R. Silva, linux-hardening, Nathan Chancellor, Bill Wendling, Justin Stitt, Mark Rutland, Miguel Ojeda, Marco Elver, linux-kernel, llvm The check_*_overflow() helpers will return results with potentially wrapped-around values. These values have always been checked by the selftests, so avoid the confusing language in the kern-doc. The idea of "safe for use" was relative to the expectation of whether or not the caller wants a wrapped value -- the calculation itself will always follow arithmetic wrapping rules. Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org> Cc: linux-hardening@vger.kernel.org Signed-off-by: Kees Cook <keescook@chromium.org> --- include/linux/overflow.h | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/include/linux/overflow.h b/include/linux/overflow.h index 7b5cf4a5cd19..4e741ebb8005 100644 --- a/include/linux/overflow.h +++ b/include/linux/overflow.h @@ -57,11 +57,9 @@ static inline bool __must_check __must_check_overflow(bool overflow) * @b: second addend * @d: pointer to store sum * - * Returns 0 on success. + * Returns 0 on success, 1 on wrap-around. * - * *@d holds the results of the attempted addition, but is not considered - * "safe for use" on a non-zero return value, which indicates that the - * sum has overflowed or been truncated. + * *@d holds the results of the attempted addition, which may wrap-around. */ #define check_add_overflow(a, b, d) \ __must_check_overflow(__builtin_add_overflow(a, b, d)) @@ -72,11 +70,9 @@ static inline bool __must_check __must_check_overflow(bool overflow) * @b: subtrahend; value to subtract from @a * @d: pointer to store difference * - * Returns 0 on success. + * Returns 0 on success, 1 on wrap-around. * - * *@d holds the results of the attempted subtraction, but is not considered - * "safe for use" on a non-zero return value, which indicates that the - * difference has underflowed or been truncated. + * *@d holds the results of the attempted subtraction, which may wrap-around. */ #define check_sub_overflow(a, b, d) \ __must_check_overflow(__builtin_sub_overflow(a, b, d)) @@ -87,11 +83,9 @@ static inline bool __must_check __must_check_overflow(bool overflow) * @b: second factor * @d: pointer to store product * - * Returns 0 on success. + * Returns 0 on success, 1 on wrap-around. * - * *@d holds the results of the attempted multiplication, but is not - * considered "safe for use" on a non-zero return value, which indicates - * that the product has overflowed or been truncated. + * *@d holds the results of the attempted multiplication, which may wrap-around. */ #define check_mul_overflow(a, b, d) \ __must_check_overflow(__builtin_mul_overflow(a, b, d)) -- 2.34.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 2/5] overflow: Expand check_add_overflow() for pointer addition 2024-01-29 18:34 [PATCH 0/5] overflow: Introduce wrapping helpers Kees Cook 2024-01-29 18:34 ` [PATCH 1/5] overflow: Adjust check_*_overflow() kern-doc to reflect results Kees Cook @ 2024-01-29 18:34 ` Kees Cook 2024-01-29 18:34 ` [PATCH 3/5] overflow: Introduce add_would_overflow() Kees Cook ` (2 subsequent siblings) 4 siblings, 0 replies; 10+ messages in thread From: Kees Cook @ 2024-01-29 18:34 UTC (permalink / raw) To: Rasmus Villemoes Cc: Kees Cook, Gustavo A. R. Silva, Andrew Morton, Nathan Chancellor, Nick Desaulniers, Bill Wendling, Justin Stitt, llvm, linux-hardening, Mark Rutland, Miguel Ojeda, Marco Elver, linux-kernel The check_add_overflow() helper is mostly a wrapper around __builtin_add_overflow(), but GCC and Clang refuse to operate on pointer arguments that would normally be allowed if the addition were open-coded. For example, we have many places where pointer overflow is tested: struct foo *ptr; ... /* Check for overflow */ if (ptr + count < ptr) ... And in order to avoid running into the overflow sanitizers in the future, we need to rewrite these "intended" overflow checks: if (check_add_overflow(ptr, count, &result)) ... Frustratingly the argument type validation for __builtin_add_overflow() is done before evaluating __builtin_choose_expr(), so for arguments to be valid simultaneously for sizeof(*p) (when p may not be a pointer), and __builtin_add_overflow(a, ...) (when a may be a pointer), we must introduce wrappers that always produce a specific type (but they are only used in the places where the bogus arguments will be ignored). To test whether a variable is a pointer or not, introduce the __is_ptr() helper, which uses __builtin_classify_type() to find arrays and pointers (via the new __is_ptr_or_array() helper), and then decays arrays into pointers (via the new __decay() helper), to distinguish pointers from arrays. Additionally update the unit tests to cover pointer addition. Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk> Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Nathan Chancellor <nathan@kernel.org> Cc: Nick Desaulniers <ndesaulniers@google.com> Cc: Bill Wendling <morbo@google.com> Cc: Justin Stitt <justinstitt@google.com> Cc: llvm@lists.linux.dev Cc: linux-hardening@vger.kernel.org Signed-off-by: Kees Cook <keescook@chromium.org> --- include/linux/compiler_types.h | 10 +++++ include/linux/overflow.h | 44 ++++++++++++++++++- lib/overflow_kunit.c | 77 ++++++++++++++++++++++++++++++---- 3 files changed, 120 insertions(+), 11 deletions(-) diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index 6f1ca49306d2..d27b58fddfaa 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -375,6 +375,16 @@ struct ftrace_likely_data { /* Are two types/vars the same type (ignoring qualifiers)? */ #define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b)) +/* Is variable addressable? */ +#define __is_ptr_or_array(p) (__builtin_classify_type(p) == 5) + +/* Return an array decayed to a pointer. */ +#define __decay(p) \ + (&*__builtin_choose_expr(__is_ptr_or_array(p), p, NULL)) + +/* Report if variable is a pointer type. */ +#define __is_ptr(p) __same_type(p, __decay(p)) + /* * __unqual_scalar_typeof(x) - Declare an unqualified scalar type, leaving * non-scalar types unchanged. diff --git a/include/linux/overflow.h b/include/linux/overflow.h index 4e741ebb8005..210e5602e89b 100644 --- a/include/linux/overflow.h +++ b/include/linux/overflow.h @@ -51,6 +51,43 @@ static inline bool __must_check __must_check_overflow(bool overflow) return unlikely(overflow); } +/* Always produce an integral variable expression. */ +#define __filter_integral(x) \ + __builtin_choose_expr(!__is_ptr(x), (x), 0) + +/* Always produce a pointer value. */ +#define __filter_ptr(x) \ + __builtin_choose_expr(__is_ptr(x), (x), NULL) + +/* Always produce a pointer to an integral value. */ +#define __filter_ptrint(x) \ + __builtin_choose_expr(!__is_ptr(*(x)), x, &(int){ 0 }) + +/** + * __check_ptr_add_overflow() - Calculate pointer addition with overflow checking + * @a: pointer addend + * @b: numeric addend + * @d: pointer to store sum + * + * Returns 0 on success, 1 on wrap-around. + * + * Do not use this function directly, use check_add_overflow() instead. + * + * *@d holds the results of the attempted addition, which may wrap-around. + */ +#define __check_ptr_add_overflow(a, b, d) \ + ({ \ + typeof(a) __a = (a); \ + typeof(b) __b = (b); \ + size_t __bytes; \ + bool __overflow; \ + \ + /* we want to perform the wrap-around, but retain the result */ \ + __overflow = __builtin_mul_overflow(sizeof(*(__a)), __b, &__bytes); \ + __builtin_add_overflow((unsigned long)(__a), __bytes, (unsigned long *)(d)) || \ + __overflow; \ + }) + /** * check_add_overflow() - Calculate addition with overflow checking * @a: first addend @@ -61,8 +98,11 @@ static inline bool __must_check __must_check_overflow(bool overflow) * * *@d holds the results of the attempted addition, which may wrap-around. */ -#define check_add_overflow(a, b, d) \ - __must_check_overflow(__builtin_add_overflow(a, b, d)) +#define check_add_overflow(a, b, d) \ + __must_check_overflow(__builtin_choose_expr(__is_ptr(a), \ + __check_ptr_add_overflow(__filter_ptr(a), b, d), \ + __builtin_add_overflow(__filter_integral(a), b, \ + __filter_ptrint(d)))) /** * check_sub_overflow() - Calculate subtraction with overflow checking diff --git a/lib/overflow_kunit.c b/lib/overflow_kunit.c index c527f6b75789..2d106e880956 100644 --- a/lib/overflow_kunit.c +++ b/lib/overflow_kunit.c @@ -45,13 +45,18 @@ # define SKIP_64_ON_32(t) do { } while (0) #endif -#define DEFINE_TEST_ARRAY_TYPED(t1, t2, t) \ - static const struct test_ ## t1 ## _ ## t2 ## __ ## t { \ +#define DEFINE_TEST_ARRAY_NAMED_TYPED(n1, n2, n, t1, t2, t) \ + static const struct test_ ## n1 ## _ ## n2 ## __ ## n { \ t1 a; \ t2 b; \ - t sum, diff, prod; \ + t sum; \ + t diff; \ + t prod; \ bool s_of, d_of, p_of; \ - } t1 ## _ ## t2 ## __ ## t ## _tests[] + } n1 ## _ ## n2 ## __ ## n ## _tests[] + +#define DEFINE_TEST_ARRAY_TYPED(t1, t2, t) \ + DEFINE_TEST_ARRAY_NAMED_TYPED(t1, t2, t, t1, t2, t) #define DEFINE_TEST_ARRAY(t) DEFINE_TEST_ARRAY_TYPED(t, t, t) @@ -251,8 +256,10 @@ DEFINE_TEST_ARRAY(s64) = { }; #define check_one_op(t, fmt, op, sym, a, b, r, of) do { \ - int _a_orig = a, _a_bump = a + 1; \ - int _b_orig = b, _b_bump = b + 1; \ + typeof(a + 0) _a_orig = a; \ + typeof(a + 0) _a_bump = a + 1; \ + typeof(b + 0) _b_orig = b; \ + typeof(b + 0) _b_bump = b + 1; \ bool _of; \ t _r; \ \ @@ -260,13 +267,13 @@ DEFINE_TEST_ARRAY(s64) = { KUNIT_EXPECT_EQ_MSG(test, _of, of, \ "expected "fmt" "sym" "fmt" to%s overflow (type %s)\n", \ a, b, of ? "" : " not", #t); \ - KUNIT_EXPECT_EQ_MSG(test, _r, r, \ + KUNIT_EXPECT_TRUE_MSG(test, _r == r, \ "expected "fmt" "sym" "fmt" == "fmt", got "fmt" (type %s)\n", \ a, b, r, _r, #t); \ /* Check for internal macro side-effects. */ \ _of = check_ ## op ## _overflow(_a_orig++, _b_orig++, &_r); \ - KUNIT_EXPECT_EQ_MSG(test, _a_orig, _a_bump, "Unexpected " #op " macro side-effect!\n"); \ - KUNIT_EXPECT_EQ_MSG(test, _b_orig, _b_bump, "Unexpected " #op " macro side-effect!\n"); \ + KUNIT_EXPECT_TRUE_MSG(test, _a_orig == _a_bump, "Unexpected " #op " macro side-effect!\n"); \ + KUNIT_EXPECT_TRUE_MSG(test, _b_orig == _b_bump, "Unexpected " #op " macro side-effect!\n"); \ } while (0) #define DEFINE_TEST_FUNC_TYPED(n, t, fmt) \ @@ -333,6 +340,55 @@ DEFINE_TEST_ARRAY_TYPED(int, int, u8) = { }; DEFINE_TEST_FUNC_TYPED(int_int__u8, u8, "%d"); +#define DEFINE_TEST_PTR_FUNC_TYPED(n, t, fmt) \ +static void do_ptr_test_ ## n(struct kunit *test, const struct test_ ## n *p) \ +{ \ + /* we're only doing single-direction sums, no product or division */ \ + check_one_op(t, fmt, add, "+", p->a, p->b, p->sum, p->s_of);\ +} \ + \ +static void n ## _overflow_test(struct kunit *test) { \ + unsigned i; \ + \ + for (i = 0; i < ARRAY_SIZE(n ## _tests); ++i) \ + do_ptr_test_ ## n(test, &n ## _tests[i]); \ + kunit_info(test, "%zu %s arithmetic tests finished\n", \ + ARRAY_SIZE(n ## _tests), #n); \ +} + +DEFINE_TEST_ARRAY_NAMED_TYPED(void, int, void, void *, int, void *) = { + {NULL, 0, NULL, NULL, NULL, false, false, false}, + {(void *)0x30, 0x10, (void *)0x40, NULL, NULL, false, false, false}, + {(void *)ULONG_MAX, 0, (void *)ULONG_MAX, NULL, NULL, false, false, false}, + {(void *)ULONG_MAX, 1, NULL, NULL, NULL, true, false, false}, + {(void *)ULONG_MAX, INT_MAX, (void *)(INT_MAX - 1), NULL, NULL, true, false, false}, +}; +DEFINE_TEST_PTR_FUNC_TYPED(void_int__void, void *, "%lx"); + +struct _sized { + int a; + char b; +}; + +DEFINE_TEST_ARRAY_NAMED_TYPED(sized, int, sized, struct _sized *, int, struct _sized *) = { + {NULL, 0, NULL, NULL, NULL, false, false, false}, + {NULL, 1, (struct _sized *)(sizeof(struct _sized)), NULL, NULL, false, false, false}, + {NULL, 0x10, (struct _sized *)(sizeof(struct _sized) * 0x10), NULL, NULL, false, false, false}, + {(void *)(ULONG_MAX - sizeof(struct _sized)), 1, (struct _sized *)ULONG_MAX, NULL, NULL, false, false, false}, + {(void *)(ULONG_MAX - sizeof(struct _sized) + 1), 1, NULL, NULL, NULL, true, false, false}, + {(void *)(ULONG_MAX - sizeof(struct _sized) + 1), 2, (struct _sized *)(sizeof(struct _sized)), NULL, NULL, true, false, false}, + {(void *)(ULONG_MAX - sizeof(struct _sized) + 1), 3, (struct _sized *)(sizeof(struct _sized) * 2), NULL, NULL, true, false, false}, +}; +DEFINE_TEST_PTR_FUNC_TYPED(sized_int__sized, struct _sized *, "%lx"); + +DEFINE_TEST_ARRAY_NAMED_TYPED(sized, size_t, sized, struct _sized *, size_t, struct _sized *) = { + {NULL, 0, NULL, NULL, NULL, false, false, false}, + {NULL, 1, (struct _sized *)(sizeof(struct _sized)), NULL, NULL, false, false, false}, + {NULL, 0x10, (struct _sized *)(sizeof(struct _sized) * 0x10), NULL, NULL, false, false, false}, + {NULL, SIZE_MAX - 10, (struct _sized *)18446744073709551528UL, NULL, NULL, true, false, false}, +}; +DEFINE_TEST_PTR_FUNC_TYPED(sized_size_t__sized, struct _sized *, "%zu"); + /* Args are: value, shift, type, expected result, overflow expected */ #define TEST_ONE_SHIFT(a, s, t, expect, of) do { \ typeof(a) __a = (a); \ @@ -1122,6 +1178,9 @@ static struct kunit_case overflow_test_cases[] = { KUNIT_CASE(s32_s32__s32_overflow_test), KUNIT_CASE(u64_u64__u64_overflow_test), KUNIT_CASE(s64_s64__s64_overflow_test), + KUNIT_CASE(void_int__void_overflow_test), + KUNIT_CASE(sized_int__sized_overflow_test), + KUNIT_CASE(sized_size_t__sized_overflow_test), KUNIT_CASE(u32_u32__int_overflow_test), KUNIT_CASE(u32_u32__u8_overflow_test), KUNIT_CASE(u8_u8__int_overflow_test), -- 2.34.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 3/5] overflow: Introduce add_would_overflow() 2024-01-29 18:34 [PATCH 0/5] overflow: Introduce wrapping helpers Kees Cook 2024-01-29 18:34 ` [PATCH 1/5] overflow: Adjust check_*_overflow() kern-doc to reflect results Kees Cook 2024-01-29 18:34 ` [PATCH 2/5] overflow: Expand check_add_overflow() for pointer addition Kees Cook @ 2024-01-29 18:34 ` Kees Cook 2024-01-29 18:34 ` [PATCH 4/5] overflow: Introduce add_wrap(), sub_wrap(), and mul_wrap() Kees Cook 2024-01-29 18:34 ` [PATCH 5/5] overflow: Introduce inc_wrap() and dec_wrap() Kees Cook 4 siblings, 0 replies; 10+ messages in thread From: Kees Cook @ 2024-01-29 18:34 UTC (permalink / raw) To: Rasmus Villemoes Cc: Kees Cook, Gustavo A. R. Silva, linux-hardening, Nathan Chancellor, Bill Wendling, Justin Stitt, Mark Rutland, Miguel Ojeda, Marco Elver, linux-kernel, llvm For instances where only the overflow needs to be checked (and the sum isn't used), provide the new helper add_would_overflow(), which is a wrapper for check_add_overflow(). Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk> Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org> Cc: linux-hardening@vger.kernel.org Signed-off-by: Kees Cook <keescook@chromium.org> --- include/linux/overflow.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/linux/overflow.h b/include/linux/overflow.h index 210e5602e89b..3c46c648d2e8 100644 --- a/include/linux/overflow.h +++ b/include/linux/overflow.h @@ -104,6 +104,22 @@ static inline bool __must_check __must_check_overflow(bool overflow) __builtin_add_overflow(__filter_integral(a), b, \ __filter_ptrint(d)))) +/** + * add_would_overflow() - Check if an addition would overflow + * @var: variable to add to that is checked for overflow + * @offset: value to add + * + * Returns true if the sum would overflow. + * + * To keep a copy of the sum when the addition doesn't overflow, use + * check_add_overflow() instead. + */ +#define add_would_overflow(var, offset) \ + __must_check_overflow(({ \ + typeof(var) __result; \ + check_add_overflow(var, offset, &__result); \ + })) + /** * check_sub_overflow() - Calculate subtraction with overflow checking * @a: minuend; value to subtract from -- 2.34.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* [PATCH 4/5] overflow: Introduce add_wrap(), sub_wrap(), and mul_wrap() 2024-01-29 18:34 [PATCH 0/5] overflow: Introduce wrapping helpers Kees Cook ` (2 preceding siblings ...) 2024-01-29 18:34 ` [PATCH 3/5] overflow: Introduce add_would_overflow() Kees Cook @ 2024-01-29 18:34 ` Kees Cook 2024-01-29 20:08 ` Rasmus Villemoes 2024-01-29 18:34 ` [PATCH 5/5] overflow: Introduce inc_wrap() and dec_wrap() Kees Cook 4 siblings, 1 reply; 10+ messages in thread From: Kees Cook @ 2024-01-29 18:34 UTC (permalink / raw) To: Rasmus Villemoes Cc: Kees Cook, Mark Rutland, linux-hardening, Gustavo A. R. Silva, Nathan Chancellor, Bill Wendling, Justin Stitt, Miguel Ojeda, Marco Elver, linux-kernel, llvm Provide helpers that will perform wrapping addition, subtraction, or multiplication without tripping the arithmetic wrap-around sanitizers. The first argument is the type under which the wrap-around should happen with. In other words, these two calls will get very different results: add_wrap(int, 50, 50) == 2500 add_wrap(u8, 50, 50) == 196 Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk> Cc: Mark Rutland <mark.rutland@arm.com> Cc: linux-hardening@vger.kernel.org Signed-off-by: Kees Cook <keescook@chromium.org> --- include/linux/overflow.h | 54 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/include/linux/overflow.h b/include/linux/overflow.h index 3c46c648d2e8..4f945e9e7881 100644 --- a/include/linux/overflow.h +++ b/include/linux/overflow.h @@ -120,6 +120,24 @@ static inline bool __must_check __must_check_overflow(bool overflow) check_add_overflow(var, offset, &__result); \ })) +/** + * add_wrap() - Intentionally perform a wrapping addition + * @type: type to check overflow against + * @a: first addend + * @b: second addend + * + * Return the potentially wrapped-around addition without + * tripping any overflow sanitizers that may be enabled. + */ +#define add_wrap(type, a, b) \ + ({ \ + type __sum; \ + if (check_add_overflow(a, b, &__sum)) { \ + /* do nothing */ \ + } \ + __sum; \ + }) + /** * check_sub_overflow() - Calculate subtraction with overflow checking * @a: minuend; value to subtract from @@ -133,6 +151,24 @@ static inline bool __must_check __must_check_overflow(bool overflow) #define check_sub_overflow(a, b, d) \ __must_check_overflow(__builtin_sub_overflow(a, b, d)) +/** + * sub_wrap() - Intentionally perform a wrapping subtraction + * @type: type to check underflow against + * @a: minuend; value to subtract from + * @b: subtrahend; value to subtract from @a + * + * Return the potentially wrapped-around subtraction without + * tripping any overflow sanitizers that may be enabled. + */ +#define sub_wrap(type, a, b) \ + ({ \ + type __val; \ + if (check_sub_overflow(a, b, &__val)) { \ + /* do nothing */ \ + } \ + __val; \ + }) + /** * check_mul_overflow() - Calculate multiplication with overflow checking * @a: first factor @@ -146,6 +182,24 @@ static inline bool __must_check __must_check_overflow(bool overflow) #define check_mul_overflow(a, b, d) \ __must_check_overflow(__builtin_mul_overflow(a, b, d)) +/** + * mul_wrap() - Intentionally perform a wrapping multiplication + * @type: type to check underflow against + * @a: first factor + * @b: second factor + * + * Return the potentially wrapped-around multiplication without + * tripping any overflow sanitizers that may be enabled. + */ +#define mul_wrap(type, a, b) \ + ({ \ + type __val; \ + if (check_mul_overflow(a, b, &__val)) { \ + /* do nothing */ \ + } \ + __val; \ + }) + /** * check_shl_overflow() - Calculate a left-shifted value and check overflow * @a: Value to be shifted -- 2.34.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 4/5] overflow: Introduce add_wrap(), sub_wrap(), and mul_wrap() 2024-01-29 18:34 ` [PATCH 4/5] overflow: Introduce add_wrap(), sub_wrap(), and mul_wrap() Kees Cook @ 2024-01-29 20:08 ` Rasmus Villemoes 2024-01-29 20:26 ` Kees Cook 0 siblings, 1 reply; 10+ messages in thread From: Rasmus Villemoes @ 2024-01-29 20:08 UTC (permalink / raw) To: Kees Cook Cc: Mark Rutland, linux-hardening, Gustavo A. R. Silva, Nathan Chancellor, Bill Wendling, Justin Stitt, Miguel Ojeda, Marco Elver, linux-kernel, llvm On 29/01/2024 19.34, Kees Cook wrote: > Provide helpers that will perform wrapping addition, subtraction, or > multiplication without tripping the arithmetic wrap-around sanitizers. The > first argument is the type under which the wrap-around should happen > with. In other words, these two calls will get very different results: > > add_wrap(int, 50, 50) == 2500 > add_wrap(u8, 50, 50) == 196 s/add/mul/g I suppose. > Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk> > Cc: Mark Rutland <mark.rutland@arm.com> > Cc: linux-hardening@vger.kernel.org > Signed-off-by: Kees Cook <keescook@chromium.org> > --- > include/linux/overflow.h | 54 ++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 54 insertions(+) > > diff --git a/include/linux/overflow.h b/include/linux/overflow.h > index 3c46c648d2e8..4f945e9e7881 100644 > --- a/include/linux/overflow.h > +++ b/include/linux/overflow.h > @@ -120,6 +120,24 @@ static inline bool __must_check __must_check_overflow(bool overflow) > check_add_overflow(var, offset, &__result); \ > })) > > +/** > + * add_wrap() - Intentionally perform a wrapping addition > + * @type: type to check overflow against Well, nothing is "checked", so why not just say "type of result"? > > +/** > + * sub_wrap() - Intentionally perform a wrapping subtraction > + * @type: type to check underflow against The terminology becomes muddy, is (INT_MAX) - (-1) an underflow or overflow? Anyway, see above. > > +/** > + * mul_wrap() - Intentionally perform a wrapping multiplication > + * @type: type to check underflow against And here there's definitely a copy-pasto. The code itself looks fine. Rasmus ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 4/5] overflow: Introduce add_wrap(), sub_wrap(), and mul_wrap() 2024-01-29 20:08 ` Rasmus Villemoes @ 2024-01-29 20:26 ` Kees Cook 0 siblings, 0 replies; 10+ messages in thread From: Kees Cook @ 2024-01-29 20:26 UTC (permalink / raw) To: Rasmus Villemoes Cc: Mark Rutland, linux-hardening, Gustavo A. R. Silva, Nathan Chancellor, Bill Wendling, Justin Stitt, Miguel Ojeda, Marco Elver, linux-kernel, llvm On Mon, Jan 29, 2024 at 09:08:43PM +0100, Rasmus Villemoes wrote: > On 29/01/2024 19.34, Kees Cook wrote: > > Provide helpers that will perform wrapping addition, subtraction, or > > multiplication without tripping the arithmetic wrap-around sanitizers. The > > first argument is the type under which the wrap-around should happen > > with. In other words, these two calls will get very different results: > > > > add_wrap(int, 50, 50) == 2500 > > add_wrap(u8, 50, 50) == 196 > > s/add/mul/g I suppose. Oops, yes. > > Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk> > > Cc: Mark Rutland <mark.rutland@arm.com> > > Cc: linux-hardening@vger.kernel.org > > Signed-off-by: Kees Cook <keescook@chromium.org> > > --- > > include/linux/overflow.h | 54 ++++++++++++++++++++++++++++++++++++++++ > > 1 file changed, 54 insertions(+) > > > > diff --git a/include/linux/overflow.h b/include/linux/overflow.h > > index 3c46c648d2e8..4f945e9e7881 100644 > > --- a/include/linux/overflow.h > > +++ b/include/linux/overflow.h > > @@ -120,6 +120,24 @@ static inline bool __must_check __must_check_overflow(bool overflow) > > check_add_overflow(var, offset, &__result); \ > > })) > > > > +/** > > + * add_wrap() - Intentionally perform a wrapping addition > > + * @type: type to check overflow against > > Well, nothing is "checked", so why not just say "type of result"? Yeah, that's better. I was trying to describe that @type will affect the value of the result. > > +/** > > + * sub_wrap() - Intentionally perform a wrapping subtraction > > + * @type: type to check underflow against > > The terminology becomes muddy, is (INT_MAX) - (-1) an underflow or > overflow? Anyway, see above. Right, I should explicitly say "wrap-around". > > > > > +/** > > + * mul_wrap() - Intentionally perform a wrapping multiplication > > + * @type: type to check underflow against > > And here there's definitely a copy-pasto. Ek, yes. > The code itself looks fine. Thanks! -- Kees Cook ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH 5/5] overflow: Introduce inc_wrap() and dec_wrap() 2024-01-29 18:34 [PATCH 0/5] overflow: Introduce wrapping helpers Kees Cook ` (3 preceding siblings ...) 2024-01-29 18:34 ` [PATCH 4/5] overflow: Introduce add_wrap(), sub_wrap(), and mul_wrap() Kees Cook @ 2024-01-29 18:34 ` Kees Cook 2024-01-29 20:16 ` Rasmus Villemoes 4 siblings, 1 reply; 10+ messages in thread From: Kees Cook @ 2024-01-29 18:34 UTC (permalink / raw) To: Rasmus Villemoes Cc: Kees Cook, Mark Rutland, Gustavo A. R. Silva, linux-hardening, Nathan Chancellor, Bill Wendling, Justin Stitt, Miguel Ojeda, Marco Elver, linux-kernel, llvm This allows replacements of the idioms "var += offset" and "var -= offset" with the inc_wrap() and dec_wrap() helpers respectively. They will avoid wrap-around sanitizer instrumentation. Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk> Cc: Mark Rutland <mark.rutland@arm.com> Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org> Cc: linux-hardening@vger.kernel.org Signed-off-by: Kees Cook <keescook@chromium.org> --- include/linux/overflow.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/include/linux/overflow.h b/include/linux/overflow.h index 4f945e9e7881..080b18b84498 100644 --- a/include/linux/overflow.h +++ b/include/linux/overflow.h @@ -138,6 +138,22 @@ static inline bool __must_check __must_check_overflow(bool overflow) __sum; \ }) +/** + * add_wrap() - Intentionally perform a wrapping increment + * @a: variable to be incremented + * @b: amount to add + * + * Increments @a by @b with wrap-around. Returns the resulting + * value of @a. Will not trip any wrap-around sanitizers. + */ +#define inc_wrap(var, offset) \ + ({ \ + if (check_add_overflow(var, offset, &var)) { \ + /* do nothing */ \ + } \ + var; \ + }) + /** * check_sub_overflow() - Calculate subtraction with overflow checking * @a: minuend; value to subtract from @@ -169,6 +185,22 @@ static inline bool __must_check __must_check_overflow(bool overflow) __val; \ }) +/** + * dec_wrap() - Intentionally perform a wrapping decrement + * @a: variable to be decremented + * @b: amount to subtract + * + * Decrements @a by @b with wrap-around. Returns the resulting + * value of @a. Will not trip any wrap-around sanitizers. + */ +#define dec_wrap(var, offset) \ + ({ \ + if (check_sub_overflow(var, offset, &var)) { \ + /* do nothing */ \ + } \ + var; \ + }) + /** * check_mul_overflow() - Calculate multiplication with overflow checking * @a: first factor -- 2.34.1 ^ permalink raw reply related [flat|nested] 10+ messages in thread
* Re: [PATCH 5/5] overflow: Introduce inc_wrap() and dec_wrap() 2024-01-29 18:34 ` [PATCH 5/5] overflow: Introduce inc_wrap() and dec_wrap() Kees Cook @ 2024-01-29 20:16 ` Rasmus Villemoes 2024-01-29 21:56 ` Kees Cook 0 siblings, 1 reply; 10+ messages in thread From: Rasmus Villemoes @ 2024-01-29 20:16 UTC (permalink / raw) To: Kees Cook Cc: Mark Rutland, Gustavo A. R. Silva, linux-hardening, Nathan Chancellor, Bill Wendling, Justin Stitt, Miguel Ojeda, Marco Elver, linux-kernel, llvm On 29/01/2024 19.34, Kees Cook wrote: > This allows replacements of the idioms "var += offset" and "var -= offset" > with the inc_wrap() and dec_wrap() helpers respectively. They will avoid > wrap-around sanitizer instrumentation. > > Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk> > Cc: Mark Rutland <mark.rutland@arm.com> > Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org> > Cc: linux-hardening@vger.kernel.org > Signed-off-by: Kees Cook <keescook@chromium.org> > --- > include/linux/overflow.h | 32 ++++++++++++++++++++++++++++++++ > 1 file changed, 32 insertions(+) > > diff --git a/include/linux/overflow.h b/include/linux/overflow.h > index 4f945e9e7881..080b18b84498 100644 > --- a/include/linux/overflow.h > +++ b/include/linux/overflow.h > @@ -138,6 +138,22 @@ static inline bool __must_check __must_check_overflow(bool overflow) > __sum; \ > }) > > +/** > + * add_wrap() - Intentionally perform a wrapping increment inc_wrap > + * @a: variable to be incremented > + * @b: amount to add > + * > + * Increments @a by @b with wrap-around. Returns the resulting > + * value of @a. Will not trip any wrap-around sanitizers. > + */ > +#define inc_wrap(var, offset) \ > + ({ \ > + if (check_add_overflow(var, offset, &var)) { \ > + /* do nothing */ \ > + } \ > + var; \ Hm. I wonder if multiple evaluations of var could be a problem. Obviously never if var is actually some automatic variable, nor if it is some simple foo->bar expression. But nothing really prevents var from being, say, foo[gimme_an_index()] or something similarly convoluted. Does the compiler generate ok code if one does typeof(var) *__pvar = &(var); if (check_add_overflow(*__pvar, offset, __pvar)) {} *__pvar; [in fact, does it even generate code, i.e. does it compile?] I dunno, maybe it's overkill to worry about. Rasmus ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH 5/5] overflow: Introduce inc_wrap() and dec_wrap() 2024-01-29 20:16 ` Rasmus Villemoes @ 2024-01-29 21:56 ` Kees Cook 0 siblings, 0 replies; 10+ messages in thread From: Kees Cook @ 2024-01-29 21:56 UTC (permalink / raw) To: Rasmus Villemoes Cc: Mark Rutland, Gustavo A. R. Silva, linux-hardening, Nathan Chancellor, Bill Wendling, Justin Stitt, Miguel Ojeda, Marco Elver, linux-kernel, llvm On Mon, Jan 29, 2024 at 09:16:36PM +0100, Rasmus Villemoes wrote: > On 29/01/2024 19.34, Kees Cook wrote: > > This allows replacements of the idioms "var += offset" and "var -= offset" > > with the inc_wrap() and dec_wrap() helpers respectively. They will avoid > > wrap-around sanitizer instrumentation. > > > > Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk> > > Cc: Mark Rutland <mark.rutland@arm.com> > > Cc: "Gustavo A. R. Silva" <gustavoars@kernel.org> > > Cc: linux-hardening@vger.kernel.org > > Signed-off-by: Kees Cook <keescook@chromium.org> > > --- > > include/linux/overflow.h | 32 ++++++++++++++++++++++++++++++++ > > 1 file changed, 32 insertions(+) > > > > diff --git a/include/linux/overflow.h b/include/linux/overflow.h > > index 4f945e9e7881..080b18b84498 100644 > > --- a/include/linux/overflow.h > > +++ b/include/linux/overflow.h > > @@ -138,6 +138,22 @@ static inline bool __must_check __must_check_overflow(bool overflow) > > __sum; \ > > }) > > > > +/** > > + * add_wrap() - Intentionally perform a wrapping increment > > inc_wrap Thanks, fixed. > > > + * @a: variable to be incremented > > + * @b: amount to add > > + * > > + * Increments @a by @b with wrap-around. Returns the resulting > > + * value of @a. Will not trip any wrap-around sanitizers. > > + */ > > +#define inc_wrap(var, offset) \ > > + ({ \ > > + if (check_add_overflow(var, offset, &var)) { \ > > + /* do nothing */ \ > > + } \ > > + var; \ > > Hm. I wonder if multiple evaluations of var could be a problem. I am normally defensive about this, but due to @a normally being an lvalue, I lacked the imagination to think of other side-effects, but you've set me straight below. > Obviously never if var is actually some automatic variable, nor if it is > some simple foo->bar expression. But nothing really prevents var from > being, say, foo[gimme_an_index()] or something similarly convoluted. > > Does the compiler generate ok code if one does > > typeof(var) *__pvar = &(var); > if (check_add_overflow(*__pvar, offset, __pvar)) {} > *__pvar; > > [in fact, does it even generate code, i.e. does it compile?] > > I dunno, maybe it's overkill to worry about. Yeah, an index-fetch is a great example that would get lost here. I've updated these to be defined in terms of add/sub_wrap() and to use your pointer typing method to avoid side-effects. -- Kees Cook ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2024-01-29 21:56 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-01-29 18:34 [PATCH 0/5] overflow: Introduce wrapping helpers Kees Cook 2024-01-29 18:34 ` [PATCH 1/5] overflow: Adjust check_*_overflow() kern-doc to reflect results Kees Cook 2024-01-29 18:34 ` [PATCH 2/5] overflow: Expand check_add_overflow() for pointer addition Kees Cook 2024-01-29 18:34 ` [PATCH 3/5] overflow: Introduce add_would_overflow() Kees Cook 2024-01-29 18:34 ` [PATCH 4/5] overflow: Introduce add_wrap(), sub_wrap(), and mul_wrap() Kees Cook 2024-01-29 20:08 ` Rasmus Villemoes 2024-01-29 20:26 ` Kees Cook 2024-01-29 18:34 ` [PATCH 5/5] overflow: Introduce inc_wrap() and dec_wrap() Kees Cook 2024-01-29 20:16 ` Rasmus Villemoes 2024-01-29 21:56 ` Kees Cook
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox