You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
278 lines
8.9 KiB
278 lines
8.9 KiB
commit 567f7413fb1179acd936766b542ea72f7ef60d8b |
|
Author: Adam Yi <ayi@janestreet.com> |
|
Date: Tue Mar 7 07:30:02 2023 -0500 |
|
|
|
posix: Fix system blocks SIGCHLD erroneously [BZ #30163] |
|
|
|
Fix bug that SIGCHLD is erroneously blocked forever in the following |
|
scenario: |
|
|
|
1. Thread A calls system but hasn't returned yet |
|
2. Thread B calls another system but returns |
|
|
|
SIGCHLD would be blocked forever in thread B after its system() returns, |
|
even after the system() in thread A returns. |
|
|
|
Although POSIX does not require, glibc system implementation aims to be |
|
thread and cancellation safe. This bug was introduced in |
|
5fb7fc96350575c9adb1316833e48ca11553be49 when we moved reverting signal |
|
mask to happen when the last concurrently running system returns, |
|
despite that signal mask is per thread. This commit reverts this logic |
|
and adds a test. |
|
|
|
Signed-off-by: Adam Yi <ayi@janestreet.com> |
|
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org> |
|
(cherry picked from commit 436a604b7dc741fc76b5a6704c6cd8bb178518e7) |
|
|
|
diff --git a/stdlib/tst-system.c b/stdlib/tst-system.c |
|
index 178808e048a9b3bc..d1413d7c6a4cb82c 100644 |
|
--- a/stdlib/tst-system.c |
|
+++ b/stdlib/tst-system.c |
|
@@ -26,6 +26,7 @@ |
|
#include <support/check.h> |
|
#include <support/temp_file.h> |
|
#include <support/support.h> |
|
+#include <support/xthread.h> |
|
#include <support/xunistd.h> |
|
|
|
static char *tmpdir; |
|
@@ -72,6 +73,20 @@ call_system (void *closure) |
|
} |
|
} |
|
|
|
+static void * |
|
+sleep_and_check_sigchld (void *closure) |
|
+{ |
|
+ double *seconds = (double *) closure; |
|
+ char cmd[namemax]; |
|
+ sprintf (cmd, "sleep %lf" , *seconds); |
|
+ TEST_COMPARE (system (cmd), 0); |
|
+ |
|
+ sigset_t blocked = {0}; |
|
+ TEST_COMPARE (sigprocmask (SIG_BLOCK, NULL, &blocked), 0); |
|
+ TEST_COMPARE (sigismember (&blocked, SIGCHLD), 0); |
|
+ return NULL; |
|
+} |
|
+ |
|
static int |
|
do_test (void) |
|
{ |
|
@@ -155,6 +170,17 @@ do_test (void) |
|
xchmod (_PATH_BSHELL, st.st_mode); |
|
} |
|
|
|
+ { |
|
+ pthread_t long_sleep_thread = xpthread_create (NULL, |
|
+ sleep_and_check_sigchld, |
|
+ &(double) { 0.2 }); |
|
+ pthread_t short_sleep_thread = xpthread_create (NULL, |
|
+ sleep_and_check_sigchld, |
|
+ &(double) { 0.1 }); |
|
+ xpthread_join (short_sleep_thread); |
|
+ xpthread_join (long_sleep_thread); |
|
+ } |
|
+ |
|
TEST_COMPARE (system (""), 0); |
|
|
|
return 0; |
|
diff --git a/support/Makefile b/support/Makefile |
|
index 0aa9d41c5a890087..e7f66920f1abbb48 100644 |
|
--- a/support/Makefile |
|
+++ b/support/Makefile |
|
@@ -32,6 +32,8 @@ libsupport-routines = \ |
|
check_hostent \ |
|
check_netent \ |
|
delayed_exit \ |
|
+ dtotimespec \ |
|
+ dtotimespec-time64 \ |
|
ignore_stderr \ |
|
next_to_fault \ |
|
oom_error \ |
|
diff --git a/support/dtotimespec-time64.c b/support/dtotimespec-time64.c |
|
new file mode 100644 |
|
index 0000000000000000..b3d5e351e3a631ac |
|
--- /dev/null |
|
+++ b/support/dtotimespec-time64.c |
|
@@ -0,0 +1,27 @@ |
|
+/* Convert double to timespec. 64-bit time support. |
|
+ Copyright (C) 2011-2023 Free Software Foundation, Inc. |
|
+ This file is part of the GNU C Library and is also part of gnulib. |
|
+ Patches to this file should be submitted to both projects. |
|
+ |
|
+ 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 <time.h> |
|
+ |
|
+#if __TIMESIZE != 64 |
|
+# define timespec __timespec64 |
|
+# define time_t __time64_t |
|
+# define dtotimespec dtotimespec_time64 |
|
+# include "dtotimespec.c" |
|
+#endif |
|
diff --git a/support/dtotimespec.c b/support/dtotimespec.c |
|
new file mode 100644 |
|
index 0000000000000000..cde5b4d74cba1932 |
|
--- /dev/null |
|
+++ b/support/dtotimespec.c |
|
@@ -0,0 +1,50 @@ |
|
+/* Convert double to timespec. |
|
+ Copyright (C) 2011-2023 Free Software Foundation, Inc. |
|
+ This file is part of the GNU C Library and is also part of gnulib. |
|
+ Patches to this file should be submitted to both projects. |
|
+ |
|
+ 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/>. */ |
|
+ |
|
+/* Convert the double value SEC to a struct timespec. Round toward |
|
+ positive infinity. On overflow, return an extremal value. */ |
|
+ |
|
+#include <support/timespec.h> |
|
+#include <intprops.h> |
|
+ |
|
+struct timespec |
|
+dtotimespec (double sec) |
|
+{ |
|
+ if (sec <= TYPE_MINIMUM (time_t)) |
|
+ return make_timespec (TYPE_MINIMUM (time_t), 0); |
|
+ else if (sec >= 1.0 + TYPE_MAXIMUM (time_t)) |
|
+ return make_timespec (TYPE_MAXIMUM (time_t), TIMESPEC_HZ - 1); |
|
+ else |
|
+ { |
|
+ time_t s = sec; |
|
+ double frac = TIMESPEC_HZ * (sec - s); |
|
+ long ns = frac; |
|
+ ns += ns < frac; |
|
+ s += ns / TIMESPEC_HZ; |
|
+ ns %= TIMESPEC_HZ; |
|
+ |
|
+ if (ns < 0) |
|
+ { |
|
+ s--; |
|
+ ns += TIMESPEC_HZ; |
|
+ } |
|
+ |
|
+ return make_timespec (s, ns); |
|
+ } |
|
+} |
|
diff --git a/support/shell-container.c b/support/shell-container.c |
|
index b2a4324dc7444be5..6fe925dc49075848 100644 |
|
--- a/support/shell-container.c |
|
+++ b/support/shell-container.c |
|
@@ -39,6 +39,7 @@ |
|
#include <error.h> |
|
|
|
#include <support/support.h> |
|
+#include <support/timespec.h> |
|
|
|
/* Design considerations |
|
|
|
@@ -171,6 +172,32 @@ kill_func (char **argv) |
|
return 0; |
|
} |
|
|
|
+/* Emulate the "/bin/sleep" command. No suffix support. Options are |
|
+ ignored. */ |
|
+static int |
|
+sleep_func (char **argv) |
|
+{ |
|
+ if (argv[0] == NULL) |
|
+ { |
|
+ fprintf (stderr, "sleep: missing operand\n"); |
|
+ return 1; |
|
+ } |
|
+ char *endptr = NULL; |
|
+ double sec = strtod (argv[0], &endptr); |
|
+ if (endptr == argv[0] || errno == ERANGE || sec < 0) |
|
+ { |
|
+ fprintf (stderr, "sleep: invalid time interval '%s'\n", argv[0]); |
|
+ return 1; |
|
+ } |
|
+ struct timespec ts = dtotimespec (sec); |
|
+ if (nanosleep (&ts, NULL) < 0) |
|
+ { |
|
+ fprintf (stderr, "sleep: failed to nanosleep: %s\n", strerror (errno)); |
|
+ return 1; |
|
+ } |
|
+ return 0; |
|
+} |
|
+ |
|
/* This is a list of all the built-in commands we understand. */ |
|
static struct { |
|
const char *name; |
|
@@ -181,6 +208,7 @@ static struct { |
|
{ "cp", copy_func }, |
|
{ "exit", exit_func }, |
|
{ "kill", kill_func }, |
|
+ { "sleep", sleep_func }, |
|
{ NULL, NULL } |
|
}; |
|
|
|
diff --git a/support/timespec.h b/support/timespec.h |
|
index 0478aef51fdcc5ae..843a90d60675f37d 100644 |
|
--- a/support/timespec.h |
|
+++ b/support/timespec.h |
|
@@ -57,6 +57,8 @@ int support_timespec_check_in_range (struct timespec expected, |
|
struct timespec observed, |
|
double lower_bound, double upper_bound); |
|
|
|
+struct timespec dtotimespec (double sec) __attribute__((const)); |
|
+ |
|
#else |
|
struct timespec __REDIRECT (timespec_add, (struct timespec, struct timespec), |
|
timespec_add_time64); |
|
@@ -82,6 +84,8 @@ int __REDIRECT (support_timespec_check_in_range, (struct timespec expected, |
|
double lower_bound, |
|
double upper_bound), |
|
support_timespec_check_in_range_time64); |
|
+ |
|
+struct timespec __REDIRECT (dtotimespec, (double sec), dtotimespec_time64); |
|
#endif |
|
|
|
/* Check that the timespec on the left represents a time before the |
|
diff --git a/sysdeps/posix/system.c b/sysdeps/posix/system.c |
|
index 48668fb392e67d1a..b9676abb1c6f35eb 100644 |
|
--- a/sysdeps/posix/system.c |
|
+++ b/sysdeps/posix/system.c |
|
@@ -179,16 +179,16 @@ do_system (const char *line) |
|
as if the shell had terminated using _exit(127). */ |
|
status = W_EXITCODE (127, 0); |
|
|
|
+ /* sigaction can not fail with SIGINT/SIGQUIT used with old |
|
+ disposition. Same applies for sigprocmask. */ |
|
DO_LOCK (); |
|
if (SUB_REF () == 0) |
|
{ |
|
- /* sigaction can not fail with SIGINT/SIGQUIT used with old |
|
- disposition. Same applies for sigprocmask. */ |
|
__sigaction (SIGINT, &intr, NULL); |
|
__sigaction (SIGQUIT, &quit, NULL); |
|
- __sigprocmask (SIG_SETMASK, &omask, NULL); |
|
} |
|
DO_UNLOCK (); |
|
+ __sigprocmask (SIG_SETMASK, &omask, NULL); |
|
|
|
if (ret != 0) |
|
__set_errno (ret);
|
|
|