This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Also work around renameat() kernel bug on GNU/kFreeBSD
[perl5.git] / doio.c
diff --git a/doio.c b/doio.c
index 9f76c10..8d9131c 100644 (file)
--- a/doio.c
+++ b/doio.c
 
 #include <signal.h>
 
+void
+Perl_setfd_cloexec(int fd)
+{
+    assert(fd >= 0);
+#if defined(HAS_FCNTL) && defined(F_SETFD) && defined(FD_CLOEXEC)
+    (void) fcntl(fd, F_SETFD, FD_CLOEXEC);
+#endif
+}
+
+void
+Perl_setfd_inhexec(int fd)
+{
+    assert(fd >= 0);
+#if defined(HAS_FCNTL) && defined(F_SETFD) && defined(FD_CLOEXEC)
+    (void) fcntl(fd, F_SETFD, 0);
+#endif
+}
+
+void
+Perl_setfd_cloexec_for_nonsysfd(pTHX_ int fd)
+{
+    assert(fd >= 0);
+    if(fd > PL_maxsysfd)
+       setfd_cloexec(fd);
+}
+
+void
+Perl_setfd_inhexec_for_sysfd(pTHX_ int fd)
+{
+    assert(fd >= 0);
+    if(fd <= PL_maxsysfd)
+       setfd_inhexec(fd);
+}
+void
+Perl_setfd_cloexec_or_inhexec_by_sysfdness(pTHX_ int fd)
+{
+    assert(fd >= 0);
+    if(fd <= PL_maxsysfd)
+       setfd_inhexec(fd);
+    else
+       setfd_cloexec(fd);
+}
+
+
+#define DO_GENOPEN_THEN_CLOEXEC(GENOPEN_NORMAL, GENSETFD_CLOEXEC) \
+       do { \
+           int res = (GENOPEN_NORMAL); \
+           if(LIKELY(res != -1)) GENSETFD_CLOEXEC; \
+           return res; \
+       } while(0)
+#if defined(HAS_FCNTL) && defined(F_SETFD) && defined(FD_CLOEXEC) && \
+                       defined(F_GETFD)
+enum { CLOEXEC_EXPERIMENT, CLOEXEC_AT_OPEN, CLOEXEC_AFTER_OPEN };
+#  define DO_GENOPEN_EXPERIMENTING_CLOEXEC(TESTFD, GENOPEN_CLOEXEC, \
+                       GENOPEN_NORMAL, GENSETFD_CLOEXEC) \
+       do { \
+           static int strategy = CLOEXEC_EXPERIMENT; \
+           switch (strategy) { \
+               case CLOEXEC_EXPERIMENT: default: { \
+                   int res = (GENOPEN_CLOEXEC), eno; \
+                   if (LIKELY(res != -1)) { \
+                       int fdflags = fcntl((TESTFD), F_GETFD); \
+                       if (LIKELY(fdflags != -1) && \
+                               LIKELY(fdflags & FD_CLOEXEC)) { \
+                           strategy = CLOEXEC_AT_OPEN; \
+                       } else { \
+                           strategy = CLOEXEC_AFTER_OPEN; \
+                           GENSETFD_CLOEXEC; \
+                       } \
+                   } else if (UNLIKELY((eno = errno) == EINVAL || \
+                                               eno == ENOSYS)) { \
+                       res = (GENOPEN_NORMAL); \
+                       if (LIKELY(res != -1)) { \
+                           strategy = CLOEXEC_AFTER_OPEN; \
+                           GENSETFD_CLOEXEC; \
+                       } else if (!LIKELY((eno = errno) == EINVAL || \
+                                               eno == ENOSYS)) { \
+                           strategy = CLOEXEC_AFTER_OPEN; \
+                       } \
+                   } \
+                   return res; \
+               } \
+               case CLOEXEC_AT_OPEN: \
+                   return (GENOPEN_CLOEXEC); \
+               case CLOEXEC_AFTER_OPEN: \
+                   DO_GENOPEN_THEN_CLOEXEC(GENOPEN_NORMAL, GENSETFD_CLOEXEC); \
+           } \
+       } while(0)
+#else
+#  define DO_GENOPEN_EXPERIMENTING_CLOEXEC(TESTFD, GENOPEN_CLOEXEC, \
+                       GENOPEN_NORMAL, GENSETFD_CLOEXEC) \
+       DO_GENOPEN_THEN_CLOEXEC(GENOPEN_NORMAL, GENSETFD_CLOEXEC)
+#endif
+
+#define DO_ONEOPEN_THEN_CLOEXEC(ONEOPEN_NORMAL) \
+       do { \
+           int fd; \
+           DO_GENOPEN_THEN_CLOEXEC(fd = (ONEOPEN_NORMAL), \
+               setfd_cloexec(fd)); \
+       } while(0)
+#define DO_ONEOPEN_EXPERIMENTING_CLOEXEC(ONEOPEN_CLOEXEC, ONEOPEN_NORMAL) \
+       do { \
+           int fd; \
+           DO_GENOPEN_EXPERIMENTING_CLOEXEC(fd, fd = (ONEOPEN_CLOEXEC), \
+               fd = (ONEOPEN_NORMAL), setfd_cloexec(fd)); \
+       } while(0)
+
+#define DO_PIPESETFD_CLOEXEC(PIPEFD) \
+       do { \
+           setfd_cloexec((PIPEFD)[0]); \
+           setfd_cloexec((PIPEFD)[1]); \
+       } while(0)
+#define DO_PIPEOPEN_THEN_CLOEXEC(PIPEFD, PIPEOPEN_NORMAL) \
+       DO_GENOPEN_THEN_CLOEXEC(PIPEOPEN_NORMAL, DO_PIPESETFD_CLOEXEC(PIPEFD))
+#define DO_PIPEOPEN_EXPERIMENTING_CLOEXEC(PIPEFD, PIPEOPEN_CLOEXEC, \
+                       PIPEOPEN_NORMAL) \
+       DO_GENOPEN_EXPERIMENTING_CLOEXEC((PIPEFD)[0], PIPEOPEN_CLOEXEC, \
+           PIPEOPEN_NORMAL, DO_PIPESETFD_CLOEXEC(PIPEFD))
+
+int
+Perl_PerlLIO_dup_cloexec(pTHX_ int oldfd)
+{
+#if !defined(PERL_IMPLICIT_SYS) && defined(F_DUPFD_CLOEXEC)
+    /*
+     * struct IPerlLIO doesn't cover fcntl(), and there's no clear way
+     * to extend it, so for the time being this just isn't available on
+     * PERL_IMPLICIT_SYS builds.
+     */
+    DO_ONEOPEN_EXPERIMENTING_CLOEXEC(
+       fcntl(oldfd, F_DUPFD_CLOEXEC, 0),
+       PerlLIO_dup(oldfd));
+#else
+    DO_ONEOPEN_THEN_CLOEXEC(PerlLIO_dup(oldfd));
+#endif
+}
+
+int
+Perl_PerlLIO_dup2_cloexec(pTHX_ int oldfd, int newfd)
+{
+#if !defined(PERL_IMPLICIT_SYS) && defined(HAS_DUP3) && defined(O_CLOEXEC)
+    /*
+     * struct IPerlLIO doesn't cover dup3(), and there's no clear way
+     * to extend it, so for the time being this just isn't available on
+     * PERL_IMPLICIT_SYS builds.
+     */
+    DO_ONEOPEN_EXPERIMENTING_CLOEXEC(
+       dup3(oldfd, newfd, O_CLOEXEC),
+       PerlLIO_dup2(oldfd, newfd));
+#else
+    DO_ONEOPEN_THEN_CLOEXEC(PerlLIO_dup2(oldfd, newfd));
+#endif
+}
+
+int
+Perl_PerlLIO_open_cloexec(pTHX_ const char *file, int flag)
+{
+    PERL_ARGS_ASSERT_PERLLIO_OPEN_CLOEXEC;
+#if defined(O_CLOEXEC)
+    DO_ONEOPEN_EXPERIMENTING_CLOEXEC(
+       PerlLIO_open(file, flag | O_CLOEXEC),
+       PerlLIO_open(file, flag));
+#else
+    DO_ONEOPEN_THEN_CLOEXEC(PerlLIO_open(file, flag));
+#endif
+}
+
+int
+Perl_PerlLIO_open3_cloexec(pTHX_ const char *file, int flag, int perm)
+{
+    PERL_ARGS_ASSERT_PERLLIO_OPEN3_CLOEXEC;
+#if defined(O_CLOEXEC)
+    DO_ONEOPEN_EXPERIMENTING_CLOEXEC(
+       PerlLIO_open3(file, flag | O_CLOEXEC, perm),
+       PerlLIO_open3(file, flag, perm));
+#else
+    DO_ONEOPEN_THEN_CLOEXEC(PerlLIO_open3(file, flag, perm));
+#endif
+}
+
+int
+Perl_my_mkstemp_cloexec(char *templte)
+{
+    PERL_ARGS_ASSERT_MY_MKSTEMP_CLOEXEC;
+#if defined(O_CLOEXEC)
+    DO_ONEOPEN_EXPERIMENTING_CLOEXEC(
+       Perl_my_mkostemp(templte, O_CLOEXEC),
+       Perl_my_mkstemp(templte));
+#else
+    DO_ONEOPEN_THEN_CLOEXEC(Perl_my_mkstemp(templte));
+#endif
+}
+
+#ifdef HAS_PIPE
+int
+Perl_PerlProc_pipe_cloexec(pTHX_ int *pipefd)
+{
+    PERL_ARGS_ASSERT_PERLPROC_PIPE_CLOEXEC;
+    /*
+     * struct IPerlProc doesn't cover pipe2(), and there's no clear way
+     * to extend it, so for the time being this just isn't available on
+     * PERL_IMPLICIT_SYS builds.
+     */
+#  if !defined(PERL_IMPLICIT_SYS) && defined(HAS_PIPE2) && defined(O_CLOEXEC)
+    DO_PIPEOPEN_EXPERIMENTING_CLOEXEC(pipefd,
+       pipe2(pipefd, O_CLOEXEC),
+       PerlProc_pipe(pipefd));
+#  else
+    DO_PIPEOPEN_THEN_CLOEXEC(pipefd, PerlProc_pipe(pipefd));
+#  endif
+}
+#endif
+
+#ifdef HAS_SOCKET
+
+int
+Perl_PerlSock_socket_cloexec(pTHX_ int domain, int type, int protocol)
+{
+#  if defined(SOCK_CLOEXEC)
+    DO_ONEOPEN_EXPERIMENTING_CLOEXEC(
+       PerlSock_socket(domain, type | SOCK_CLOEXEC, protocol),
+       PerlSock_socket(domain, type, protocol));
+#  else
+    DO_ONEOPEN_THEN_CLOEXEC(PerlSock_socket(domain, type, protocol));
+#  endif
+}
+
+int
+Perl_PerlSock_accept_cloexec(pTHX_ int listenfd, struct sockaddr *addr,
+    Sock_size_t *addrlen)
+{
+#  if !defined(PERL_IMPLICIT_SYS) && \
+       defined(HAS_ACCEPT4) && defined(SOCK_CLOEXEC)
+    /*
+     * struct IPerlSock doesn't cover accept4(), and there's no clear
+     * way to extend it, so for the time being this just isn't available
+     * on PERL_IMPLICIT_SYS builds.
+     */
+    DO_ONEOPEN_EXPERIMENTING_CLOEXEC(
+       accept4(listenfd, addr, addrlen, SOCK_CLOEXEC),
+       PerlSock_accept(listenfd, addr, addrlen));
+#  else
+    DO_ONEOPEN_THEN_CLOEXEC(PerlSock_accept(listenfd, addr, addrlen));
+#  endif
+}
+
+#endif
+
+#if defined (HAS_SOCKETPAIR) || \
+    (defined (HAS_SOCKET) && defined(SOCK_DGRAM) && \
+       defined(AF_INET) && defined(PF_INET))
+int
+Perl_PerlSock_socketpair_cloexec(pTHX_ int domain, int type, int protocol,
+    int *pairfd)
+{
+    PERL_ARGS_ASSERT_PERLSOCK_SOCKETPAIR_CLOEXEC;
+#  ifdef SOCK_CLOEXEC
+    DO_PIPEOPEN_EXPERIMENTING_CLOEXEC(pairfd,
+       PerlSock_socketpair(domain, type | SOCK_CLOEXEC, protocol, pairfd),
+       PerlSock_socketpair(domain, type, protocol, pairfd));
+#  else
+    DO_PIPEOPEN_THEN_CLOEXEC(pairfd,
+       PerlSock_socketpair(domain, type, protocol, pairfd));
+#  endif
+}
+#endif
+
 static IO *
 S_openn_setup(pTHX_ GV *gv, char *mode, PerlIO **saveifp, PerlIO **saveofp,
               int *savefd,  char *savetype)
