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.
This commit is contained in:
parent
0d1189b4e6
commit
e7e0eeeb6e
1 changed files with 55 additions and 28 deletions
|
@ -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::atomic<bool> futex_clock_realtime_unavailable;
|
||||
std::atomic<bool> futex_clock_monotonic_unavailable;
|
||||
}
|
||||
|
||||
namespace std _GLIBCXX_VISIBILITY(default)
|
||||
{
|
||||
_GLIBCXX_BEGIN_NAMESPACE_VERSION
|
||||
|
||||
namespace
|
||||
{
|
||||
std::atomic<bool> futex_clock_realtime_unavailable;
|
||||
std::atomic<bool> futex_clock_monotonic_unavailable;
|
||||
|
||||
// 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;
|
||||
|
|
Loading…
Add table
Reference in a new issue