* [PATCH 0/2] overflow: extend shift helpers and add size_shl()
@ 2025-11-28 3:38 Rafael V. Volkmer
2025-11-28 3:38 ` [PATCH 1/2] overflow: make check_shl_overflow() 128-bit aware Rafael V. Volkmer
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Rafael V. Volkmer @ 2025-11-28 3:38 UTC (permalink / raw)
To: Kees Cook
Cc: Gustavo A . R . Silva, linux-hardening, linux-kernel,
Rafael V. Volkmer
Hello,
while experimenting with check_shl_overflow() on an architecture with
native __int128 support, I noticed that the helper always reports
overflow as soon as the promoted type of @a/@d is wider than 64 bits,
even when *@d is actually able to represent the shifted value.
The current implementation always evaluates (@a << @s) through an
unsigned long long accumulator:
unsigned long long _a_full = _a;
*_d = (_a_full << _to_shift);
When @a is __int128, this truncates it to 64 bits before the shift and
then checks for overflow against the truncated result. As a consequence,
the helper cannot reason correctly about __int128 inputs.
A minimal reproducer looks like this:
__int128 a = (__int128)1 << 80;
unsigned int s = 4;
__int128 res = 0;
bool overflow = check_shl_overflow(a, s, &res);
On a toolchain and architecture that support __int128, this reports
overflow even though __int128 can hold the full result of (1 << 84).
This series introduces an internal evaluation type selector that widens
the accumulator to unsigned __int128 when available and needed, and
then builds a size_t-focused size_shl() helper on top of the
existing size_* family.
Patch 1 adds __shl_eval_type(), which chooses the accumulator type based
on the promoted width of @a and *@d. On CONFIG_ARCH_SUPPORTS_INT128
systems it uses u128 whenever the expression is wider than 64 bits,
otherwise it keeps the existing unsigned long long behaviour. This keeps
code generation identical for all current 64-bit users, while allowing
callers that use __int128 for intermediate arithmetic (e.g. 64x64->128
products or wide offset calculations) to benefit from a wider accumulator
before eventually truncating back to a narrower destination type.
Patch 2 adds size_shl(), a saturating left-shift helper for size_t that
mirrors size_add(), size_sub() and size_mul(). It computes
(value << shift) and returns SIZE_MAX whenever check_shl_overflow()
reports an invalid or overflowing shift, which makes it easier to build
higher-level size calculations that must treat any shift overflow as a
hard upper bound.
Testing
-------
- built and boot-tested defconfig kernels on x86_64 and arm64, but with
few drivers/modules enabled in kconfig, and
- exercised check_shl_overflow() with 32-bit, 64-bit and __int128
inputs in a standalone userspace test.
For convenience during review, a small userspace snippet mirroring
the change is available on Compiler Explorer:
https://godbolt.org/z/5dq413d73
-------
Feedback on the approach is very welcome.
Thanks for your time and review.
Best regards,
Rafael V. Volkmer
Rafael V. Volkmer (2):
overflow: make check_shl_overflow() 128-bit aware
overflow: add size_shl() helper for saturated left shifts
include/linux/overflow.h | 41 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 40 insertions(+), 1 deletion(-)
--
2.43.0
^ permalink raw reply [flat|nested] 4+ messages in thread
* [PATCH 1/2] overflow: make check_shl_overflow() 128-bit aware
2025-11-28 3:38 [PATCH 0/2] overflow: extend shift helpers and add size_shl() Rafael V. Volkmer
@ 2025-11-28 3:38 ` Rafael V. Volkmer
2025-11-28 3:38 ` [PATCH 2/2] overflow: add size_shl() helper for saturated left shifts Rafael V. Volkmer
2026-01-16 0:43 ` [PATCH 0/2] overflow: extend shift helpers and add size_shl() Kees Cook
2 siblings, 0 replies; 4+ messages in thread
From: Rafael V. Volkmer @ 2025-11-28 3:38 UTC (permalink / raw)
To: Kees Cook
Cc: Gustavo A . R . Silva, linux-hardening, linux-kernel,
Rafael V. Volkmer
check_shl_overflow() currently evaluates (@a << @s) in an
unsigned long long accumulator. When callers pass __int128/u128
values, the intermediate is truncated to 64 bits before the
comparison, so the helper always reports overflow and returns a
zeroed result even when *@d is wide enough to hold the full shift.
Introduce __shl_eval_type() to derive the internal evaluation type
from @a and *@d. On architectures with CONFIG_ARCH_SUPPORTS_INT128
and compiler support for __int128, it promotes the accumulator to
u128 when the promoted sum of @a and *@d is wider than 64 bits;
otherwise it stays in an unsigned 64-bit type.
This keeps the accumulator unsigned (avoiding UB when left-shifting
negative signed values), preserves existing code generation for all
current 32/64-bit users, and fixes the spurious overflow reporting
for 128-bit shift users.
Signed-off-by: Rafael V. Volkmer <rafael.v.volkmer@gmail.com>
---
include/linux/overflow.h | 22 +++++++++++++++++++++-
1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/include/linux/overflow.h b/include/linux/overflow.h
index 725f95f7e416..ca8252e625d5 100644
--- a/include/linux/overflow.h
+++ b/include/linux/overflow.h
@@ -175,6 +175,26 @@ static inline bool __must_check __must_check_overflow(bool overflow)
__val; \
})
+/**
+ * __shl_eval_type() - Choose evaluation type for shift checks
+ * @a: value to be shifted
+ * @d: destination pointer
+ *
+ * Returns the internal type used by check_shl_overflow() to evaluate
+ * (@a << @s), widening to unsigned __int128 when available and either
+ * @a or *@d promote wider than 64 bits, otherwise using unsigned long long.
+ */
+#if defined(__SIZEOF_INT128__)
+#define __shl_eval_type(a, d) \
+ typeof(__builtin_choose_expr( \
+ sizeof((a) + (typeof(*(d)))0) > sizeof(unsigned long long), \
+ (unsigned __int128)0, \
+ 0ULL))
+#else
+#define __shl_eval_type(a, d) \
+ typeof(0ULL + (a) + (typeof(*(d)))0)
+#endif
+
/**
* check_shl_overflow() - Calculate a left-shifted value and check overflow
* @a: Value to be shifted
@@ -199,7 +219,7 @@ static inline bool __must_check __must_check_overflow(bool overflow)
typeof(a) _a = a; \
typeof(s) _s = s; \
typeof(d) _d = d; \
- unsigned long long _a_full = _a; \
+ __shl_eval_type(_a, _d) _a_full = (__shl_eval_type(_a, _d))_a; \
unsigned int _to_shift = \
is_non_negative(_s) && _s < 8 * sizeof(*d) ? _s : 0; \
*_d = (_a_full << _to_shift); \
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] overflow: add size_shl() helper for saturated left shifts
2025-11-28 3:38 [PATCH 0/2] overflow: extend shift helpers and add size_shl() Rafael V. Volkmer
2025-11-28 3:38 ` [PATCH 1/2] overflow: make check_shl_overflow() 128-bit aware Rafael V. Volkmer
@ 2025-11-28 3:38 ` Rafael V. Volkmer
2026-01-16 0:43 ` [PATCH 0/2] overflow: extend shift helpers and add size_shl() Kees Cook
2 siblings, 0 replies; 4+ messages in thread
From: Rafael V. Volkmer @ 2025-11-28 3:38 UTC (permalink / raw)
To: Kees Cook
Cc: Gustavo A . R . Silva, linux-hardening, linux-kernel,
Rafael V. Volkmer
Introduce size_shl() as a sibling to size_mul(), size_add() and size_sub()
for the left-shift case. It computes value << shift with both operands
promoted to size_t and uses check_shl_overflow() to detect invalid or
overflowing shifts, returning SIZE_MAX on failure.
No functional change for existing callers; this only adds a new helper.
Signed-off-by: Rafael V. Volkmer <rafael.v.volkmer@gmail.com>
---
include/linux/overflow.h | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/include/linux/overflow.h b/include/linux/overflow.h
index ca8252e625d5..91d0b5b00a56 100644
--- a/include/linux/overflow.h
+++ b/include/linux/overflow.h
@@ -404,6 +404,25 @@ static inline size_t __must_check size_sub(size_t minuend, size_t subtrahend)
return bytes;
}
+/**
+ * size_shl() - Calculate size_t left shift with saturation at SIZE_MAX
+ * @value: value to be shifted
+ * @shift: how many bits left to shift
+ *
+ * Returns: calculate @value << @shift, both promoted to size_t, with any
+ * overflow or invalid shift causing the return value to be SIZE_MAX. The
+ * lvalue must be size_t to avoid implicit type conversion.
+ */
+static inline size_t __must_check size_shl(size_t value, size_t shift)
+{
+ size_t out;
+
+ if (check_shl_overflow(value, shift, &out))
+ return SIZE_MAX;
+
+ return out;
+}
+
/**
* array_size() - Calculate size of 2-dimensional array.
* @a: dimension one
--
2.43.0
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH 0/2] overflow: extend shift helpers and add size_shl()
2025-11-28 3:38 [PATCH 0/2] overflow: extend shift helpers and add size_shl() Rafael V. Volkmer
2025-11-28 3:38 ` [PATCH 1/2] overflow: make check_shl_overflow() 128-bit aware Rafael V. Volkmer
2025-11-28 3:38 ` [PATCH 2/2] overflow: add size_shl() helper for saturated left shifts Rafael V. Volkmer
@ 2026-01-16 0:43 ` Kees Cook
2 siblings, 0 replies; 4+ messages in thread
From: Kees Cook @ 2026-01-16 0:43 UTC (permalink / raw)
To: Rafael V. Volkmer; +Cc: Gustavo A . R . Silva, linux-hardening, linux-kernel
On Fri, Nov 28, 2025 at 12:38:28AM -0300, Rafael V. Volkmer wrote:
> while experimenting with check_shl_overflow() on an architecture with
> native __int128 support, I noticed that the helper always reports
> overflow as soon as the promoted type of @a/@d is wider than 64 bits,
> even when *@d is actually able to represent the shifted value.
Ah, whoops. Yeah. Can you add some tests for int128 to
lib/tests/overflow_kunit.c (see how check_one_op() uses:
check_ ## op ## _overflow
there's a bunch of type checking in there but nothing uses int128, so
there might be more than just shl?
> On a toolchain and architecture that support __int128, this reports
What's the right way to attempt such testing?
--
Kees Cook
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-01-16 0:43 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-28 3:38 [PATCH 0/2] overflow: extend shift helpers and add size_shl() Rafael V. Volkmer
2025-11-28 3:38 ` [PATCH 1/2] overflow: make check_shl_overflow() 128-bit aware Rafael V. Volkmer
2025-11-28 3:38 ` [PATCH 2/2] overflow: add size_shl() helper for saturated left shifts Rafael V. Volkmer
2026-01-16 0:43 ` [PATCH 0/2] overflow: extend shift helpers and add size_shl() Kees Cook
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox