]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Fix UB in weekday::weekday(sys_days) and add test
authorCassio Neri <cassio.neri@gmail.com>
Sun, 12 Nov 2023 01:33:52 +0000 (01:33 +0000)
committerJonathan Wakely <jwakely@redhat.com>
Wed, 13 Mar 2024 09:49:53 +0000 (09:49 +0000)
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.

(cherry picked from commit f6ce081d0ffb5f25d71eb2f30fcfdff7f20dba22)

libstdc++-v3/include/std/chrono
libstdc++-v3/testsuite/std/time/weekday/1.cc

index 83e0e5b8bd042dc8300dc24bb707a9b94469b783..f5a88331b1b50ac3ad2539d50db8148ebc80860a 100644 (file)
@@ -573,8 +573,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:
index 6e7989ec103b5acbc0c234d277cad15a1607ae66..96cd5ebc0d8dd0b9510ff45f4d6dbe0d3b4619bf 100644 (file)
@@ -21,6 +21,7 @@
 // Class template day [time.cal.weekday]
 
 #include <chrono>
+#include <limits>
 
 constexpr void
 constexpr_weekday()
@@ -38,6 +39,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);