@@ -452,7 +718,7 @@ Perl_do_open6(pTHX_ GV *gv, const char *oname, STRLEN len,
                    }
                    else {
                        if (dodup)
-                            wanted_fd = PerlLIO_dup(wanted_fd);
+                            wanted_fd = PerlLIO_dup_cloexec(wanted_fd);
                        else
                            was_fdopen = TRUE;
                         if (!(fp = PerlIO_openn(aTHX_ type,mode,wanted_fd,0,0,NULL,num_svs,svp))) {
@@ -629,9 +895,9 @@ S_openn_cleanup(pTHX_ GV *gv, IO *io, PerlIO *fp, char *mode, const char *oname,
            
        )
         {
-            GCC_DIAG_IGNORE(-Wformat-nonliteral); /* PL_warn_nl is constant */
+            GCC_DIAG_IGNORE_STMT(-Wformat-nonliteral); /* PL_warn_nl is constant */
            Perl_warner(aTHX_ packWARN(WARN_NEWLINE), PL_warn_nl, "open");
-            GCC_DIAG_RESTORE;
+            GCC_DIAG_RESTORE_STMT;
         }
        goto say_false;
     }
@@ -743,33 +1009,15 @@ S_openn_cleanup(pTHX_ GV *gv, IO *io, PerlIO *fp, char *mode, const char *oname,
            if (was_fdopen) {
                 /* need to close fp without closing underlying fd */
                 int ofd = PerlIO_fileno(fp);
-                int dupfd = ofd >= 0 ? PerlLIO_dup(ofd) : -1;
-#if defined(HAS_FCNTL) && defined(F_SETFD)
-               /* Assume if we have F_SETFD we have F_GETFD. */
-                /* Get a copy of all the fd flags. */
-                int fd_flags = ofd >= 0 ? fcntl(ofd, F_GETFD) : -1;
-                if (fd_flags < 0) {
-                    if (dupfd >= 0)
-                        PerlLIO_close(dupfd);
-                    goto say_false;
-                }
-#endif
+                int dupfd = ofd >= 0 ? PerlLIO_dup_cloexec(ofd) : -1;
                 if (ofd < 0 || dupfd < 0) {
                     if (dupfd >= 0)
                         PerlLIO_close(dupfd);
                     goto say_false;
                 }
                 PerlIO_close(fp);
-                PerlLIO_dup2(dupfd, ofd);
-#if defined(HAS_FCNTL) && defined(F_SETFD)
-               /* The dup trick has lost close-on-exec on ofd,
-                 * and possibly any other flags, so restore them. */
-               if (fcntl(ofd,F_SETFD, fd_flags) < 0) {
-                    if (dupfd >= 0)
-                        PerlLIO_close(dupfd);
-                    goto say_false;
-                }
-#endif
+                PerlLIO_dup2_cloexec(dupfd, ofd);
+                setfd_inhexec_for_sysfd(ofd);
                 PerlLIO_close(dupfd);
            }
             else
@@ -779,12 +1027,6 @@ S_openn_cleanup(pTHX_ GV *gv, IO *io, PerlIO *fp, char *mode, const char *oname,
        PerlIO_clearerr(fp);
        fd = PerlIO_fileno(fp);
     }
-#if defined(HAS_FCNTL) && defined(F_SETFD) && defined(FD_CLOEXEC)
-    if (fd >= 0 && fd > PL_maxsysfd && fcntl(fd, F_SETFD, FD_CLOEXEC) < 0) {
-        PerlLIO_close(fd);
-        goto say_false;
-    }
-#endif
     IoIFP(io) = fp;
 
     IoFLAGS(io) &= ~IOf_NOLINE;
@@ -854,7 +1096,7 @@ S_openindirtemp(pTHX_ GV *gv, SV *orig_name, SV *temp_out_name) {
 
     {
       int old_umask = umask(0177);
-      fd = Perl_my_mkstemp(SvPVX(temp_out_name));
+      fd = Perl_my_mkstemp_cloexec(SvPVX(temp_out_name));
       umask(old_umask);
     }
 
@@ -921,44 +1163,50 @@ S_argvout_free(pTHX_ SV *io, MAGIC *mg) {
 
     /* mg_obj can be NULL if a thread is created with the handle open, in which
      case we leave any clean up to the parent thread */
-    if (mg->mg_obj && IoIFP(io)) {
-        SV **pid_psv;
+    if (mg->mg_obj) {
 #ifdef ARGV_USE_ATFUNCTIONS
         SV **dir_psv;
         DIR *dir;
+
+        dir_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_DIRP, FALSE);
+        assert(dir_psv && *dir_psv && SvIOK(*dir_psv));
+        dir = INT2PTR(DIR *, SvIV(*dir_psv));
 #endif
-        PerlIO *iop = IoIFP(io);
+        if (IoIFP(io)) {
+            SV **pid_psv;
+            PerlIO *iop = IoIFP(io);
 
-        assert(SvTYPE(mg->mg_obj) == SVt_PVAV);
+            assert(SvTYPE(mg->mg_obj) == SVt_PVAV);
 
-        pid_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_PID, FALSE);
+            pid_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_PID, FALSE);
 
-        assert(pid_psv && *pid_psv);
+            assert(pid_psv && *pid_psv);
 
-        if (SvIV(*pid_psv) == (IV)PerlProc_getpid()) {
-            /* if we get here the file hasn't been closed explicitly by the
-               user and hadn't been closed implicitly by nextargv(), so
-               abandon the edit */
-            SV **temp_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_TEMP_NAME, FALSE);
-            const char *temp_pv = SvPVX(*temp_psv);
+            if (SvIV(*pid_psv) == (IV)PerlProc_getpid()) {
+                /* if we get here the file hasn't been closed explicitly by the
+                   user and hadn't been closed implicitly by nextargv(), so
+                   abandon the edit */
+                SV **temp_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_TEMP_NAME, FALSE);
+                const char *temp_pv = SvPVX(*temp_psv);
 
-            assert(temp_psv && *temp_psv && SvPOK(*temp_psv));
-            (void)PerlIO_close(iop);
-            IoIFP(io) = IoOFP(io) = NULL;
+                assert(temp_psv && *temp_psv && SvPOK(*temp_psv));
+                (void)PerlIO_close(iop);
+                IoIFP(io) = IoOFP(io) = NULL;
 #ifdef ARGV_USE_ATFUNCTIONS
-            dir_psv = av_fetch((AV*)mg->mg_obj, ARGVMG_ORIG_DIRP, FALSE);
-            assert(dir_psv && *dir_psv && SvIOK(*dir_psv));
-            dir = INT2PTR(DIR *, SvIV(*dir_psv));
-            if (dir) {
-                if (unlinkat(my_dirfd(dir), temp_pv, 0) < 0 &&
-                    NotSupported(errno))
-                    (void)UNLINK(temp_pv);
-                closedir(dir);
-            }
+                if (dir) {
+                    if (unlinkat(my_dirfd(dir), temp_pv, 0) < 0 &&
+                        NotSupported(errno))
+                        (void)UNLINK(temp_pv);
+                }
 #else
-            (void)UNLINK(temp_pv);
+                (void)UNLINK(temp_pv);
 #endif
+            }
         }
+#ifdef ARGV_USE_ATFUNCTIONS
+        if (dir)
+            closedir(dir);
+#endif
     }
 
     return 0;
@@ -1213,7 +1461,7 @@ Perl_nextargv(pTHX_ GV *gv, bool nomagicopen)
 }
 
 #ifdef ARGV_USE_ATFUNCTIONS
