commit: a3ff1534945c3898332b2481c9fd355dfbd56e1f Author: Mike Frysinger <vapier <AT> gentoo <DOT> org> AuthorDate: Sat Jun 23 18:52:51 2012 +0000 Commit: Mike Frysinger <vapier <AT> gentoo <DOT> org> CommitDate: Sat Jun 23 18:52:51 2012 +0000 URL: https://gitweb.gentoo.org/proj/sandbox.git/commit/?id=a3ff1534
libsandbox: clean up open file handles in parent tracing process Currently, if a non-static app sets up a pipe (with cloexec enabled) and executes a static app, the handle to that pipe is left open in the parent process. This causes trouble when the parent is waiting for that to be closed immediately. Since none of the fds in the forked parent process matter to us, we can just go ahead and clean up all fds before we start tracing the child. URL: http://bugs.gentoo.org/364877 Reported-by: Victor Stinner <victor.stinner <AT> haypocalc.com> Signed-off-by: Mike Frysinger <vapier <AT> gentoo.org> libsandbox/trace.c | 3 +- libsbutil/sb_close.c | 26 +++++++++++- libsbutil/sbutil.h | 1 + tests/Makefile.am | 2 + tests/pipe-fork_static_tst.c | 18 +++++++++ tests/pipe-fork_tst.c | 95 ++++++++++++++++++++++++++++++++++++++++++++ tests/script-9.sh | 5 +++ tests/script.at | 1 + 8 files changed, 149 insertions(+), 2 deletions(-) diff --git a/libsandbox/trace.c b/libsandbox/trace.c index 32ad2d6..dfbab18 100644 --- a/libsandbox/trace.c +++ b/libsandbox/trace.c @@ -504,8 +504,9 @@ void trace_main(const char *filename, char *const argv[]) /* Not all kernel versions support this, so ignore return */ ptrace(PTRACE_SETOPTIONS, trace_pid, NULL, (void *)PTRACE_O_TRACESYSGOOD); #endif + sb_close_all_fds(); trace_loop(); - return; + sb_ebort("ISE: child should have quit, as should we\n"); } sb_debug("child setting up ..."); diff --git a/libsbutil/sb_close.c b/libsbutil/sb_close.c index 17a4560..5379197 100644 --- a/libsbutil/sb_close.c +++ b/libsbutil/sb_close.c @@ -3,7 +3,7 @@ * * IO functions. * - * Copyright 1999-2008 Gentoo Foundation + * Copyright 1999-2012 Gentoo Foundation * Licensed under the GPL-2 */ @@ -29,3 +29,27 @@ int sb_close(int fd) return res; } + +/* Quickly close all the open fds (good for daemonization) */ +void sb_close_all_fds(void) +{ + DIR *dirp; + struct dirent *de; + int dfd, fd; + const char *fd_dir = sb_get_fd_dir(); + + dirp = opendir(fd_dir); + if (!dirp) + sb_ebort("could not process %s\n", fd_dir); + dfd = dirfd(dirp); + + while ((de = readdir(dirp)) != NULL) { + if (de->d_name[0] == '.') + continue; + fd = atoi(de->d_name); + if (fd != dfd) + close(fd); + } + + closedir(dirp); +} diff --git a/libsbutil/sbutil.h b/libsbutil/sbutil.h index 02b88cb..479734b 100644 --- a/libsbutil/sbutil.h +++ b/libsbutil/sbutil.h @@ -97,6 +97,7 @@ int sb_open(const char *path, int flags, mode_t mode); size_t sb_read(int fd, void *buf, size_t count); size_t sb_write(int fd, const void *buf, size_t count); int sb_close(int fd); +void sb_close_all_fds(void); int sb_copy_file_to_fd(const char *file, int ofd); /* Reliable output */ diff --git a/tests/Makefile.am b/tests/Makefile.am index cd8f9c2..1d32e2e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -72,6 +72,8 @@ check_PROGRAMS = \ \ getcwd-gnulib_tst \ libsigsegv_tst \ + pipe-fork_tst \ + pipe-fork_static_tst \ sb_printf_tst \ sigsuspend-zsh_tst \ sigsuspend-zsh_static_tst diff --git a/tests/pipe-fork_static_tst.c b/tests/pipe-fork_static_tst.c new file mode 100644 index 0000000..3f4839e --- /dev/null +++ b/tests/pipe-fork_static_tst.c @@ -0,0 +1,18 @@ +/* +https://bugs.gentoo.org/364877 +written by Victor Stinner <[email protected]> +*/ + +#include "headers.h" + +int main(int argc, char *argv[]) +{ + const size_t n = 1024* 1024; + char *data; + alarm(10); + data = malloc(n); + memset(data, 'a', n); + if (write(1, data, n)) {} + free(data); + return 0; +} diff --git a/tests/pipe-fork_tst.c b/tests/pipe-fork_tst.c new file mode 100644 index 0000000..72669bf --- /dev/null +++ b/tests/pipe-fork_tst.c @@ -0,0 +1,95 @@ +/* +https://bugs.gentoo.org/364877 +written by Victor Stinner <[email protected]> +*/ + +#include "headers.h" + +static void cloexec(int fd) +{ + int flags; + + flags = fcntl(fd, F_GETFD); + if (flags == -1) { + perror("fcntl(F_GETFD)"); + exit(1); + } + fcntl(fd, F_SETFD, flags | FD_CLOEXEC); +} + +int main(int argc, char *argv[]) +{ + int err; + pid_t child; + int outpipe[2]; + int errpipe[2]; + ssize_t n; + char buffer[4096]; + char *dir = dirname(argv[0]); + char *argv0 = "pipe-fork_static_tst"; + char *child_argv[] = {argv0, NULL}; + + if (dir) + if (chdir(dir)) {} + + err = pipe(outpipe); + if (err) { + perror("open"); + return 1; + } + + err = pipe(errpipe); + if (err) { + perror("open"); + return 1; + } + cloexec(errpipe[0]); + cloexec(errpipe[1]); + + child = fork(); + if (child < 0) { + perror("fork"); + return 1; + } + if (child == 0) { + close(outpipe[0]); + close(errpipe[0]); + + dup2(outpipe[1], 1); + execvp(argv0, child_argv); + execv(argv0, child_argv); + + perror("execvp"); + return 1; + } else { + close(outpipe[1]); + close(errpipe[1]); + + printf("wait errpipe..."); fflush(stdout); + n = read(errpipe[0], buffer, sizeof(buffer)); + if (n < 0) { + perror("fcntl(F_GETFD)"); + return 1; + } + printf(" done "); + + while (1) { + n = read(outpipe[0], buffer, sizeof(buffer)); + if (n < 0) { + perror("fcntl(F_GETFD)"); + return 1; + } + if (n == 0) + break; + } + + int status; + waitpid(child, &status, 0); + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + printf("OK!"); + return 0; + } + printf("\nfailed! child status: %#x\n", status); + return 1; + } +} diff --git a/tests/script-9.sh b/tests/script-9.sh new file mode 100755 index 0000000..dc21e64 --- /dev/null +++ b/tests/script-9.sh @@ -0,0 +1,5 @@ +#!/bin/sh +# verify tracing static apps with pipes #364877 +[ "${at_xfail}" = "yes" ] && exit 77 # see script-0 + +pipe-fork_tst diff --git a/tests/script.at b/tests/script.at index 9e8e900..b095ce1 100644 --- a/tests/script.at +++ b/tests/script.at @@ -6,3 +6,4 @@ SB_CHECK(5) SB_CHECK(6,,,8) SB_CHECK(7) SB_CHECK(8) +SB_CHECK(9, [wait errpipe... done OK!])
