From 1c5441c26aa476ffb7ee2a83cea9d73817f83292 Mon Sep 17 00:00:00 2001 From: Simon Ruderich Date: Sun, 2 Jun 2013 18:25:49 +0200 Subject: [PATCH] Hook vfork() and replace it with fork(). --- .gitignore | 2 ++ src/coloredstderr.c | 18 +++++++++++++++ tests/Makefile.am | 4 +++- tests/example_vfork.c | 43 ++++++++++++++++++++++++++++++++++++ tests/example_vfork.expected | 3 +++ tests/run.sh | 1 + 6 files changed, 70 insertions(+), 1 deletion(-) create mode 100644 tests/example_vfork.c create mode 100644 tests/example_vfork.expected diff --git a/.gitignore b/.gitignore index 0148b42..bab1c5a 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,8 @@ /tests/Makefile.in /tests/example /tests/example.o +/tests/example_vfork +/tests/example_vfork.o /tests/run.sh.log /tests/run.sh.trs /tests/test-suite.log diff --git a/src/coloredstderr.c b/src/coloredstderr.c index b16633f..9ade423 100644 --- a/src/coloredstderr.c +++ b/src/coloredstderr.c @@ -326,3 +326,21 @@ int fclose(FILE *fp) { close_fd(fileno(fp)); return real_fclose(fp); } + + +/* Hook functions which are necessary for correct tracking. */ + +pid_t vfork(void) { + /* vfork() is similar to fork() but the address space is shared between + * father and child. It's designed for fork()/exec() usage because it's + * faster than fork(). However according to the POSIX standard the "child" + * isn't allowed to perform any memory-modifications before the exec() + * (except the pid_t result variable of vfork()). + * + * As some programs don't adhere to the standard (e.g. the "child" closes + * or dups a descriptor before the exec()) and this breaks our tracking of + * file descriptors (e.g. it gets closed in the parent as well), we just + * fork() instead. This is in compliance with the POSIX standard and as + * most systems use copy-on-write anyway not a performance issue. */ + return fork(); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 9928cdc..bfaaea2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,9 +1,11 @@ TESTS = run.sh -check_PROGRAMS = example +check_PROGRAMS = example example_vfork example_SOURCES = example.c +example_vfork_SOURCES = example_vfork.c dist_check_SCRIPTS = run.sh lib.sh dist_check_DATA = example.expected \ + example_vfork.expected \ example-noforce.sh \ example-noforce.sh.expected \ example-redirects.sh \ diff --git a/tests/example_vfork.c b/tests/example_vfork.c new file mode 100644 index 0000000..35ceaf4 --- /dev/null +++ b/tests/example_vfork.c @@ -0,0 +1,43 @@ +/* + * Test issues with vfork(). + * + * Copyright (C) 2013 Simon Ruderich + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +int main(int argc, char **argv) { + pid_t pid; + + fprintf(stderr, "Before vfork().\n"); + + pid = vfork(); + if (pid == 0) { + /* This violates the POSIX standard! The "child" is only allowed to + * modify the result of vfork(), e.g. the pid variable. Some programs + * (e.g. gdb) do it anyway so we have to workaround it. */ + dup2(STDOUT_FILENO, STDERR_FILENO); + + _exit(2); + } + + fprintf(stderr, "After vfork().\n"); + puts(""); + + return EXIT_SUCCESS; +} diff --git a/tests/example_vfork.expected b/tests/example_vfork.expected new file mode 100644 index 0000000..0be6ad8 --- /dev/null +++ b/tests/example_vfork.expected @@ -0,0 +1,3 @@ +>stderr>Before vfork(). +stderr>After vfork(). +