libstdc++: Remove UB from month and weekday additions and subtractions.
The following invoke signed integer overflow (UB) [1]: month + months{MAX} // where MAX is the maximum value of months::rep month + months{MIN} // where MIN is the maximum value of months::rep month - months{MIN} // where MIN is the minimum value of months::rep weekday + days {MAX} // where MAX is the maximum value of days::rep weekday - days {MIN} // where MIN is the minimum value of days::rep For the additions to MAX, the crux of the problem is that, in libstdc++, months::rep and days::rep are int64_t. Other implementations use int32_t, cast operands to int64_t and perform arithmetic operations without risk of overflowing. For month + months{MIN}, the implementation follows the Standard's "returns clause" and evaluates: modulo(static_cast<long long>(unsigned{__x}) + (__y.count() - 1), 12); Overflow occurs when MIN - 1 is evaluated. Casting to a larger type could help but, unfortunately again, this is not possible for libstdc++. For the subtraction of MIN, the problem is that -MIN is not representable. It's fair to say that the intention is for these additions/subtractions to be performed in modulus (12 or 7) arithmetic so that no overflow is expected. To fix these UB, this patch implements: template <unsigned __d, typename _T> unsigned __add_modulo(unsigned __x, _T __y); template <unsigned __d, typename _T> unsigned __sub_modulo(unsigned __x, _T __y); which respectively, returns the remainder of Euclidean division of, __x + __y and __x - __y by __d without overflowing. These functions replace constexpr unsigned __modulo(long long __n, unsigned __d); which also calculates the reminder of __n, where __n is the result of the addition or subtraction. Hence, these operations might invoke UB before __modulo is called and thus, __modulo can't do anything to remediate the issue. In addition to solve the UB issues, __add_modulo and __sub_modulo allow better codegen (shorter and branchless) on x86-64 and ARM [2]. [1] https://godbolt.org/z/a9YfWdn57 [2] https://godbolt.org/z/Gh36cr7E4 libstdc++-v3/ChangeLog: * include/std/chrono: Fix + and - for months and weekdays. * testsuite/std/time/month/1.cc: Add constexpr tests against overflow. * testsuite/std/time/month/2.cc: New test for extreme values. * testsuite/std/time/weekday/1.cc: Add constexpr tests against overflow. * testsuite/std/time/weekday/2.cc: New test for extreme values.
Showing
- libstdc++-v3/include/std/chrono 54 additions, 25 deletionslibstdc++-v3/include/std/chrono
- libstdc++-v3/testsuite/std/time/month/1.cc 19 additions, 0 deletionslibstdc++-v3/testsuite/std/time/month/1.cc
- libstdc++-v3/testsuite/std/time/month/2.cc 32 additions, 0 deletionslibstdc++-v3/testsuite/std/time/month/2.cc
- libstdc++-v3/testsuite/std/time/weekday/1.cc 14 additions, 2 deletionslibstdc++-v3/testsuite/std/time/weekday/1.cc
- libstdc++-v3/testsuite/std/time/weekday/2.cc 32 additions, 0 deletionslibstdc++-v3/testsuite/std/time/weekday/2.cc
Loading
Please register or sign in to comment