summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDagfinn Ilmari Mannsåker <ilmari@ilmari.org>2017-07-22 19:32:40 +0100
committerDagfinn Ilmari Mannsåker <ilmari@ilmari.org>2017-10-18 11:44:17 +0100
commit2fcab33080eaee2f4f7ee329a3769df17bb5ca3f (patch)
tree3df7773ab159557730b8497a4a1afbe033954244
parent317b515c14bcc4830f53c56c444c43122589ebad (diff)
downloadperl-2fcab33080eaee2f4f7ee329a3769df17bb5ca3f.tar.gz
[perl #131730] Fix exec PROGRAM LIST with empty LIST
This should call execvp() with an empty argv array (containing only the terminating NULL pointer), but was instead just returning false (and not setting $!). Executing a program with an empty argv array is valid ISO C: it merely requires argc to be nonnegative and argv[argc] to be NULL. POSIX states that that argv[0] "should point to a filename string that is associated with the process being started", but "should" only applies to applications claiming strict POSIX conformance. Perl does not, and certainly should not impose it on perl programs. This also requires handling the case where both PROGRAM and LIST are empty, to avoid calling execvp(NULL, …), which _is_ invalid. I've made it return ENOENT, which it the error POSIX specifies for execvp("", …).
-rw-r--r--doio.c13
-rw-r--r--t/op/exec.t17
2 files changed, 24 insertions, 6 deletions
diff --git a/doio.c b/doio.c
index 7b6c43531b..a12f8dc8a9 100644
--- a/doio.c
+++ b/doio.c
@@ -1924,7 +1924,8 @@ Perl_do_aexec5(pTHX_ SV *really, SV **mark, SV **sp,
#if defined(__SYMBIAN32__) || defined(__LIBCATAMOUNT__)
Perl_croak(aTHX_ "exec? I'm not *that* kind of operating system");
#else
- if (sp > mark) {
+ assert(sp >= mark);
+ {
const char **a;
const char *tmps = NULL;
Newx(PL_Argv, sp - mark + 1, const char*);
@@ -1939,17 +1940,19 @@ Perl_do_aexec5(pTHX_ SV *really, SV **mark, SV **sp,
*a = NULL;
if (really)
tmps = SvPV_nolen_const(really);
- if ((!really && *PL_Argv[0] != '/') ||
+ if ((!really && PL_Argv[0] && *PL_Argv[0] != '/') ||
(really && *tmps != '/')) /* will execvp use PATH? */
TAINT_ENV(); /* testing IFS here is overkill, probably */
PERL_FPU_PRE_EXEC
if (really && *tmps) {
PerlProc_execvp(tmps,EXEC_ARGV_CAST(PL_Argv));
- } else {
+ } else if (PL_Argv[0]) {
PerlProc_execvp(PL_Argv[0],EXEC_ARGV_CAST(PL_Argv));
- }
+ } else {
+ SETERRNO(ENOENT,RMS_FNF);
+ }
PERL_FPU_POST_EXEC
- S_exec_failed(aTHX_ (really ? tmps : PL_Argv[0]), fd, do_report);
+ S_exec_failed(aTHX_ (really ? tmps : PL_Argv[0] ? PL_Argv[0] : ""), fd, do_report);
}
do_execfree();
#endif
diff --git a/t/op/exec.t b/t/op/exec.t
index 11554395b9..5a0f7b5601 100644
--- a/t/op/exec.t
+++ b/t/op/exec.t
@@ -36,7 +36,7 @@ $ENV{LANGUAGE} = 'C'; # Ditto in GNU.
my $Is_VMS = $^O eq 'VMS';
my $Is_Win32 = $^O eq 'MSWin32';
-plan(tests => 25);
+plan(tests => 29);
my $Perl = which_perl();
@@ -156,6 +156,21 @@ TODO: {
"exec failure doesn't terminate process");
}
+{
+ local $! = 0;
+ ok !exec(), 'empty exec LIST fails';
+ ok $! == 2 || $! =~ qr/\bno\b.*\bfile\b/i, 'errno = ENOENT'
+ or printf "# \$! eq %d, '%s'\n", $!, $!;
+
+}
+{
+ local $! = 0;
+ my $err = $!;
+ ok !(exec {""} ()), 'empty exec PROGRAM LIST fails';
+ ok $! == 2 || $! =~ qr/\bno\b.*\bfile\b/, 'errno = ENOENT'
+ or printf "# \$! eq %d, '%s'\n", $!, $!;
+}
+
my $test = curr_test();
exec $Perl, '-le', qq{${quote}print 'ok $test - exec PROG, LIST'${quote}};
fail("This should never be reached if the exec() worked");