diff options
author | Cassio Neri <cassio.neri@gmail.com> | 2023-11-12 01:33:52 +0000 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2023-11-14 22:32:08 +0000 |
commit | f6ce081d0ffb5f25d71eb2f30fcfdff7f20dba22 (patch) | |
tree | beb9a50210c8d8edcae4a919c9dc678cfac1cc23 | |
parent | 86a0df1a6c7fe4a835620b868e76ea78d42d6620 (diff) | |
download | gcc-f6ce081d0ffb5f25d71eb2f30fcfdff7f20dba22.zip gcc-f6ce081d0ffb5f25d71eb2f30fcfdff7f20dba22.tar.gz gcc-f6ce081d0ffb5f25d71eb2f30fcfdff7f20dba22.tar.bz2 |
libstdc++: Fix UB in weekday::weekday(sys_days) and add test
The following has undefined behaviour (signed overflow) [1]:
weekday max{sys_days{days{numeric_limits<days::rep>::max()}}};
The issue is in this line when __n is very large and __n + 4 overflows:
return weekday(__n >= -4 ? (__n + 4) % 7 : (__n + 5) % 7 + 6);
In addition to fixing this bug, the new implementation makes the compiler emit
shorter and branchless code for x86-64 and ARM [2].
[1] https://godbolt.org/z/1s5bv7KfT
[2] https://godbolt.org/z/zKsabzrhs
libstdc++-v3/ChangeLog:
* include/std/chrono (weekday::_S_from_days): Fix UB.
* testsuite/std/time/weekday/1.cc: Add test for overflow.
-rw-r--r-- | libstdc++-v3/include/std/chrono | 11 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/std/time/weekday/1.cc | 9 |
2 files changed, 18 insertions, 2 deletions
diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono index 45e7d26..bd21ec1 100644 --- a/libstdc++-v3/include/std/chrono +++ b/libstdc++-v3/include/std/chrono @@ -930,8 +930,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static constexpr weekday _S_from_days(const days& __d) { - auto __n = __d.count(); - return weekday(__n >= -4 ? (__n + 4) % 7 : (__n + 5) % 7 + 6); + using _Rep = days::rep; + using _URep = make_unsigned_t<_Rep>; + const auto __n = __d.count(); + const auto __m = static_cast<_URep>(__n); + + // 1970-01-01 (__n = 0, __m = 0 ) -> Thursday (4) + // 1969-31-12 (__n = -1, __m = _URep(-1)) -> Wednesday (3) + const auto __offset = __n >= 0 ? _URep(4) : 3 - _URep(-1) % 7 - 7; + return weekday((__m + __offset) % 7); } public: diff --git a/libstdc++-v3/testsuite/std/time/weekday/1.cc b/libstdc++-v3/testsuite/std/time/weekday/1.cc index 00278c8..e89fca4 100644 --- a/libstdc++-v3/testsuite/std/time/weekday/1.cc +++ b/libstdc++-v3/testsuite/std/time/weekday/1.cc @@ -20,6 +20,7 @@ // Class template day [time.cal.weekday] #include <chrono> +#include <limits> constexpr void constexpr_weekday() @@ -37,6 +38,14 @@ constexpr_weekday() static_assert(weekday{3}[2].weekday() == weekday{3}); static_assert(weekday{3}[last].weekday() == weekday{3}); + // Test for UB (overflow). + { + using rep = days::rep; + using std::numeric_limits; + constexpr weekday max{sys_days{days{numeric_limits<rep>::max()}}}; + constexpr weekday min{sys_days{days{numeric_limits<rep>::min()}}}; + } + static_assert(weekday{sys_days{1900y/January/1}} == Monday); static_assert(weekday{sys_days{1970y/January/1}} == Thursday); static_assert(weekday{sys_days{2020y/August/21}} == Friday); |