[SV 62145] Remove a stdin temp file on re-exec failure.

If the re-exec fails, be sure to remove a temp makefile that was
created to read from stdin.

* src/job.c (exec_command): Return on failure.
(child_execute_job): Call exit if exec_command returns.
* src/job.h (exec_command): Don't mark as NORETURN.
* src/main.c (main): Unlink stdin temporary file if re-exec fails.
* tests/run_make_tests.pl: Get value for ERR_nonexe_file/ERR_exe_dir.
* tests/scripts/features/temp_stdin: Test that temp file unlink works.
This commit is contained in:
Dmitry Goncharov 2022-04-23 15:33:41 -04:00 committed by Paul Smith
parent 416664b1e7
commit 949c0464a9
5 changed files with 118 additions and 21 deletions

View file

@ -2309,6 +2309,7 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv)
/* Run the command. */
exec_command (argv, child->environment);
_exit (127);
#else /* USE_POSIX_SPAWN */
@ -2452,12 +2453,7 @@ child_execute_job (struct childbase *child, int good_stdin, char **argv)
/* Replace the current process with one running the command in ARGV,
with environment ENVP. This function does not return. */
/* EMX: This function returns the pid of the child process. */
# ifdef __EMX__
pid_t
# else
void
# endif
exec_command (char **argv, char **envp)
{
#ifdef VMS
@ -2524,14 +2520,12 @@ exec_command (char **argv, char **envp)
}
}
/* return child's exit code as our exit code */
/* Use the child's exit code as our exit code */
exit (exit_code);
#else /* !WINDOWS32 */
# ifdef __EMX__
pid_t pid;
# endif
pid_t pid = -1;
/* Be the user, permanently. */
child_access ();
@ -2630,11 +2624,7 @@ exec_command (char **argv, char **envp)
break;
}
# ifdef __EMX__
return pid;
# else
_exit (127);
# endif
#endif /* !WINDOWS32 */
#endif /* !VMS */
}

View file

@ -81,10 +81,8 @@ pid_t child_execute_job (struct childbase *child, int good_stdin, char **argv);
#ifdef _AMIGA
void exec_command (char **argv) NORETURN;
#elif defined(__EMX__)
int exec_command (char **argv, char **envp);
#else
void exec_command (char **argv, char **envp) NORETURN;
pid_t exec_command (char **argv, char **envp);
#endif
void unblock_all_sigs (void);

View file

@ -2655,10 +2655,13 @@ main (int argc, char **argv, char **envp)
#endif
exec_command ((char **)nargv, environ);
#endif
/* We shouldn't get here but just in case. */
jobserver_post_child(1);
break;
/* Get rid of any stdin temp file. */
if (stdin_offset >= 0)
unlink (makefiles->list[stdin_offset]);
_exit (127);
}
if (any_failed)

View file

@ -141,14 +141,14 @@ $ERR_command_not_found = undef;
$ERR_read_only_file = "$!";
}
$_ = `./file.out 2>/dev/null`;
$_ = `./file.out 2>&1`;
if ($? == 0) {
print "Executed non-executable file! Skipping related tests.\n";
} else {
$ERR_nonexe_file = "$!";
}
$_ = `./. 2>/dev/null`;
$_ = `./. 2>&1`;
if ($? == 0) {
print "Executed directory! Skipping related tests.\n";
} else {

View file

@ -0,0 +1,106 @@
# -*-mode: perl-*-
$description = "Test handling of temporary file created from stdin.";
use File::Temp qw /tempdir/;
sub check_tempfile
{
my ($tdir) = @_;
my @left = glob $tdir . '/Gm*';
scalar @left == 0 && return;
my $answer = "temporary file $left[0] is left behind\n";
compare_output($answer, &get_logfile(1));
}
create_file('input.mk', "world:=1\n");
create_file('bye.mk', "moon:=2\n");
# sv 62118,62145.
# Test that makes leaves no temp file when make code is piped to stdin and -v,
# -h or an invalid option is specified.
my @opts = ('-v', '-h', '--nosuchopt');
my @exit_codes = (0, 0, 512);
for my $i (0 .. $#opts) {
my $tdir = tempdir(CLEANUP => 1);
$ENV{'TMPDIR'} = $tdir;
$ENV{'TMP'} = $tdir;
close(STDIN);
open(STDIN, "<", 'input.mk') || die "$0: cannot open input.mk for reading: $!";
run_make_test(q!
all:; $(info hello world)
!,
"$opts[$i] -f-", "/uilt for /", $exit_codes[$i]);
check_tempfile($tdir);
}
# sv 62118,62145.
# Test that a stdin temp file is removed.
my $tdir = tempdir(CLEANUP => 1);
$ENV{'TMPDIR'} = $tdir;
$ENV{'TMP'} = $tdir;
close(STDIN);
open(STDIN, "<", 'input.mk') || die "$0: cannot open input.mk for reading: $!";
run_make_test(q!
all:; $(info world=$(world))
!,
'-f-', "world=1\n#MAKE#: 'all' is up to date.\n");
check_tempfile($tdir);
# sv 62118,62145.
# Test that a stdin temp file is removed, even when make re-execs.
# Also test that make nohors TMPDIR to create the temp file.
my $tdir = tempdir(CLEANUP => 1);
$ENV{'TMPDIR'} = $tdir;
$ENV{'TMP'} = $tdir;
# Ensure touching bye.mk causes re-exec.
&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
all:; $(info hello)
$(MAKE_RESTARTS)bye.mk: force; touch $@
force:
!,
'-R --debug=b -f-', "/Re-executing.+?--temp-stdin=\Q$tdir\E/");
check_tempfile($tdir);
if ($port_type eq 'UNIX') {
# 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
# directory and take away the 'x' bit.
use File::Copy;
my $tdir = tempdir(CLEANUP => 1);
$ENV{'TMPDIR'} = $tdir;
$ENV{'TMP'} = $tdir;
my $makecopy = "$tdir/make";
copy("$mkpath", $makecopy);
# Set file mode bits, because perl copy won't.
chmod 0750, $makecopy;
my @make_orig = @make_command;
@make_command = ($makecopy);
# Ensure touching bye.mk causes re-exec.
&utouch(-600, 'bye.mk');
close(STDIN);
open(STDIN, "<", 'input.mk') || die "$0: cannot open input.mk for reading: $!";
run_make_test("
include bye.mk
all:; \$(info hello)
\$(MAKE_RESTARTS)bye.mk: force; touch \$@ && chmod -x $makecopy
force:
",
"-f-", "touch bye.mk && chmod -x $makecopy\nmake: $makecopy: $ERR_nonexe_file\n", 32512);
check_tempfile($tdir);
@make_command = @make_orig;
}
unlink('input.mk', 'bye.mk');
# This tells the test driver that the perl test script executed properly.
1;