-#  if defined(__FreeBSD__)
+#  if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
 
 /* FreeBSD 11 renameat() mis-behaves strangely with absolute paths in cases where the
  * equivalent rename() succeeds
@@ -1233,7 +1481,7 @@ S_my_renameat(int olddfd, const char *oldpath, int newdfd, const char *newpath)
 
 #  else
 #    define S_my_renameat(dh1, pv1, dh2, pv2) renameat((dh1), (pv1), (dh2), (pv2))
-#  endif /* if defined(__FreeBSD__) */
+#  endif /* if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) */
 #endif
 
 static bool
@@ -1266,7 +1514,7 @@ S_dir_unchanged(pTHX_ const char *orig_pv, MAGIC *mg) {
     */
     if (!PERL_FILE_IS_ABSOLUTE(orig_pv)
         && PerlLIO_stat(SvPVX(*temp_psv), &statbuf) < 0) {
-        Perl_croak(aTHX_ "Cannot complete in-place edit of %" SVf ": %s",
+        Perl_croak(aTHX_ "Cannot complete in-place edit of %s: %s",
                    orig_pv,
                    "Work file is missing - did you change directory?");
     }
@@ -1443,8 +1691,9 @@ Perl_do_close(pTHX_ GV *gv, bool not_implicit)
 #else
                     UNLINK(SvPVX(*temp_psv));
 #endif
-                    Perl_croak(aTHX_ "Can't rename in-place work file '%s' to '%s': %s\n",
-                               SvPVX(*temp_psv), SvPVX(*orig_psv), Strerror(errno));
+                    /* diag_listed_as: Cannot complete in-place edit of %s: %s */
+                    Perl_croak(aTHX_ "Cannot complete in-place edit of %s: failed to rename work file '%s' to '%s': %s",
+                               orig_pv, SvPVX(*temp_psv), orig_pv, Strerror(errno));
                 }
             abort_inplace:
                 UNLINK(SvPVX_const(*temp_psv));
@@ -1887,9 +2136,9 @@ Perl_my_stat_flags(pTHX_ const U32 flags)
             PL_laststatval = PerlLIO_stat(d, &PL_statcache);
         }
        if (PL_laststatval < 0 && ckWARN(WARN_NEWLINE) && should_warn_nl(s)) {
-            GCC_DIAG_IGNORE(-Wformat-nonliteral); /* PL_warn_nl is constant */
+            GCC_DIAG_IGNORE_STMT(-Wformat-nonliteral); /* PL_warn_nl is constant */
            Perl_warner(aTHX_ packWARN(WARN_NEWLINE), PL_warn_nl, "stat");
-            GCC_DIAG_RESTORE;
+            GCC_DIAG_RESTORE_STMT;
         }
        return PL_laststatval;
     }
