* [PATCH v6 0/3] overflow: Introduce wrapping helpers
@ 2024-02-13 22:10 Kees Cook
2024-02-13 22:10 ` [PATCH v6 1/3] overflow: Adjust check_*_overflow() kern-doc to reflect results Kees Cook
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Kees Cook @ 2024-02-13 22:10 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Kees Cook, Rasmus Villemoes, Gustavo A. R. Silva, Marco Elver,
Eric Biggers, Mark Rutland, Andrew Morton, linux-kernel,
linux-hardening
v6: rename wrapping_inc/dec to wraping_assign_add/sub (andy)
v5: https://lore.kernel.org/all/20240207152317.do.560-kees@kernel.org/
v4: https://lore.kernel.org/all/20240206102354.make.081-kees@kernel.org/
v3: https://lore.kernel.org/all/20240205090854.make.507-kees@kernel.org/
v2: https://lore.kernel.org/all/20240130220218.it.154-kees@kernel.org/
v1: https://lore.kernel.org/all/20240129182845.work.694-kees@kernel.org/
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 add/sub/mul
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 (3):
overflow: Adjust check_*_overflow() kern-doc to reflect results
overflow: Introduce wrapping_add(), wrapping_sub(), and wrapping_mul()
overflow: Introduce wrapping_assign_add() and wrapping_assign_sub()
include/linux/overflow.h | 98 +++++++++++++++++++++++++++++++++++-----
lib/overflow_kunit.c | 67 +++++++++++++++++++++++++--
2 files changed, 149 insertions(+), 16 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v6 1/3] overflow: Adjust check_*_overflow() kern-doc to reflect results 2024-02-13 22:10 [PATCH v6 0/3] overflow: Introduce wrapping helpers Kees Cook @ 2024-02-13 22:10 ` Kees Cook 2024-02-14 11:57 ` Mark Rutland 2024-02-13 22:10 ` [PATCH v6 2/3] overflow: Introduce wrapping_add(), wrapping_sub(), and wrapping_mul() Kees Cook 2024-02-13 22:10 ` [PATCH v6 3/3] overflow: Introduce wrapping_assign_add() and wrapping_assign_sub() Kees Cook 2 siblings, 1 reply; 7+ messages in thread From: Kees Cook @ 2024-02-13 22:10 UTC (permalink / raw) To: Andy Shevchenko Cc: Kees Cook, Gustavo A . R . Silva, linux-hardening, Rasmus Villemoes, Marco Elver, Eric Biggers, Mark Rutland, Andrew Morton, linux-kernel 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. Reviewed-by: 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] 7+ messages in thread
* Re: [PATCH v6 1/3] overflow: Adjust check_*_overflow() kern-doc to reflect results 2024-02-13 22:10 ` [PATCH v6 1/3] overflow: Adjust check_*_overflow() kern-doc to reflect results Kees Cook @ 2024-02-14 11:57 ` Mark Rutland 2024-02-14 19:38 ` Kees Cook 0 siblings, 1 reply; 7+ messages in thread From: Mark Rutland @ 2024-02-14 11:57 UTC (permalink / raw) To: Kees Cook Cc: Andy Shevchenko, Gustavo A . R . Silva, linux-hardening, Rasmus Villemoes, Marco Elver, Eric Biggers, Andrew Morton, linux-kernel On Tue, Feb 13, 2024 at 02:10:57PM -0800, Kees Cook wrote: > 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. > > Reviewed-by: 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. Sorry for the last minute bikeshedding, but could we clarify 'success' here? e.g. I think it'd be clearer to say: Returns true on wrap-around, false otherwise. Note that also uses true/false since these all return bool (as do the underlying __builtin_*_overflow() functions). > * > - * *@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. How about: @d holds the results of the attempted addition, regardless of whether wrap-around occurred. ... and likewise for the others below? Mark. > */ > #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 [flat|nested] 7+ messages in thread
* Re: [PATCH v6 1/3] overflow: Adjust check_*_overflow() kern-doc to reflect results 2024-02-14 11:57 ` Mark Rutland @ 2024-02-14 19:38 ` Kees Cook 0 siblings, 0 replies; 7+ messages in thread From: Kees Cook @ 2024-02-14 19:38 UTC (permalink / raw) To: Mark Rutland Cc: Andy Shevchenko, Gustavo A . R . Silva, linux-hardening, Rasmus Villemoes, Marco Elver, Eric Biggers, Andrew Morton, linux-kernel On Wed, Feb 14, 2024 at 11:57:28AM +0000, Mark Rutland wrote: > On Tue, Feb 13, 2024 at 02:10:57PM -0800, Kees Cook wrote: > > 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. > > > > Reviewed-by: 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. > > Sorry for the last minute bikeshedding, but could we clarify 'success' here? > e.g. I think it'd be clearer to say: > > Returns true on wrap-around, false otherwise. > > Note that also uses true/false since these all return bool (as do the > underlying __builtin_*_overflow() functions). Yeah, that's a good point. I'll update this. > > * > > - * *@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. > > How about: > > @d holds the results of the attempted addition, regardless of whether > wrap-around occurred. > > ... and likewise for the others below? Yeah, that's more clear. Thanks! -Kees -- Kees Cook ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v6 2/3] overflow: Introduce wrapping_add(), wrapping_sub(), and wrapping_mul() 2024-02-13 22:10 [PATCH v6 0/3] overflow: Introduce wrapping helpers Kees Cook 2024-02-13 22:10 ` [PATCH v6 1/3] overflow: Adjust check_*_overflow() kern-doc to reflect results Kees Cook @ 2024-02-13 22:10 ` Kees Cook 2024-02-14 11:58 ` Mark Rutland 2024-02-13 22:10 ` [PATCH v6 3/3] overflow: Introduce wrapping_assign_add() and wrapping_assign_sub() Kees Cook 2 siblings, 1 reply; 7+ messages in thread From: Kees Cook @ 2024-02-13 22:10 UTC (permalink / raw) To: Andy Shevchenko Cc: Kees Cook, Rasmus Villemoes, Marco Elver, Eric Biggers, Mark Rutland, linux-hardening, Gustavo A . R . Silva, Andrew Morton, linux-kernel 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: wrapping_mul(int, 50, 50) == 2500 wrapping_mul(u8, 50, 50) == 196 Add to the selftests to validate behavior and lack of side-effects. Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk> Cc: Marco Elver <elver@google.com> Cc: Eric Biggers <ebiggers@kernel.org> Cc: Mark Rutland <mark.rutland@arm.com> Cc: linux-hardening@vger.kernel.org Reviewed-by: Gustavo A. R. Silva <gustavoars@kernel.org> Reviewed-by: Marco Elver <elver@google.com> Signed-off-by: Kees Cook <keescook@chromium.org> --- include/linux/overflow.h | 48 ++++++++++++++++++++++++++++++++++++++++ lib/overflow_kunit.c | 24 ++++++++++++++++---- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/include/linux/overflow.h b/include/linux/overflow.h index 4e741ebb8005..849a49fb496e 100644 --- a/include/linux/overflow.h +++ b/include/linux/overflow.h @@ -64,6 +64,22 @@ static inline bool __must_check __must_check_overflow(bool overflow) #define check_add_overflow(a, b, d) \ __must_check_overflow(__builtin_add_overflow(a, b, d)) +/** + * wrapping_add() - Intentionally perform a wrapping addition + * @type: type for result of calculation + * @a: first addend + * @b: second addend + * + * Return the potentially wrapped-around addition without + * tripping any wrap-around sanitizers that may be enabled. + */ +#define wrapping_add(type, a, b) \ + ({ \ + type __val; \ + __builtin_add_overflow(a, b, &__val); \ + __val; \ + }) + /** * check_sub_overflow() - Calculate subtraction with overflow checking * @a: minuend; value to subtract from @@ -77,6 +93,22 @@ 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)) +/** + * wrapping_sub() - Intentionally perform a wrapping subtraction + * @type: type for result of calculation + * @a: minuend; value to subtract from + * @b: subtrahend; value to subtract from @a + * + * Return the potentially wrapped-around subtraction without + * tripping any wrap-around sanitizers that may be enabled. + */ +#define wrapping_sub(type, a, b) \ + ({ \ + type __val; \ + __builtin_sub_overflow(a, b, &__val); \ + __val; \ + }) + /** * check_mul_overflow() - Calculate multiplication with overflow checking * @a: first factor @@ -90,6 +122,22 @@ 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)) +/** + * wrapping_mul() - Intentionally perform a wrapping multiplication + * @type: type for result of calculation + * @a: first factor + * @b: second factor + * + * Return the potentially wrapped-around multiplication without + * tripping any wrap-around sanitizers that may be enabled. + */ +#define wrapping_mul(type, a, b) \ + ({ \ + type __val; \ + __builtin_mul_overflow(a, b, &__val); \ + __val; \ + }) + /** * check_shl_overflow() - Calculate a left-shifted value and check overflow * @a: Value to be shifted diff --git a/lib/overflow_kunit.c b/lib/overflow_kunit.c index c527f6b75789..d3fdb906d3fe 100644 --- a/lib/overflow_kunit.c +++ b/lib/overflow_kunit.c @@ -258,20 +258,36 @@ DEFINE_TEST_ARRAY(s64) = { \ _of = check_ ## op ## _overflow(a, b, &_r); \ KUNIT_EXPECT_EQ_MSG(test, _of, of, \ - "expected "fmt" "sym" "fmt" to%s overflow (type %s)\n", \ + "expected check "fmt" "sym" "fmt" to%s overflow (type %s)\n", \ a, b, of ? "" : " not", #t); \ KUNIT_EXPECT_EQ_MSG(test, _r, r, \ - "expected "fmt" "sym" "fmt" == "fmt", got "fmt" (type %s)\n", \ + "expected check "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_EQ_MSG(test, _a_orig, _a_bump, \ + "Unexpected check " #op " macro side-effect!\n"); \ + KUNIT_EXPECT_EQ_MSG(test, _b_orig, _b_bump, \ + "Unexpected check " #op " macro side-effect!\n"); \ + \ + _r = wrapping_ ## op(t, a, b); \ + KUNIT_EXPECT_TRUE_MSG(test, _r == r, \ + "expected wrap "fmt" "sym" "fmt" == "fmt", got "fmt" (type %s)\n", \ + a, b, r, _r, #t); \ + /* Check for internal macro side-effects. */ \ + _a_orig = a; \ + _b_orig = b; \ + _r = wrapping_ ## op(t, _a_orig++, _b_orig++); \ + KUNIT_EXPECT_EQ_MSG(test, _a_orig, _a_bump, \ + "Unexpected wrap " #op " macro side-effect!\n"); \ + KUNIT_EXPECT_EQ_MSG(test, _b_orig, _b_bump, \ + "Unexpected wrap " #op " macro side-effect!\n"); \ } while (0) #define DEFINE_TEST_FUNC_TYPED(n, t, fmt) \ static void do_test_ ## n(struct kunit *test, const struct test_ ## n *p) \ { \ + /* check_{add,sub,mul}_overflow() and wrapping_{add,sub,mul} */ \ check_one_op(t, fmt, add, "+", p->a, p->b, p->sum, p->s_of); \ check_one_op(t, fmt, add, "+", p->b, p->a, p->sum, p->s_of); \ check_one_op(t, fmt, sub, "-", p->a, p->b, p->diff, p->d_of); \ -- 2.34.1 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v6 2/3] overflow: Introduce wrapping_add(), wrapping_sub(), and wrapping_mul() 2024-02-13 22:10 ` [PATCH v6 2/3] overflow: Introduce wrapping_add(), wrapping_sub(), and wrapping_mul() Kees Cook @ 2024-02-14 11:58 ` Mark Rutland 0 siblings, 0 replies; 7+ messages in thread From: Mark Rutland @ 2024-02-14 11:58 UTC (permalink / raw) To: Kees Cook Cc: Andy Shevchenko, Rasmus Villemoes, Marco Elver, Eric Biggers, linux-hardening, Gustavo A . R . Silva, Andrew Morton, linux-kernel On Tue, Feb 13, 2024 at 02:10:58PM -0800, 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: > > wrapping_mul(int, 50, 50) == 2500 > wrapping_mul(u8, 50, 50) == 196 > > Add to the selftests to validate behavior and lack of side-effects. > > Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk> > Cc: Marco Elver <elver@google.com> > Cc: Eric Biggers <ebiggers@kernel.org> > Cc: Mark Rutland <mark.rutland@arm.com> > Cc: linux-hardening@vger.kernel.org > Reviewed-by: Gustavo A. R. Silva <gustavoars@kernel.org> > Reviewed-by: Marco Elver <elver@google.com> > Signed-off-by: Kees Cook <keescook@chromium.org> Acked-by: Mark Rutland <mark.rutland@arm.com> Mark. > --- > include/linux/overflow.h | 48 ++++++++++++++++++++++++++++++++++++++++ > lib/overflow_kunit.c | 24 ++++++++++++++++---- > 2 files changed, 68 insertions(+), 4 deletions(-) > > diff --git a/include/linux/overflow.h b/include/linux/overflow.h > index 4e741ebb8005..849a49fb496e 100644 > --- a/include/linux/overflow.h > +++ b/include/linux/overflow.h > @@ -64,6 +64,22 @@ static inline bool __must_check __must_check_overflow(bool overflow) > #define check_add_overflow(a, b, d) \ > __must_check_overflow(__builtin_add_overflow(a, b, d)) > > +/** > + * wrapping_add() - Intentionally perform a wrapping addition > + * @type: type for result of calculation > + * @a: first addend > + * @b: second addend > + * > + * Return the potentially wrapped-around addition without > + * tripping any wrap-around sanitizers that may be enabled. > + */ > +#define wrapping_add(type, a, b) \ > + ({ \ > + type __val; \ > + __builtin_add_overflow(a, b, &__val); \ > + __val; \ > + }) > + > /** > * check_sub_overflow() - Calculate subtraction with overflow checking > * @a: minuend; value to subtract from > @@ -77,6 +93,22 @@ 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)) > > +/** > + * wrapping_sub() - Intentionally perform a wrapping subtraction > + * @type: type for result of calculation > + * @a: minuend; value to subtract from > + * @b: subtrahend; value to subtract from @a > + * > + * Return the potentially wrapped-around subtraction without > + * tripping any wrap-around sanitizers that may be enabled. > + */ > +#define wrapping_sub(type, a, b) \ > + ({ \ > + type __val; \ > + __builtin_sub_overflow(a, b, &__val); \ > + __val; \ > + }) > + > /** > * check_mul_overflow() - Calculate multiplication with overflow checking > * @a: first factor > @@ -90,6 +122,22 @@ 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)) > > +/** > + * wrapping_mul() - Intentionally perform a wrapping multiplication > + * @type: type for result of calculation > + * @a: first factor > + * @b: second factor > + * > + * Return the potentially wrapped-around multiplication without > + * tripping any wrap-around sanitizers that may be enabled. > + */ > +#define wrapping_mul(type, a, b) \ > + ({ \ > + type __val; \ > + __builtin_mul_overflow(a, b, &__val); \ > + __val; \ > + }) > + > /** > * check_shl_overflow() - Calculate a left-shifted value and check overflow > * @a: Value to be shifted > diff --git a/lib/overflow_kunit.c b/lib/overflow_kunit.c > index c527f6b75789..d3fdb906d3fe 100644 > --- a/lib/overflow_kunit.c > +++ b/lib/overflow_kunit.c > @@ -258,20 +258,36 @@ DEFINE_TEST_ARRAY(s64) = { > \ > _of = check_ ## op ## _overflow(a, b, &_r); \ > KUNIT_EXPECT_EQ_MSG(test, _of, of, \ > - "expected "fmt" "sym" "fmt" to%s overflow (type %s)\n", \ > + "expected check "fmt" "sym" "fmt" to%s overflow (type %s)\n", \ > a, b, of ? "" : " not", #t); \ > KUNIT_EXPECT_EQ_MSG(test, _r, r, \ > - "expected "fmt" "sym" "fmt" == "fmt", got "fmt" (type %s)\n", \ > + "expected check "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_EQ_MSG(test, _a_orig, _a_bump, \ > + "Unexpected check " #op " macro side-effect!\n"); \ > + KUNIT_EXPECT_EQ_MSG(test, _b_orig, _b_bump, \ > + "Unexpected check " #op " macro side-effect!\n"); \ > + \ > + _r = wrapping_ ## op(t, a, b); \ > + KUNIT_EXPECT_TRUE_MSG(test, _r == r, \ > + "expected wrap "fmt" "sym" "fmt" == "fmt", got "fmt" (type %s)\n", \ > + a, b, r, _r, #t); \ > + /* Check for internal macro side-effects. */ \ > + _a_orig = a; \ > + _b_orig = b; \ > + _r = wrapping_ ## op(t, _a_orig++, _b_orig++); \ > + KUNIT_EXPECT_EQ_MSG(test, _a_orig, _a_bump, \ > + "Unexpected wrap " #op " macro side-effect!\n"); \ > + KUNIT_EXPECT_EQ_MSG(test, _b_orig, _b_bump, \ > + "Unexpected wrap " #op " macro side-effect!\n"); \ > } while (0) > > #define DEFINE_TEST_FUNC_TYPED(n, t, fmt) \ > static void do_test_ ## n(struct kunit *test, const struct test_ ## n *p) \ > { \ > + /* check_{add,sub,mul}_overflow() and wrapping_{add,sub,mul} */ \ > check_one_op(t, fmt, add, "+", p->a, p->b, p->sum, p->s_of); \ > check_one_op(t, fmt, add, "+", p->b, p->a, p->sum, p->s_of); \ > check_one_op(t, fmt, sub, "-", p->a, p->b, p->diff, p->d_of); \ > -- > 2.34.1 > ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v6 3/3] overflow: Introduce wrapping_assign_add() and wrapping_assign_sub() 2024-02-13 22:10 [PATCH v6 0/3] overflow: Introduce wrapping helpers Kees Cook 2024-02-13 22:10 ` [PATCH v6 1/3] overflow: Adjust check_*_overflow() kern-doc to reflect results Kees Cook 2024-02-13 22:10 ` [PATCH v6 2/3] overflow: Introduce wrapping_add(), wrapping_sub(), and wrapping_mul() Kees Cook @ 2024-02-13 22:10 ` Kees Cook 2 siblings, 0 replies; 7+ messages in thread From: Kees Cook @ 2024-02-13 22:10 UTC (permalink / raw) To: Andy Shevchenko Cc: Kees Cook, Rasmus Villemoes, Marco Elver, Eric Biggers, Mark Rutland, Gustavo A. R. Silva, linux-hardening, Andrew Morton, linux-kernel This allows replacements of the idioms "var += offset" and "var -= offset" with the wrapping_assign_add() and wrapping_assign_sub() helpers respectively. They will avoid wrap-around sanitizer instrumentation. Add to the selftests to validate behavior and lack of side-effects. Cc: Rasmus Villemoes <rasmus.villemoes@prevas.dk> Cc: Marco Elver <elver@google.com> Cc: Eric Biggers <ebiggers@kernel.org> 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 ++++++++++++++++++++++++++++++ lib/overflow_kunit.c | 43 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/include/linux/overflow.h b/include/linux/overflow.h index 849a49fb496e..09019b19876f 100644 --- a/include/linux/overflow.h +++ b/include/linux/overflow.h @@ -80,6 +80,22 @@ static inline bool __must_check __must_check_overflow(bool overflow) __val; \ }) +/** + * wrapping_assign_add() - Intentionally perform a wrapping increment assignment + * @var: variable to be incremented + * @offset: amount to add + * + * Increments @var by @offset with wrap-around. Returns the resulting + * value of @var. Will not trip any wrap-around sanitizers. + * + * Returns the new value of @var. + */ +#define wrapping_assign_add(var, offset) \ + ({ \ + typeof(var) *__ptr = &(var); \ + *__ptr = wrapping_add(typeof(var), *__ptr, offset); \ + }) + /** * check_sub_overflow() - Calculate subtraction with overflow checking * @a: minuend; value to subtract from @@ -109,6 +125,22 @@ static inline bool __must_check __must_check_overflow(bool overflow) __val; \ }) +/** + * wrapping_assign_sub() - Intentionally perform a wrapping decrement assign + * @var: variable to be decremented + * @offset: amount to subtract + * + * Decrements @var by @offset with wrap-around. Returns the resulting + * value of @var. Will not trip any wrap-around sanitizers. + * + * Returns the new value of @var. + */ +#define wrapping_assign_sub(var, offset) \ + ({ \ + typeof(var) *__ptr = &(var); \ + *__ptr = wrapping_sub(typeof(var), *__ptr, offset); \ + }) + /** * check_mul_overflow() - Calculate multiplication with overflow checking * @a: first factor diff --git a/lib/overflow_kunit.c b/lib/overflow_kunit.c index d3fdb906d3fe..65e8a72a83bf 100644 --- a/lib/overflow_kunit.c +++ b/lib/overflow_kunit.c @@ -284,6 +284,45 @@ DEFINE_TEST_ARRAY(s64) = { "Unexpected wrap " #op " macro side-effect!\n"); \ } while (0) +static int global_counter; +static void bump_counter(void) +{ + global_counter++; +} + +static int get_index(void) +{ + volatile int index = 0; + bump_counter(); + return index; +} + +#define check_self_op(fmt, op, sym, a, b) do { \ + typeof(a + 0) _a = a; \ + typeof(b + 0) _b = b; \ + typeof(a + 0) _a_sym = a; \ + typeof(a + 0) _a_orig[1] = { a }; \ + typeof(b + 0) _b_orig = b; \ + typeof(b + 0) _b_bump = b + 1; \ + typeof(a + 0) _r; \ + \ + _a_sym sym _b; \ + _r = wrapping_ ## op(_a, _b); \ + KUNIT_EXPECT_TRUE_MSG(test, _r == _a_sym, \ + "expected "fmt" "#op" "fmt" == "fmt", got "fmt"\n", \ + a, b, _a_sym, _r); \ + KUNIT_EXPECT_TRUE_MSG(test, _a == _a_sym, \ + "expected "fmt" "#op" "fmt" == "fmt", got "fmt"\n", \ + a, b, _a_sym, _a); \ + /* Check for internal macro side-effects. */ \ + global_counter = 0; \ + wrapping_ ## op(_a_orig[get_index()], _b_orig++); \ + KUNIT_EXPECT_EQ_MSG(test, global_counter, 1, \ + "Unexpected wrapping_" #op " macro side-effect on arg1!\n"); \ + KUNIT_EXPECT_EQ_MSG(test, _b_orig, _b_bump, \ + "Unexpected wrapping_" #op " macro side-effect on arg2!\n"); \ +} while (0) + #define DEFINE_TEST_FUNC_TYPED(n, t, fmt) \ static void do_test_ ## n(struct kunit *test, const struct test_ ## n *p) \ { \ @@ -293,6 +332,10 @@ static void do_test_ ## n(struct kunit *test, const struct test_ ## n *p) \ check_one_op(t, fmt, sub, "-", p->a, p->b, p->diff, p->d_of); \ check_one_op(t, fmt, mul, "*", p->a, p->b, p->prod, p->p_of); \ check_one_op(t, fmt, mul, "*", p->b, p->a, p->prod, p->p_of); \ + /* wrapping_assign_{add,sub}() */ \ + check_self_op(fmt, assign_add, +=, p->a, p->b); \ + check_self_op(fmt, assign_add, +=, p->b, p->a); \ + check_self_op(fmt, assign_sub, -=, p->a, p->b); \ } \ \ static void n ## _overflow_test(struct kunit *test) { \ -- 2.34.1 ^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2024-02-14 19:38 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-02-13 22:10 [PATCH v6 0/3] overflow: Introduce wrapping helpers Kees Cook 2024-02-13 22:10 ` [PATCH v6 1/3] overflow: Adjust check_*_overflow() kern-doc to reflect results Kees Cook 2024-02-14 11:57 ` Mark Rutland 2024-02-14 19:38 ` Kees Cook 2024-02-13 22:10 ` [PATCH v6 2/3] overflow: Introduce wrapping_add(), wrapping_sub(), and wrapping_mul() Kees Cook 2024-02-14 11:58 ` Mark Rutland 2024-02-13 22:10 ` [PATCH v6 3/3] overflow: Introduce wrapping_assign_add() and wrapping_assign_sub() Kees Cook
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox