https://gcc.gnu.org/g:1a8d20cd803b1818378e06b2a437debd3cc5176f

commit r13-9536-g1a8d20cd803b1818378e06b2a437debd3cc5176f
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Tue Dec 17 21:32:19 2024 +0000

    libstdc++: Fix std::future::wait_until for subsecond negative times 
[PR118093]
    
    The current check for negative times (i.e. before the epoch) only checks
    for a negative number of seconds. For a time 1ms before the epoch the
    seconds part will be zero, but the futex syscall will still fail with an
    EINVAL error. Extend the check to handle this case.
    
    This change adds a redundant check in the headers too, so that we avoid
    even calling into the library for negative times. Both checks can be
    marked [[unlikely]]. The check in the headers avoids the cost of
    splitting the time into seconds and nanoseconds and then making a PLT
    call. The check inside the library matches where we were checking
    already, and fixes existing binaries that were compiled against older
    headers but use a newer libstdc++.so.6 at runtime.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/118093
            * include/bits/atomic_futex.h (_M_load_and_test_until_impl):
            Return false for times before the epoch.
            * src/c++11/futex.cc (_M_futex_wait_until): Extend check for
            negative times to check for subsecond times. Add unlikely
            attribute.
            (_M_futex_wait_until_steady): Likewise.
            * testsuite/30_threads/future/members/118093.cc: New test.
    
    (cherry picked from commit 8ade3c3ea77e166f2873fb7ae57f9690e2b8d0e0)

Diff:
---
 libstdc++-v3/include/bits/atomic_futex.h           | 20 ++++++++++-------
 libstdc++-v3/src/c++11/futex.cc                    |  4 ++--
 .../testsuite/30_threads/future/members/118093.cc  | 26 ++++++++++++++++++++++
 3 files changed, 40 insertions(+), 10 deletions(-)

diff --git a/libstdc++-v3/include/bits/atomic_futex.h 
b/libstdc++-v3/include/bits/atomic_futex.h
index 6b37ce5ed120..a9f2bdabc69f 100644
--- a/libstdc++-v3/include/bits/atomic_futex.h
+++ b/libstdc++-v3/include/bits/atomic_futex.h
@@ -170,11 +170,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        bool __equal, memory_order __mo,
        const chrono::time_point<std::chrono::system_clock, _Dur>& __atime)
     {
-      auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
-      auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
-      // XXX correct?
+      auto __d = __atime.time_since_epoch();
+      if (__d < __d.zero()) [[__unlikely__]]
+       return false;
+      auto __s = chrono::duration_cast<chrono::seconds>(__d);
+      auto __ns = chrono::duration_cast<chrono::nanoseconds>(__d - __s);
       return _M_load_and_test_until(__assumed, __operand, __equal, __mo,
-         true, __s.time_since_epoch(), __ns);
+                                   true, __s, __ns);
     }
 
     template<typename _Dur>
@@ -183,11 +185,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        bool __equal, memory_order __mo,
        const chrono::time_point<std::chrono::steady_clock, _Dur>& __atime)
     {
-      auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
-      auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
-      // XXX correct?
+      auto __d = __atime.time_since_epoch();
+      if (__d < __d.zero()) [[__unlikely__]]
+       return false;
+      auto __s = chrono::duration_cast<chrono::seconds>(__d);
+      auto __ns = chrono::duration_cast<chrono::nanoseconds>(__d - __s);
       return _M_load_and_test_until_steady(__assumed, __operand, __equal, __mo,
-         true, __s.time_since_epoch(), __ns);
+         true, __s, __ns);
     }
 
   public:
diff --git a/libstdc++-v3/src/c++11/futex.cc b/libstdc++-v3/src/c++11/futex.cc
index c930ab286833..698952c54076 100644
--- a/libstdc++-v3/src/c++11/futex.cc
+++ b/libstdc++-v3/src/c++11/futex.cc
@@ -128,7 +128,7 @@ namespace
        if (!futex_clock_realtime_unavailable.load(std::memory_order_relaxed))
          {
            // futex sets errno=EINVAL for absolute timeouts before the epoch.
-           if (__s.count() < 0)
+           if (__s.count() < 0 || __ns.count() < 0) [[unlikely]]
              return false;
 
            syscall_timespec rt;
@@ -204,7 +204,7 @@ namespace
        if (!futex_clock_monotonic_unavailable.load(std::memory_order_relaxed))
          {
            // futex sets errno=EINVAL for absolute timeouts before the epoch.
-           if (__s.count() < 0) [[unlikely]]
+           if (__s.count() < 0 || __ns.count() < 0) [[unlikely]]
              return false;
 
            syscall_timespec rt;
diff --git a/libstdc++-v3/testsuite/30_threads/future/members/118093.cc 
b/libstdc++-v3/testsuite/30_threads/future/members/118093.cc
new file mode 100644
index 000000000000..2bb1e1c6dadc
--- /dev/null
+++ b/libstdc++-v3/testsuite/30_threads/future/members/118093.cc
@@ -0,0 +1,26 @@
+// { dg-do run { target c++11 } }
+
+#include <chrono>
+#include <future>
+
+void
+test_sys()
+{
+  std::promise<void> p;
+  std::chrono::system_clock::time_point tp(std::chrono::milliseconds{-10});
+  (void) p.get_future().wait_until(tp);
+}
+
+void
+test_steady()
+{
+  std::promise<void> p;
+  std::chrono::steady_clock::time_point tp(std::chrono::milliseconds{-10});
+  (void) p.get_future().wait_until(tp);
+}
+
+int main()
+{
+  test_sys();
+  test_steady();
+}

Reply via email to