@@ -1958,9 +2207,9 @@ Perl_my_lstat_flags(pTHX_ const U32 flags)
         PL_laststatval = PerlLIO_lstat(file,&PL_statcache);
     }
     if (PL_laststatval < 0 && ckWARN(WARN_NEWLINE) && should_warn_nl(file)) {
-        GCC_DIAG_IGNORE(-Wformat-nonliteral); /* PL_warn_nl is constant */
+        GCC_DIAG_IGNORE_STMT(-Wformat-nonliteral); /* PL_warn_nl is constant */
         Perl_warner(aTHX_ packWARN(WARN_NEWLINE), PL_warn_nl, "lstat");
-        GCC_DIAG_RESTORE;
+        GCC_DIAG_RESTORE_STMT;
     }
     return PL_laststatval;
 }
@@ -1991,56 +2240,53 @@ Perl_do_aexec5(pTHX_ SV *really, SV **mark, SV **sp,
     Perl_croak(aTHX_ "exec? I'm not *that* kind of operating system");
 #else
     assert(sp >= mark);
+    ENTER;
     {
-       const char **a;
+       const char **argv, **a;
        const char *tmps = NULL;
-       Newx(PL_Argv, sp - mark + 1, const char*);
-       a = PL_Argv;
+       Newx(argv, sp - mark + 1, const char*);
+       SAVEFREEPV(argv);
+       a = argv;
 
        while (++mark <= sp) {
-           if (*mark)
-               *a++ = SvPV_nolen_const(*mark);
-           else
+           if (*mark) {
+               char *arg = savepv(SvPV_nolen_const(*mark));
+               SAVEFREEPV(arg);
+               *a++ = arg;
+           } else
                *a++ = "";
        }
        *a = NULL;
-       if (really)
-           tmps = SvPV_nolen_const(really);
-        if ((!really && PL_Argv[0] && *PL_Argv[0] != '/') ||
+       if (really) {
+           tmps = savepv(SvPV_nolen_const(really));
+           SAVEFREEPV(tmps);
+       }
+        if ((!really && argv[0] && *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 if (PL_Argv[0]) {
-            PerlProc_execvp(PL_Argv[0],EXEC_ARGV_CAST(PL_Argv));
+            PerlProc_execvp(tmps,EXEC_ARGV_CAST(argv));
+        } else if (argv[0]) {
+            PerlProc_execvp(argv[0],EXEC_ARGV_CAST(argv));
         } else {
             SETERRNO(ENOENT,RMS_FNF);
         }
        PERL_FPU_POST_EXEC
-        S_exec_failed(aTHX_ (really ? tmps : PL_Argv[0] ? PL_Argv[0] : ""), fd, do_report);
+        S_exec_failed(aTHX_ (really ? tmps : argv[0] ? argv[0] : ""), fd, do_report);
     }
-    do_execfree();
+    LEAVE;
 #endif
     return FALSE;
 }
 
-void
-Perl_do_execfree(pTHX)
-{
-    Safefree(PL_Argv);
-    PL_Argv = NULL;
-    Safefree(PL_Cmd);
-    PL_Cmd = NULL;
-}
-
 #ifdef PERL_DEFAULT_DO_EXEC3_IMPLEMENTATION
 
 bool
 Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report)
 {
     dVAR;
-    const char **a;
+    const char **argv, **a;
     char *s;
     char *buf;
     char *cmd;
@@ -2049,7 +2295,9 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report)
 
     PERL_ARGS_ASSERT_DO_EXEC3;
 
+    ENTER;
     Newx(buf, cmdlen, char);
+    SAVEFREEPV(buf);
     cmd = buf;
     memcpy(cmd, incmd, cmdlen);
 
@@ -2085,8 +2333,7 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report)
                  PERL_FPU_POST_EXEC
                  *s = '\'';
                  S_exec_failed(aTHX_ PL_cshname, fd, do_report);
-                 Safefree(buf);
-                 return FALSE;
+                 goto leave;
              }
          }
        }
