time64_to_tm() keeps the day count in a long, which wraps on 32-bit
kernels once it reaches 2^31 days (about 5.88 million years after the
epoch). Patch 1 widens it to s64; patch 2 adds a KUnit case at that
boundary.
Why years beyond LONG_MAX are not handled: with this series the
calculation is correct on 32-bit for every year that fits struct tm's
long tm_year. Timestamps whose year - 1900 exceeds LONG_MAX (about 2.1
billion years; time64_t reaches about 292 billion) still truncate, but
only in the final assignment to tm_year. Fixing that means widening
tm_year for every struct tm user, so I have left it out of this fix.
Changes in v2:
- Split the KUnit case into its own patch so the fix can be backported
on its own (Thomas).
- Declare 'long rem;' before 's64 days;' (Thomas).
- Carried Thomas' Reviewed-by on both patches; the code is unchanged
apart from the declaration order.
Testing: time_test_cases KUnit suite under QEMU, i386 and arm, built
with clang 22 (LLVM=1); arm also with gcc 13.3. With the series both cases
pass. With only patch 2 applied the new case fails on both architectures
with tm_year=-5879541 and wrong tm_mon, tm_mday, tm_yday and tm_wday.
timeconv.o references only div_s64_rem() and div64_u64_rem(), no compiler
runtime division helpers.
v1: https://lore.kernel.org/r/8d421725051450cecf0a7e6d2c514b6926df43b1.1788019619.git.kmehltretter@gmail.com
Karl Mehltretter (2):
time: Prevent time64_to_tm() day truncation on 32-bit
time/kunit: Add time64_to_tm() case beyond 32-bit day count
kernel/time/time_test.c | 16 ++++++++++++++++
kernel/time/timeconv.c | 6 ++++--
2 files changed, 20 insertions(+), 2 deletions(-)
base-commit: 50d05c7c76c96b90462f24debacca971d2e86713
--
2.39.5 (Apple Git-154)