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