@@ -2133,15 +2380,16 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report)
             PerlProc_execl(PL_sh_path, "sh", "-c", cmd, (char *)NULL);
            PERL_FPU_POST_EXEC
            S_exec_failed(aTHX_ PL_sh_path, fd, do_report);
-           Safefree(buf);
-           return FALSE;
+           goto leave;
        }
     }
 
-    Newx(PL_Argv, (s - cmd) / 2 + 2, const char*);
-    PL_Cmd = savepvn(cmd, s-cmd);
-    a = PL_Argv;
-    for (s = PL_Cmd; *s;) {
+    Newx(argv, (s - cmd) / 2 + 2, const char*);
+    SAVEFREEPV(argv);
+    cmd = savepvn(cmd, s-cmd);
+    SAVEFREEPV(cmd);
+    a = argv;
+    for (s = cmd; *s;) {
        while (isSPACE(*s))
            s++;
        if (*s)
@@ -2152,18 +2400,16 @@ Perl_do_exec3(pTHX_ const char *incmd, int fd, int do_report)
            *s++ = '\0';
     }
     *a = NULL;
-    if (PL_Argv[0]) {
+    if (argv[0]) {
        PERL_FPU_PRE_EXEC
-        PerlProc_execvp(PL_Argv[0],EXEC_ARGV_CAST(PL_Argv));
+        PerlProc_execvp(argv[0],EXEC_ARGV_CAST(argv));
        PERL_FPU_POST_EXEC
-       if (errno == ENOEXEC) {         /* for system V NIH syndrome */
-           do_execfree();
+       if (errno == ENOEXEC)           /* for system V NIH syndrome */
            goto doshell;
-       }
-       S_exec_failed(aTHX_ PL_Argv[0], fd, do_report);
+       S_exec_failed(aTHX_ argv[0], fd, do_report);
     }
-    do_execfree();
-    Safefree(buf);
+leave:
+    LEAVE;
     return FALSE;
 }
 
@@ -2998,24 +3244,24 @@ Perl_vms_start_glob
 #  if defined(OS2)
     sv_setpv(tmpcmd, "for a in ");
     sv_catsv(tmpcmd, tmpglob);
-    sv_catpv(tmpcmd, "; do echo \"$a\\0\\c\"; done |");
+    sv_catpvs(tmpcmd, "; do echo \"$a\\0\\c\"; done |");
 #  elif defined(DJGPP)
     sv_setpv(tmpcmd, "/dev/dosglob/"); /* File System Extension */
     sv_catsv(tmpcmd, tmpglob);
 #  else
     sv_setpv(tmpcmd, "perlglob ");
     sv_catsv(tmpcmd, tmpglob);
-    sv_catpv(tmpcmd, " |");
+    sv_catpvs(tmpcmd, " |");
 #  endif
 # elif defined(CSH)
     sv_setpvn(tmpcmd, PL_cshname, PL_cshlen);
-    sv_catpv(tmpcmd, " -cf 'set nonomatch; glob ");
+    sv_catpvs(tmpcmd, " -cf 'set nonomatch; glob ");
     sv_catsv(tmpcmd, tmpglob);
-    sv_catpv(tmpcmd, "' 2>/dev/null |");
+    sv_catpvs(tmpcmd, "' 2>/dev/null |");
 # else
     sv_setpv(tmpcmd, "echo ");
     sv_catsv(tmpcmd, tmpglob);
-    sv_catpv(tmpcmd, "|tr -s ' \t\f\r' '\\n\\n\\n\\n'|");
+    sv_catpvs(tmpcmd, "|tr -s ' \t\f\r' '\\n\\n\\n\\n'|");
 # endif /* !DOSISH && !CSH */
     {
         SV ** const svp = hv_fetchs(GvHVn(PL_envgv), "LS_COLORS", 0);