]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Fix std::future::wait_until for subsecond negative times [PR118093]
authorJonathan Wakely <jwakely@redhat.com>
Tue, 17 Dec 2024 21:32:19 +0000 (21:32 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Wed, 8 Jan 2025 12:45:37 +0000 (12:45 +0000)
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.

libstdc++-v3/include/bits/atomic_futex.h
libstdc++-v3/src/c++11/futex.cc
libstdc++-v3/testsuite/30_threads/future/members/118093.cc [new file with mode: 0644]

index 1146f0a78a11bcca9d8b6f2d3eeccb58af0b1a6c..e69420d230550f92945a340e7f964051323f4041 100644 (file)
@@ -172,11 +172,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>
@@ -185,11 +187,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:
index 902b821843cb5a7148c5a5c0788782bc9873a72e..d3a7ca3783d23720f554810041b8bf6f5c1fe09b 100644 (file)
@@ -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 (file)
index 0000000..2bb1e1c
--- /dev/null
@@ -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();
+}