From 18c4b508ef24596470b64ddf058c0115ac75fbc2 Mon Sep 17 00:00:00 2001 From: Paul Smith Date: Sat, 15 Oct 2022 16:34:54 -0400 Subject: [PATCH] [SV 63157] Ensure temporary files are removed when signaled Original patch from Dmitry Goncharov . When handling a fatal signal ensure the temporary files for stdin and the jobserver fifo (if in use) are deleted. * src/makeint.h (temp_stdin_unlink): Declare a new method. * src/main.c (temp_stdin_unlink): Delete the stdin temporary file if it exists. If the unlink fails and we're not handling a signal then show an error. (main): Call temp_stdin_unlink() instead of unlinking by hand. * src/commands.c (fatal_error_signal): Invoke cleanup methods if we're handling a fatal signal. * tests/scripts/features/output-sync: Test signal handling during output sync and jobserver with FIFO. * tests/scripts/features/temp_stdin: Test signal handling when makefiles are read from stdin. --- src/commands.c | 5 ++++ src/main.c | 39 +++++++++++++++--------------- src/makeint.h | 1 + tests/scripts/features/output-sync | 23 ++++++++++++++++++ tests/scripts/features/temp_stdin | 29 ++++++++++++++++++++++ 5 files changed, 78 insertions(+), 19 deletions(-) diff --git a/src/commands.c b/src/commands.c index 13d98ff9..4b8fda51 100644 --- a/src/commands.c +++ b/src/commands.c @@ -16,6 +16,7 @@ this program. If not, see . */ #include "makeint.h" #include "filedef.h" +#include "os.h" #include "dep.h" #include "variable.h" #include "job.h" @@ -532,6 +533,10 @@ fatal_error_signal (int sig) It is blocked now while we run this handler. */ signal (sig, SIG_DFL); + temp_stdin_unlink (); + osync_clear (); + jobserver_clear (); + /* A termination signal won't be sent to the entire process group, but it means we want to kill the children. */ diff --git a/src/main.c b/src/main.c index afca0656..05b11c63 100644 --- a/src/main.c +++ b/src/main.c @@ -1137,6 +1137,23 @@ reset_jobserver (void) jobserver_auth = NULL; } +void +temp_stdin_unlink () +{ + /* This function is called from a signal handler. Keep async-signal-safe. + If there is a temp file from reading from stdin, get rid of it. */ + if (stdin_offset >= 0) + { + const char *nm = makefiles->list[stdin_offset]; + int r = 0; + + stdin_offset = -1; + EINTRLOOP(r, unlink (nm)); + if (r < 0 && errno != ENOENT && !handling_fatal_signal) + perror_with_name (_("unlink (temporary file): "), nm); + } +} + #ifdef _AMIGA int main (int argc, char **argv) @@ -2776,9 +2793,7 @@ main (int argc, char **argv, char **envp) #endif jobserver_post_child(1); - /* Get rid of any stdin temp file. */ - if (stdin_offset >= 0) - unlink (makefiles->list[stdin_offset]); + temp_stdin_unlink (); _exit (127); } @@ -2804,15 +2819,7 @@ main (int argc, char **argv, char **envp) } } - /* If there is a temp file from reading a makefile from stdin, get rid of - it now. */ - if (stdin_offset >= 0) - { - const char *nm = makefiles->list[stdin_offset]; - if (unlink (nm) < 0 && errno != ENOENT) - perror_with_name (_("unlink (temporary file): "), nm); - stdin_offset = -1; - } + temp_stdin_unlink (); /* If there were no command-line goals, use the default. */ if (goals == 0) @@ -3731,13 +3738,7 @@ die (int status) print_version (); /* Get rid of a temp file from reading a makefile from stdin. */ - if (stdin_offset >= 0) - { - const char *nm = makefiles->list[stdin_offset]; - if (unlink (nm) < 0 && errno != ENOENT) - perror_with_name (_("unlink (temporary file): "), nm); - stdin_offset = -1; - } + temp_stdin_unlink (); /* Wait for children to die. */ err = (status != 0); diff --git a/src/makeint.h b/src/makeint.h index 2da2a6fd..89a5f770 100644 --- a/src/makeint.h +++ b/src/makeint.h @@ -545,6 +545,7 @@ void out_of_memory () NORETURN; (_f), (_n), (_s)) void decode_env_switches (const char*, size_t line); +void temp_stdin_unlink (void); void die (int) NORETURN; void pfatal_with_name (const char *) NORETURN; void perror_with_name (const char *, const char *); diff --git a/tests/scripts/features/output-sync b/tests/scripts/features/output-sync index 1d09174f..292ef8ff 100644 --- a/tests/scripts/features/output-sync +++ b/tests/scripts/features/output-sync @@ -338,5 +338,28 @@ all:: ; @./foo bar baz '-O', "#MAKE#: ./foo: $ERR_no_such_file\n#MAKE#: *** [#MAKEFILE#:2: all] Error 127\n", 512); } +if ($port_type eq 'UNIX') { +# POSIX doesn't require sh to set PPID so test this +my $cmd = create_command(); +add_options($cmd, '-f', '/dev/null', '-E', q!all:;@echo $$PPID!); +my $fout = 'ppidtest.out'; +run_command_with_output($fout, @$cmd); +$_ = read_file_into_string($fout); +chomp($_); +if (/^[0-9]+$/) { +use POSIX (); +# SV 63157. +# Test that make removes temporary files, even when a signal is received. +# The general test_driver postprocessing will ensure the temporary file used +# to synchronize output and the jobserver fifo are both removed. +run_make_test(q! +pid:=$(shell echo $$PPID) +all:; @kill -TERM $(pid) +!, '-O -j2', "", POSIX::SIGTERM); +} + +unlink($fout); +} + # This tells the test driver that the perl test script executed properly. 1; diff --git a/tests/scripts/features/temp_stdin b/tests/scripts/features/temp_stdin index 8c7a47a0..32a68731 100644 --- a/tests/scripts/features/temp_stdin +++ b/tests/scripts/features/temp_stdin @@ -46,6 +46,35 @@ force: '-R --debug=b -f-', "/Re-executing.+?--temp-stdin=\Q$temppath\E/"); if ($port_type eq 'UNIX') { +# POSIX doesn't require sh to set PPID so test this +my $cmd = create_command(); +add_options($cmd, '-f', '/dev/null', '-E', q!all:;@echo $$PPID!); +my $fout = 'ppidtest.out'; +run_command_with_output($fout, @$cmd); +$_ = read_file_into_string($fout); +chomp($_); +if (/^[0-9]+$/) { +use POSIX (); + +# sv 63157. +# Test that make removes the temporary file which holds make code from stdin, +# even when a signal is received. +# include bye.mk and bye.mk: rule is needed to cause make to keep the temporary +# file for re-exec. Without re-exec make will remove the file before the signal +# arrives. +&utouch(-600, 'bye.mk'); +close(STDIN); +open(STDIN, "<", 'input.mk') || die "$0: cannot open input.mk for reading: $!"; +run_make_test(q! +include bye.mk +pid:=$(shell echo $$PPID) +all:; +bye.mk: force; @kill -TERM $(pid) +force: +!, '-f-', "", POSIX::SIGTERM); +} +unlink($fout); + # sv 62118,62145. # Test that a stdin temp file is removed, when execvp fails to re-exec make. # In order to cause execvp to fail, copy the tested make binary to the temp