diff options
author | Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> | 2017-07-22 19:32:40 +0100 |
---|---|---|
committer | Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> | 2017-10-18 11:44:17 +0100 |
commit | 2fcab33080eaee2f4f7ee329a3769df17bb5ca3f (patch) | |
tree | 3df7773ab159557730b8497a4a1afbe033954244 | |
parent | 317b515c14bcc4830f53c56c444c43122589ebad (diff) | |
download | perl-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.c | 13 | ||||
-rw-r--r-- | t/op/exec.t | 17 |
2 files changed, 24 insertions, 6 deletions
@@ -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"); |