@@ -502,6 +502,11 @@ time_now (void)
__clock_gettime (TIME_CLOCK_GETTIME_CLOCKID, &ts);
return ts.tv_sec;
}
+
+#define NSEC_PER_SEC 1000000000L /* Nanoseconds per second. */
+#define USEC_PER_SEC 1000000L /* Microseconds per second. */
+#define NSEC_PER_USEC 1000L /* Nanoseconds per microsecond. */
+
#endif
#endif
@@ -19,6 +19,7 @@
#include <errno.h>
#include <support/capture_subprocess.h>
#include <support/check.h>
+#include <support/support.h>
#include <support/timespec.h>
#include <support/xunistd.h>
#include <support/xtime.h>
@@ -47,6 +48,12 @@ do_test_child (void *clousure)
int r = select (args->fds[0][0] + 1, &rfds, NULL, NULL, &args->tmo);
TEST_COMPARE (r, 0);
+ if (support_select_modify_timeout ())
+ {
+ TEST_COMPARE (args->tmo.tv_sec, 0);
+ TEST_COMPARE (args->tmo.tv_usec, 0);
+ }
+
TEST_TIMESPEC_NOW_OR_AFTER (CLOCK_REALTIME, ts);
xwrite (args->fds[1][1], "foo", 3);
@@ -69,6 +76,16 @@ do_test (void)
sc_allow_none);
}
+ if (support_select_normalize_timeout ())
+ {
+ /* This is handled as 1 second instead of failing with EINVAL. */
+ args.tmo = (struct timeval) { .tv_sec = 0, .tv_usec = 1000000 };
+ struct support_capture_subprocess result;
+ result = support_capture_subprocess (do_test_child, &args);
+ support_capture_subprocess_check (&result, "tst-select-child", 0,
+ sc_allow_none);
+ }
+
/* Same as before, but simulating polling. */
args.tmo = (struct timeval) { .tv_sec = 0, .tv_usec = 0 };
{
@@ -58,7 +58,6 @@
#define debug(msg) /*printf("svcauth_des: %s\n", msg) */
-#define USEC_PER_SEC ((uint32_t) 1000000L)
#define BEFORE(t1, t2) timercmp(t1, t2, <)
/*
@@ -68,6 +68,8 @@ libsupport-routines = \
support_quote_string \
support_record_failure \
support_run_diff \
+ support_select_modify_timeout \
+ support_select_normalize_timeout \
support_set_small_thread_stack_size \
support_shared_allocate \
support_small_stack_thread_attribute \
@@ -144,6 +144,14 @@ static __inline bool support_path_support_time64 (const char *path)
/* Return true if stat supports nanoseconds resolution. */
extern bool support_stat_nanoseconds (const char *path);
+/* Return true if select modify the timeout to reflect the amount of time
+ no slept. */
+extern bool support_select_modify_timeout (void);
+
+/* Return true if select normalize the timeout input by taking in account
+ tv_usec larger than 1000000. */
+extern bool support_select_normalize_timeout (void);
+
__END_DECLS
#endif /* SUPPORT_H */
new file mode 100644
@@ -0,0 +1,29 @@
+/* Return whether select modify the timeout.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdbool.h>
+
+bool
+support_select_modify_timeout (void)
+{
+#ifdef __linux__
+ return true;
+#else
+ return false;
+#endif
+}
new file mode 100644
@@ -0,0 +1,29 @@
+/* Return whether select normalize the timeout.
+ Copyright (C) 2021 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdbool.h>
+
+bool
+support_select_normalize_timeout (void)
+{
+#ifdef __linux__
+ return true;
+#else
+ return false;
+#endif
+}
@@ -33,12 +33,34 @@ int
__select64 (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct __timeval64 *timeout)
{
- struct __timespec64 ts64, *pts64 = NULL;
- if (timeout != NULL)
+ __time64_t s = timeout != NULL ? timeout->tv_sec : 0;
+ int32_t us = timeout != NULL ? timeout->tv_usec : 0;
+ int32_t ns;
+
+ if (s < 0 || us < 0)
+ return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
+
+ /* Normalize the timeout, as legacy Linux __NR_select and __NR__newselect.
+ Different than syscall, it also handle possible overflow. */
+ if (us / USEC_PER_SEC > INT64_MAX - s)
{
- ts64 = timeval64_to_timespec64 (*timeout);
- pts64 = &ts64;
+ s = INT64_MAX;
+ ns = NSEC_PER_SEC - 1;
}
+ else
+ {
+ s += us / USEC_PER_SEC;
+ us = us % USEC_PER_SEC;
+ ns = us * NSEC_PER_USEC;
+ }
+
+ struct __timespec64 ts64, *pts64 = NULL;
+ if (timeout != NULL)
+ {
+ ts64.tv_sec = s;
+ ts64.tv_nsec = ns;
+ pts64 = &ts64;
+ }
#ifndef __NR_pselect6_time64
# define __NR_pselect6_time64 __NR_pselect6
@@ -52,10 +74,13 @@ __select64 (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
(though the pselect() glibc call suppresses this behavior).
Since select() on Linux has the same behavior as the pselect6
syscall, we update the timeout here. */
- if (r == 0 || errno != ENOSYS)
+ if (r >= 0 || errno != ENOSYS)
{
if (timeout != NULL)
- TIMEVAL_TO_TIMESPEC (timeout, &ts64);
+ {
+ timeout->tv_sec = ts64.tv_sec;
+ timeout->tv_usec = ts64.tv_nsec / NSEC_PER_USEC;
+ }
return r;
}
@@ -71,7 +96,8 @@ __select64 (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
__set_errno (EINVAL);
return -1;
}
- ts32 = valid_timespec64_to_timespec (ts64);
+ ts32.tv_sec = s;
+ ts32.tv_nsec = ns;
pts32 = &ts32;
}
# ifndef __ASSUME_PSELECT