public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Fix UB in weekday::weekday(sys_days) and add test.
@ 2023-11-12  1:33 Cassio Neri
  2023-11-14 22:48 ` Jonathan Wakely
  0 siblings, 1 reply; 2+ messages in thread
From: Cassio Neri @ 2023-11-12  1:33 UTC (permalink / raw)
  To: libstdc++, gcc-patches; +Cc: Cassio Neri

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: Fix weekday::_S_from_days
	* testsuite/std/time/weekday/1.cc: Add test for overflow.
---

Good for trunk?

 libstdc++-v3/include/std/chrono              | 11 +++++++++--
 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 10e868e5a03..c00dd133173 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 00278c8b01c..e89fca47d4b 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);
--
2.41.0


^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] Fix UB in weekday::weekday(sys_days) and add test.
  2023-11-12  1:33 [PATCH] Fix UB in weekday::weekday(sys_days) and add test Cassio Neri
@ 2023-11-14 22:48 ` Jonathan Wakely
  0 siblings, 0 replies; 2+ messages in thread
From: Jonathan Wakely @ 2023-11-14 22:48 UTC (permalink / raw)
  To: Cassio Neri; +Cc: libstdc++, gcc-patches

On Sun, 12 Nov 2023 at 01:34, Cassio Neri <cassio.neri@gmail.com> wrote:
>
> 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: Fix weekday::_S_from_days
>         * testsuite/std/time/weekday/1.cc: Add test for overflow.
> ---
>
> Good for trunk?

Pushed to trunk, thanks. I think this one is worth backporting too.

>
>  libstdc++-v3/include/std/chrono              | 11 +++++++++--
>  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 10e868e5a03..c00dd133173 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 00278c8b01c..e89fca47d4b 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);
> --
> 2.41.0
>


^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2023-11-14 22:48 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-11-12  1:33 [PATCH] Fix UB in weekday::weekday(sys_days) and add test Cassio Neri
2023-11-14 22:48 ` Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).