|
|
87c1f6 |
commit 14d0e87d9b8caaa2eca7ca81f1189596671fe4fb
|
|
|
87c1f6 |
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
|
|
87c1f6 |
Date: Wed Sep 12 10:32:05 2018 -0300
|
|
|
87c1f6 |
|
|
|
87c1f6 |
posix: Use posix_spawn on popen
|
|
|
87c1f6 |
|
|
|
87c1f6 |
This patch uses posix_spawn on popen instead of fork and execl. On Linux
|
|
|
87c1f6 |
this has the advantage of much lower memory consumption (usually 32 Kb
|
|
|
87c1f6 |
minimum for the mmap stack area).
|
|
|
87c1f6 |
|
|
|
87c1f6 |
Two issues are also fixed with this change:
|
|
|
87c1f6 |
|
|
|
87c1f6 |
* BZ#17490: although POSIX pthread_atfork description only list 'fork'
|
|
|
87c1f6 |
as the function that should execute the atfork handlers, popen
|
|
|
87c1f6 |
description states that:
|
|
|
87c1f6 |
|
|
|
87c1f6 |
'[...] shall be *as if* a child process were created within the popen()
|
|
|
87c1f6 |
call using the fork() function [...]'
|
|
|
87c1f6 |
|
|
|
87c1f6 |
Other libc/system seems to follow the idea atfork handlers should not be
|
|
|
87c1f6 |
executed for popen:
|
|
|
87c1f6 |
|
|
|
87c1f6 |
libc/system | run atfork handles | notes
|
|
|
87c1f6 |
------------|----------------------|---------------------------------------
|
|
|
87c1f6 |
Freebsd | no | uses vfork
|
|
|
87c1f6 |
Solaris 11 | no |
|
|
|
87c1f6 |
MacOSX 11 | no | implemented through posix_spawn syscall
|
|
|
87c1f6 |
------------|----------------------|----------------------------------------
|
|
|
87c1f6 |
|
|
|
87c1f6 |
Similar to posix_spawn and system, popen idea is to spawn a different
|
|
|
87c1f6 |
binary so all the POSIX rationale to run the atfork handlers to avoid
|
|
|
87c1f6 |
internal process inconsistency is not really required and in some cases
|
|
|
87c1f6 |
might be unsafe.
|
|
|
87c1f6 |
|
|
|
87c1f6 |
* BZ#22834: the described scenario, where the forked process might access
|
|
|
87c1f6 |
invalid memory due an inconsistent state in multithreaded environment,
|
|
|
87c1f6 |
should not happen because posix_spawn does not access the affected
|
|
|
87c1f6 |
data structure (proc_file_chain).
|
|
|
87c1f6 |
|
|
|
87c1f6 |
Checked on x86_64-linux-gnu and i686-linux-gnu.
|
|
|
87c1f6 |
|
|
|
87c1f6 |
[BZ #22834]
|
|
|
87c1f6 |
[BZ #17490]
|
|
|
87c1f6 |
* NEWS: Add new semantic for atfork with popen and system.
|
|
|
87c1f6 |
* libio/iopopen.c (_IO_new_proc_open): use posix_spawn instead of
|
|
|
87c1f6 |
fork and execl.
|
|
|
87c1f6 |
|
|
|
87c1f6 |
diff --git a/libio/iopopen.c b/libio/iopopen.c
|
|
|
87c1f6 |
index 2eff45b4c80b5cd6..c768295180fdf809 100644
|
|
|
87c1f6 |
--- a/libio/iopopen.c
|
|
|
87c1f6 |
+++ b/libio/iopopen.c
|
|
|
87c1f6 |
@@ -34,7 +34,8 @@
|
|
|
87c1f6 |
#include <not-cancel.h>
|
|
|
87c1f6 |
#include <sys/types.h>
|
|
|
87c1f6 |
#include <sys/wait.h>
|
|
|
87c1f6 |
-#include <kernel-features.h>
|
|
|
87c1f6 |
+#include <spawn.h>
|
|
|
87c1f6 |
+#include <paths.h>
|
|
|
87c1f6 |
|
|
|
87c1f6 |
struct _IO_proc_file
|
|
|
87c1f6 |
{
|
|
|
87c1f6 |
@@ -59,13 +60,60 @@ unlock (void *not_used)
|
|
|
87c1f6 |
}
|
|
|
87c1f6 |
#endif
|
|
|
87c1f6 |
|
|
|
87c1f6 |
+/* POSIX states popen shall ensure that any streams from previous popen()
|
|
|
87c1f6 |
+ calls that remain open in the parent process should be closed in the new
|
|
|
87c1f6 |
+ child process.
|
|
|
87c1f6 |
+ To avoid a race-condition between checking which file descriptors need to
|
|
|
87c1f6 |
+ be close (by transversing the proc_file_chain list) and the insertion of a
|
|
|
87c1f6 |
+ new one after a successful posix_spawn this function should be called
|
|
|
87c1f6 |
+ with proc_file_chain_lock acquired. */
|
|
|
87c1f6 |
+static bool
|
|
|
87c1f6 |
+spawn_process (posix_spawn_file_actions_t *fa, FILE *fp, const char *command,
|
|
|
87c1f6 |
+ int do_cloexec, int pipe_fds[2], int parent_end, int child_end,
|
|
|
87c1f6 |
+ int child_pipe_fd)
|
|
|
87c1f6 |
+{
|
|
|
87c1f6 |
+
|
|
|
87c1f6 |
+ for (struct _IO_proc_file *p = proc_file_chain; p; p = p->next)
|
|
|
87c1f6 |
+ {
|
|
|
87c1f6 |
+ int fd = _IO_fileno ((FILE *) p);
|
|
|
87c1f6 |
+
|
|
|
87c1f6 |
+ /* If any stream from previous popen() calls has fileno
|
|
|
87c1f6 |
+ child_pipe_fd, it has been already closed by the adddup2 action
|
|
|
87c1f6 |
+ above. */
|
|
|
87c1f6 |
+ if (fd != child_pipe_fd
|
|
|
87c1f6 |
+ && __posix_spawn_file_actions_addclose (fa, fd) != 0)
|
|
|
87c1f6 |
+ return false;
|
|
|
87c1f6 |
+ }
|
|
|
87c1f6 |
+
|
|
|
87c1f6 |
+ if (__posix_spawn (&((_IO_proc_file *) fp)->pid, _PATH_BSHELL, fa, 0,
|
|
|
87c1f6 |
+ (char *const[]){ (char*) "sh", (char*) "-c",
|
|
|
87c1f6 |
+ (char *) command, NULL }, __environ) != 0)
|
|
|
87c1f6 |
+ return false;
|
|
|
87c1f6 |
+
|
|
|
87c1f6 |
+ __close_nocancel (pipe_fds[child_end]);
|
|
|
87c1f6 |
+
|
|
|
87c1f6 |
+ if (!do_cloexec)
|
|
|
87c1f6 |
+ /* Undo the effects of the pipe2 call which set the
|
|
|
87c1f6 |
+ close-on-exec flag. */
|
|
|
87c1f6 |
+ __fcntl (pipe_fds[parent_end], F_SETFD, 0);
|
|
|
87c1f6 |
+
|
|
|
87c1f6 |
+ _IO_fileno (fp) = pipe_fds[parent_end];
|
|
|
87c1f6 |
+
|
|
|
87c1f6 |
+ ((_IO_proc_file *) fp)->next = proc_file_chain;
|
|
|
87c1f6 |
+ proc_file_chain = (_IO_proc_file *) fp;
|
|
|
87c1f6 |
+
|
|
|
87c1f6 |
+ return true;
|
|
|
87c1f6 |
+}
|
|
|
87c1f6 |
+
|
|
|
87c1f6 |
FILE *
|
|
|
87c1f6 |
_IO_new_proc_open (FILE *fp, const char *command, const char *mode)
|
|
|
87c1f6 |
{
|
|
|
87c1f6 |
int read_or_write;
|
|
|
87c1f6 |
+ /* These are indexes for pipe_fds. */
|
|
|
87c1f6 |
int parent_end, child_end;
|
|
|
87c1f6 |
int pipe_fds[2];
|
|
|
87c1f6 |
- pid_t child_pid;
|
|
|
87c1f6 |
+ int child_pipe_fd;
|
|
|
87c1f6 |
+ bool spawn_ok;
|
|
|
87c1f6 |
|
|
|
87c1f6 |
int do_read = 0;
|
|
|
87c1f6 |
int do_write = 0;
|
|
|
87c1f6 |
@@ -108,72 +156,62 @@ _IO_new_proc_open (FILE *fp, const char *command, const char *mode)
|
|
|
87c1f6 |
|
|
|
87c1f6 |
if (do_read)
|
|
|
87c1f6 |
{
|
|
|
87c1f6 |
- parent_end = pipe_fds[0];
|
|
|
87c1f6 |
- child_end = pipe_fds[1];
|
|
|
87c1f6 |
+ parent_end = 0;
|
|
|
87c1f6 |
+ child_end = 1;
|
|
|
87c1f6 |
read_or_write = _IO_NO_WRITES;
|
|
|
87c1f6 |
+ child_pipe_fd = 1;
|
|
|
87c1f6 |
}
|
|
|
87c1f6 |
else
|
|
|
87c1f6 |
{
|
|
|
87c1f6 |
- parent_end = pipe_fds[1];
|
|
|
87c1f6 |
- child_end = pipe_fds[0];
|
|
|
87c1f6 |
+ parent_end = 1;
|
|
|
87c1f6 |
+ child_end = 0;
|
|
|
87c1f6 |
read_or_write = _IO_NO_READS;
|
|
|
87c1f6 |
+ child_pipe_fd = 0;
|
|
|
87c1f6 |
}
|
|
|
87c1f6 |
|
|
|
87c1f6 |
- ((_IO_proc_file *) fp)->pid = child_pid = __fork ();
|
|
|
87c1f6 |
- if (child_pid == 0)
|
|
|
87c1f6 |
- {
|
|
|
87c1f6 |
- int child_std_end = do_read ? 1 : 0;
|
|
|
87c1f6 |
- struct _IO_proc_file *p;
|
|
|
87c1f6 |
-
|
|
|
87c1f6 |
- if (child_end != child_std_end)
|
|
|
87c1f6 |
- __dup2 (child_end, child_std_end);
|
|
|
87c1f6 |
- else
|
|
|
87c1f6 |
- /* The descriptor is already the one we will use. But it must
|
|
|
87c1f6 |
- not be marked close-on-exec. Undo the effects. */
|
|
|
87c1f6 |
- __fcntl (child_end, F_SETFD, 0);
|
|
|
87c1f6 |
- /* POSIX.2: "popen() shall ensure that any streams from previous
|
|
|
87c1f6 |
- popen() calls that remain open in the parent process are closed
|
|
|
87c1f6 |
- in the new child process." */
|
|
|
87c1f6 |
- for (p = proc_file_chain; p; p = p->next)
|
|
|
87c1f6 |
- {
|
|
|
87c1f6 |
- int fd = _IO_fileno ((FILE *) p);
|
|
|
87c1f6 |
+ posix_spawn_file_actions_t fa;
|
|
|
87c1f6 |
+ /* posix_spawn_file_actions_init does not fail. */
|
|
|
87c1f6 |
+ __posix_spawn_file_actions_init (&fa);
|
|
|
87c1f6 |
|
|
|
87c1f6 |
- /* If any stream from previous popen() calls has fileno
|
|
|
87c1f6 |
- child_std_end, it has been already closed by the dup2 syscall
|
|
|
87c1f6 |
- above. */
|
|
|
87c1f6 |
- if (fd != child_std_end)
|
|
|
87c1f6 |
- __close_nocancel (fd);
|
|
|
87c1f6 |
- }
|
|
|
87c1f6 |
-
|
|
|
87c1f6 |
- execl ("/bin/sh", "sh", "-c", command, (char *) 0);
|
|
|
87c1f6 |
- _exit (127);
|
|
|
87c1f6 |
- }
|
|
|
87c1f6 |
- __close_nocancel (child_end);
|
|
|
87c1f6 |
- if (child_pid < 0)
|
|
|
87c1f6 |
+ /* The descriptor is already the one the child will use. In this case
|
|
|
87c1f6 |
+ it must be moved to another one otherwise, there is no safe way to
|
|
|
87c1f6 |
+ remove the close-on-exec flag in the child without creating a FD leak
|
|
|
87c1f6 |
+ race in the parent. */
|
|
|
87c1f6 |
+ if (pipe_fds[child_end] == child_pipe_fd)
|
|
|
87c1f6 |
{
|
|
|
87c1f6 |
- __close_nocancel (parent_end);
|
|
|
87c1f6 |
- return NULL;
|
|
|
87c1f6 |
+ int tmp = __fcntl (child_pipe_fd, F_DUPFD_CLOEXEC, 0);
|
|
|
87c1f6 |
+ if (tmp < 0)
|
|
|
87c1f6 |
+ goto spawn_failure;
|
|
|
87c1f6 |
+ __close_nocancel (pipe_fds[child_end]);
|
|
|
87c1f6 |
+ pipe_fds[child_end] = tmp;
|
|
|
87c1f6 |
}
|
|
|
87c1f6 |
|
|
|
87c1f6 |
- if (!do_cloexec)
|
|
|
87c1f6 |
- /* Undo the effects of the pipe2 call which set the
|
|
|
87c1f6 |
- close-on-exec flag. */
|
|
|
87c1f6 |
- __fcntl (parent_end, F_SETFD, 0);
|
|
|
87c1f6 |
+ if (__posix_spawn_file_actions_adddup2 (&fa, pipe_fds[child_end],
|
|
|
87c1f6 |
+ child_pipe_fd) != 0)
|
|
|
87c1f6 |
+ goto spawn_failure;
|
|
|
87c1f6 |
|
|
|
87c1f6 |
- _IO_fileno (fp) = parent_end;
|
|
|
87c1f6 |
-
|
|
|
87c1f6 |
- /* Link into proc_file_chain. */
|
|
|
87c1f6 |
#ifdef _IO_MTSAFE_IO
|
|
|
87c1f6 |
_IO_cleanup_region_start_noarg (unlock);
|
|
|
87c1f6 |
_IO_lock_lock (proc_file_chain_lock);
|
|
|
87c1f6 |
#endif
|
|
|
87c1f6 |
- ((_IO_proc_file *) fp)->next = proc_file_chain;
|
|
|
87c1f6 |
- proc_file_chain = (_IO_proc_file *) fp;
|
|
|
87c1f6 |
+ spawn_ok = spawn_process (&fa, fp, command, do_cloexec, pipe_fds,
|
|
|
87c1f6 |
+ parent_end, child_end, child_pipe_fd);
|
|
|
87c1f6 |
#ifdef _IO_MTSAFE_IO
|
|
|
87c1f6 |
_IO_lock_unlock (proc_file_chain_lock);
|
|
|
87c1f6 |
_IO_cleanup_region_end (0);
|
|
|
87c1f6 |
#endif
|
|
|
87c1f6 |
|
|
|
87c1f6 |
+ __posix_spawn_file_actions_destroy (&fa);
|
|
|
87c1f6 |
+
|
|
|
87c1f6 |
+ if (!spawn_ok)
|
|
|
87c1f6 |
+ {
|
|
|
87c1f6 |
+ spawn_failure:
|
|
|
87c1f6 |
+ __close_nocancel (pipe_fds[child_end]);
|
|
|
87c1f6 |
+ __close_nocancel (pipe_fds[parent_end]);
|
|
|
87c1f6 |
+ __set_errno (ENOMEM);
|
|
|
87c1f6 |
+ return NULL;
|
|
|
87c1f6 |
+ }
|
|
|
87c1f6 |
+
|
|
|
87c1f6 |
_IO_mask_flags (fp, read_or_write, _IO_NO_READS|_IO_NO_WRITES);
|
|
|
87c1f6 |
return fp;
|
|
|
87c1f6 |
}
|