public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r11-4998] libstdc++: Avoid 32-bit time_t overflows in futex calls
@ 2020-11-13 17:19 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2020-11-13 17:19 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:e7e0eeeb6e6707be2a6c6da49d4b6be3199e2af8

commit r11-4998-ge7e0eeeb6e6707be2a6c6da49d4b6be3199e2af8
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Nov 13 15:19:04 2020 +0000

    libstdc++: Avoid 32-bit time_t overflows in futex calls
    
    The existing code doesn't check whether the chrono::seconds value is out
    of range of time_t. When using a timeout before the epoch (with a
    negative value) subtracting the current time (as time_t) and then
    assigning it to a time_t can overflow to a large positive value. This
    means that we end up waiting several years even though the specific
    timeout was in the distant past.
    
    We do have a check for negative timeouts, but that happens after the
    conversion to time_t so happens after the overflow.
    
    The conversion to a relative timeout is done in two places, so this
    factors it into a new function and adds the overflow checks there.
    
    libstdc++-v3/ChangeLog:
    
            * src/c++11/futex.cc (relative_timespec): New function to
            create relative time from two absolute times.
            (__atomic_futex_unsigned_base::_M_futex_wait_until)
            (__atomic_futex_unsigned_base::_M_futex_wait_until_steady):
            Use relative_timespec.

Diff:
---
 libstdc++-v3/src/c++11/futex.cc | 79 +++++++++++++++++++++++++++--------------
 1 file changed, 53 insertions(+), 26 deletions(-)

diff --git a/libstdc++-v3/src/c++11/futex.cc b/libstdc++-v3/src/c++11/futex.cc
index 57f7dfe87e9..c2b2d32e8c4 100644
--- a/libstdc++-v3/src/c++11/futex.cc
+++ b/libstdc++-v3/src/c++11/futex.cc
@@ -31,6 +31,7 @@
 #include <unistd.h>
 #include <sys/time.h>
 #include <errno.h>
+#include <ext/numeric_traits.h>
 #include <debug/debug.h>
 
 #ifdef _GLIBCXX_USE_CLOCK_GETTIME_SYSCALL
@@ -46,20 +47,55 @@ const unsigned futex_clock_realtime_flag = 256;
 const unsigned futex_bitset_match_any = ~0;
 const unsigned futex_wake_op = 1;
 
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
 namespace
 {
   std::atomic<bool> futex_clock_realtime_unavailable;
   std::atomic<bool> futex_clock_monotonic_unavailable;
-}
 
-namespace std _GLIBCXX_VISIBILITY(default)
-{
-_GLIBCXX_BEGIN_NAMESPACE_VERSION
+  // Return the relative duration from (now_s + now_ns) to (abs_s + abs_ns)
+  // as a timespec.
+  struct timespec
+  relative_timespec(chrono::seconds abs_s, chrono::nanoseconds abs_ns,
+		    time_t now_s, long now_ns)
+  {
+    struct timespec rt;
+
+    // Did we already time out?
+    if (now_s > abs_s.count())
+      {
+	rt.tv_sec = -1;
+	return rt;
+      }
+
+    auto rel_s = abs_s.count() - now_s;
+
+    // Avoid overflows
+    if (rel_s > __gnu_cxx::__int_traits<time_t>::__max)
+      rel_s = __gnu_cxx::__int_traits<time_t>::__max;
+    else if (rel_s < __gnu_cxx::__int_traits<time_t>::__min)
+      rel_s = __gnu_cxx::__int_traits<time_t>::__min;
+
+    // Convert the absolute timeout value to a relative timeout
+    rt.tv_sec = rel_s;
+    rt.tv_nsec = abs_ns.count() - now_ns;
+    if (rt.tv_nsec < 0)
+      {
+	rt.tv_nsec += 1000000000;
+	--rt.tv_sec;
+      }
+
+    return rt;
+  }
+} // namespace
 
   bool
-  __atomic_futex_unsigned_base::_M_futex_wait_until(unsigned *__addr,
-      unsigned __val,
-      bool __has_timeout, chrono::seconds __s, chrono::nanoseconds __ns)
+  __atomic_futex_unsigned_base::
+  _M_futex_wait_until(unsigned *__addr, unsigned __val, bool __has_timeout,
+		      chrono::seconds __s, chrono::nanoseconds __ns)
   {
     if (!__has_timeout)
       {
@@ -109,15 +145,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	// true or has just been set to true.
 	struct timeval tv;
 	gettimeofday (&tv, NULL);
+
 	// Convert the absolute timeout value to a relative timeout
-	struct timespec rt;
-	rt.tv_sec = __s.count() - tv.tv_sec;
-	rt.tv_nsec = __ns.count() - tv.tv_usec * 1000;
-	if (rt.tv_nsec < 0)
-	  {
-	    rt.tv_nsec += 1000000000;
-	    --rt.tv_sec;
-	  }
+	auto rt = relative_timespec(__s, __ns, tv.tv_sec, tv.tv_usec * 1000);
+
 	// Did we already time out?
 	if (rt.tv_sec < 0)
 	  return false;
@@ -134,9 +165,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   }
 
   bool
-  __atomic_futex_unsigned_base::_M_futex_wait_until_steady(unsigned *__addr,
-      unsigned __val,
-      bool __has_timeout, chrono::seconds __s, chrono::nanoseconds __ns)
+  __atomic_futex_unsigned_base::
+  _M_futex_wait_until_steady(unsigned *__addr, unsigned __val,
+			     bool __has_timeout,
+			     chrono::seconds __s, chrono::nanoseconds __ns)
   {
     if (!__has_timeout)
       {
@@ -188,15 +220,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #else
 	clock_gettime(CLOCK_MONOTONIC, &ts);
 #endif
+
 	// Convert the absolute timeout value to a relative timeout
-	struct timespec rt;
-	rt.tv_sec = __s.count() - ts.tv_sec;
-	rt.tv_nsec = __ns.count() - ts.tv_nsec;
-	if (rt.tv_nsec < 0)
-	  {
-	    rt.tv_nsec += 1000000000;
-	    --rt.tv_sec;
-	  }
+	auto rt = relative_timespec(__s, __ns, ts.tv_sec, ts.tv_nsec);
+
 	// Did we already time out?
 	if (rt.tv_sec < 0)
 	  return false;


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-11-13 17:19 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-13 17:19 [gcc r11-4998] libstdc++: Avoid 32-bit time_t overflows in futex calls 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).