summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRickard Green <rickard@erlang.org>2021-11-02 23:30:27 +0100
committerRickard Green <rickard@erlang.org>2021-11-03 15:04:54 +0100
commit98dec6cf320edb46a58fcc64eda4f644e75be755 (patch)
tree010f9d9bc6e95e47fef0b6ee3403242aee4d65a0
parent6d5a5f31c36bbdaad21585d25974177bd1b75e66 (diff)
downloaderlang-98dec6cf320edb46a58fcc64eda4f644e75be755.tar.gz
Limit timeout value in calls to select()
We previously did not limit the timeout value when calling select() which could cause select() to fail when passed a huge timeout value causing a runtime system crash. We currently only use select() on Darwin which accepts a timeout value of up to 100 million seconds. However, according to posix, select() implementations are only required to provide 31 days as max timeout value. We therefore limit the timeout value to 31 days.
-rw-r--r--erts/emulator/sys/common/erl_poll.c17
-rw-r--r--erts/lib_src/pthread/ethr_event.c26
2 files changed, 41 insertions, 2 deletions
diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c
index e669572499..a2a39de5ac 100644
--- a/erts/emulator/sys/common/erl_poll.c
+++ b/erts/emulator/sys/common/erl_poll.c
@@ -71,6 +71,12 @@
# define _DARWIN_UNLIMITED_SELECT
#endif
+/*
+ * According to posix, select() implementations should
+ * support a max timeout value of at least 31 days.
+ */
+#define ERTS_SELECT_MAX_TV_SEC__ (31*24*60*60-1)
+
#ifndef WANT_NONBLOCKING
# define WANT_NONBLOCKING
#endif
@@ -1735,6 +1741,17 @@ get_timeout_timeval(ErtsPollSet *ps,
}
else {
ErtsMonotonicTime sec = timeout/(1000*1000);
+ if (sec >= ERTS_SELECT_MAX_TV_SEC__) {
+ tvp->tv_sec = ERTS_SELECT_MAX_TV_SEC__;
+ tvp->tv_usec = 0;
+ /*
+ * If we actually should time out on
+ * this (huge) timeout, the select() call
+ * will be restarted with a newly calculated
+ * timeout after verifying the timeout...
+ */
+ return 1;
+ }
tvp->tv_sec = sec;
tvp->tv_usec = timeout - sec*(1000*1000);
diff --git a/erts/lib_src/pthread/ethr_event.c b/erts/lib_src/pthread/ethr_event.c
index 464875570a..dd8e47f8ff 100644
--- a/erts/lib_src/pthread/ethr_event.c
+++ b/erts/lib_src/pthread/ethr_event.c
@@ -36,6 +36,12 @@
# define _DARWIN_UNLIMITED_SELECT
#endif
+/*
+ * According to posix, select() implementations should
+ * support a max timeout value of at least 31 days.
+ */
+#define ETHR_SELECT_MAX_TV_SEC__ (31*24*60*60-1)
+
#include "ethread.h"
#undef ETHR_INCLUDE_MONOTONIC_CLOCK__
#define ETHR_INCLUDE_MONOTONIC_CLOCK__
@@ -500,6 +506,7 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
#endif
fd_set *rsetp, *esetp;
struct timeval select_timeout;
+ int select_timeout_res;
#ifdef ETHR_HAVE_ETHR_GET_MONOTONIC_TIME
#if ERTS_USE_PREMATURE_TIMEOUT
@@ -527,7 +534,22 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
#endif
select_timeout.tv_sec = time / (1000*1000);
- select_timeout.tv_usec = time % (1000*1000);
+
+ if (select_timeout.tv_sec <= ETHR_SELECT_MAX_TV_SEC__) {
+ select_timeout.tv_usec = time % (1000*1000);
+ select_timeout_res = ETIMEDOUT;
+ }
+ else {
+ select_timeout.tv_sec = ETHR_SELECT_MAX_TV_SEC__;
+ select_timeout.tv_usec = 0;
+ /*
+ * Return EINTR (spurious wakeup) instead of
+ * ETIMEDOUT if we time out on this (huge)
+ * timeout value. Caller is responsible for
+ * restarting the wait...
+ */
+ select_timeout_res = EINTR;
+ }
ETHR_ASSERT(val != ETHR_EVENT_ON__);
@@ -577,7 +599,7 @@ wait__(ethr_event *e, int spincount, ethr_sint64_t timeout)
sres = select(fd + 1, rsetp, NULL, esetp, &select_timeout);
if (sres == 0)
- res = ETIMEDOUT;
+ res = select_timeout_res;
else {
res = EINTR;
if (sres < 0 && errno != EINTR)