X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=perl.c;h=a308cf358078d4b6de0aff2ef6c05e4a82bb2bbc;hb=b9ba2fadb18b54e35e5de54f945111a56cbcb249;hp=1abb48dbcfc70e181e471825ea6f45426102a929;hpb=0544e6df68c5534300178fdd73628d687be1a6b2;p=p5sagit%2Fp5-mst-13.2.git diff --git a/perl.c b/perl.c index 1abb48d..a308cf3 100644 --- a/perl.c +++ b/perl.c @@ -1,7 +1,7 @@ /* perl.c * - * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, - * 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 by Larry Wall and others + * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 + * 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 by Larry Wall and others * * You may distribute under the terms of either the GNU General Public * License or the Artistic License, as specified in the README file. @@ -9,7 +9,11 @@ */ /* - * "A ship then new they built for him/of mithril and of elven glass" --Bilbo + * A ship then new they built for him + * of mithril and of elven-glass + * --from Bilbo's song of EƤrendil + * + * [p.236 of _The Lord of the Rings_, II/i: "Many Meetings"] */ /* This file contains the top-level functions that are used to create, use @@ -18,65 +22,6 @@ * function of the interpreter; that can be found in perlmain.c */ -/* PSz 12 Nov 03 - * - * Be proud that perl(1) may proclaim: - * Setuid Perl scripts are safer than C programs ... - * Do not abandon (deprecate) suidperl. Do not advocate C wrappers. - * - * The flow was: perl starts, notices script is suid, execs suidperl with same - * arguments; suidperl opens script, checks many things, sets itself with - * right UID, execs perl with similar arguments but with script pre-opened on - * /dev/fd/xxx; perl checks script is as should be and does work. This was - * insecure: see perlsec(1) for many problems with this approach. - * - * The "correct" flow should be: perl starts, opens script and notices it is - * suid, checks many things, execs suidperl with similar arguments but with - * script on /dev/fd/xxx; suidperl checks script and /dev/fd/xxx object are - * same, checks arguments match #! line, sets itself with right UID, execs - * perl with same arguments; perl checks many things and does work. - * - * (Opening the script in perl instead of suidperl, we "lose" scripts that - * are readable to the target UID but not to the invoker. Where did - * unreadable scripts work anyway?) - * - * For now, suidperl and perl are pretty much the same large and cumbersome - * program, so suidperl can check its argument list (see comments elsewhere). - * - * References: - * Original bug report: - * http://bugs.perl.org/index.html?req=bug_id&bug_id=20010322.218 - * http://rt.perl.org/rt2/Ticket/Display.html?id=6511 - * Comments and discussion with Debian: - * http://bugs.debian.org/203426 - * http://bugs.debian.org/220486 - * Debian Security Advisory DSA 431-1 (does not fully fix problem): - * http://www.debian.org/security/2004/dsa-431 - * CVE candidate: - * http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2003-0618 - * Previous versions of this patch sent to perl5-porters: - * http://www.mail-archive.com/perl5-porters@perl.org/msg71953.html - * http://www.mail-archive.com/perl5-porters@perl.org/msg75245.html - * http://www.mail-archive.com/perl5-porters@perl.org/msg75563.html - * http://www.mail-archive.com/perl5-porters@perl.org/msg75635.html - * -Paul Szabo - psz@maths.usyd.edu.au http://www.maths.usyd.edu.au:8000/u/psz/ -School of Mathematics and Statistics University of Sydney 2006 Australia - * - */ -/* PSz 13 Nov 03 - * Use truthful, neat, specific error messages. - * Cannot always hide the truth; security must not depend on doing so. - */ - -/* PSz 18 Feb 04 - * Use global(?), thread-local fdscript for easier checks. - * (I do not understand how we could possibly get a thread race: - * do not all threads go through the same initialization? Or in - * fact, are not threads started only after we get the script and - * so know what to do? Oh well, make things super-safe...) - */ - #include "EXTERN.h" #define PERL_IN_PERL_C #include "perl.h" @@ -125,39 +70,29 @@ char *getenv (char *); /* Usually in */ static I32 read_e_script(pTHX_ int idx, SV *buf_sv, int maxlen); -#ifdef DOSUID -# ifdef IAMSUID -/* Drop scriptname */ -# define validate_suid(validarg, scriptname, fdscript, suidscript, linestr_sv, rsfp) S_validate_suid(aTHX_ validarg, fdscript, suidscript, linestr_sv, rsfp) -# else -/* Drop suidscript */ -# define validate_suid(validarg, scriptname, fdscript, suidscript, linestr_sv, rsfp) S_validate_suid(aTHX_ validarg, scriptname, fdscript, linestr_sv, rsfp) -# endif -#else -# ifdef SETUID_SCRIPTS_ARE_SECURE_NOW +#ifdef SETUID_SCRIPTS_ARE_SECURE_NOW /* Drop everything. Heck, don't even try to call it */ -# define validate_suid(validarg, scriptname, fdscript, suidscript, linestr_sv, rsfp) NOOP -# else +# define validate_suid(validarg, scriptname, fdscript, suidscript, linestr_sv, rsfp) NOOP +#else /* Drop almost everything */ -# define validate_suid(validarg, scriptname, fdscript, suidscript, linestr_sv, rsfp) S_validate_suid(aTHX_ rsfp) -# endif +# define validate_suid(validarg, scriptname, fdscript, suidscript, linestr_sv, rsfp) S_validate_suid(aTHX_ rsfp) #endif #define CALL_BODY_EVAL(myop) \ if (PL_op == (myop)) \ - PL_op = Perl_pp_entereval(aTHX); \ + PL_op = PL_ppaddr[OP_ENTEREVAL](aTHX); \ if (PL_op) \ CALLRUNOPS(aTHX); #define CALL_BODY_SUB(myop) \ if (PL_op == (myop)) \ - PL_op = Perl_pp_entersub(aTHX); \ + PL_op = PL_ppaddr[OP_ENTERSUB](aTHX); \ if (PL_op) \ CALLRUNOPS(aTHX); #define CALL_LIST_BODY(cv) \ PUSHMARK(PL_stack_sp); \ - call_sv((SV*)(cv), G_EVAL|G_DISCARD); + call_sv(MUTABLE_SV((cv)), G_EVAL|G_DISCARD); static void S_init_tls_and_interp(PerlInterpreter *my_perl) @@ -367,9 +302,9 @@ perl_construct(pTHXx) PL_fdpid = newAV(); /* for remembering popen pids by fd */ PL_modglobal = newHV(); /* pointers to per-interpreter module globals */ PL_errors = newSVpvs(""); - sv_setpvn(PERL_DEBUG_PAD(0), "", 0); /* For regex debugging. */ - sv_setpvn(PERL_DEBUG_PAD(1), "", 0); /* ext/re needs these */ - sv_setpvn(PERL_DEBUG_PAD(2), "", 0); /* even without DEBUGGING. */ + sv_setpvs(PERL_DEBUG_PAD(0), ""); /* For regex debugging. */ + sv_setpvs(PERL_DEBUG_PAD(1), ""); /* ext/re needs these */ + sv_setpvs(PERL_DEBUG_PAD(2), ""); /* even without DEBUGGING. */ #ifdef USE_ITHREADS /* First entry is a list of empty elements. It needs to be initialised else all hell breaks loose in S_find_uninit_var(). */ @@ -457,6 +392,10 @@ perl_construct(pTHXx) PL_timesbase.tms_cstime = 0; #endif + PL_registered_mros = newHV(); + /* Start with 1 bucket, for DFS. It's unlikely we'll need more. */ + HvMAX(PL_registered_mros) = 0; + ENTER; } @@ -662,7 +601,7 @@ perl_destruct(pTHXx) int f; const char *where; /* Our success message is an integer 0, and a char 0 */ - static const char success[sizeof(int) + 1]; + static const char success[sizeof(int) + 1] = {0}; close(fd[0]); @@ -845,6 +784,8 @@ perl_destruct(pTHXx) PL_exitlist = NULL; PL_exitlistlen = 0; + SvREFCNT_dec(PL_registered_mros); + /* jettison our possibly duplicated environment */ /* if PERL_USE_SAFE_PUTENV is defined environ will not have been copied * so we certainly shouldn't free it here @@ -901,7 +842,7 @@ perl_destruct(pTHXx) PL_regex_pad = NULL; #endif - SvREFCNT_dec((SV*) PL_stashcache); + SvREFCNT_dec(MUTABLE_SV(PL_stashcache)); PL_stashcache = NULL; /* loosen bonds of global variables */ @@ -942,8 +883,8 @@ perl_destruct(pTHXx) /* magical thingies */ - SvREFCNT_dec(PL_ofs_sv); /* $, */ - PL_ofs_sv = NULL; + SvREFCNT_dec(PL_ofsgv); /* *, */ + PL_ofsgv = NULL; SvREFCNT_dec(PL_ors_sv); /* $\ */ PL_ors_sv = NULL; @@ -1127,18 +1068,11 @@ perl_destruct(pTHXx) } /* Now absolutely destruct everything, somehow or other, loops or no. */ - SvFLAGS(PL_fdpid) |= SVTYPEMASK; /* don't clean out pid table now */ - SvFLAGS(PL_strtab) |= SVTYPEMASK; /* don't clean out strtab now */ /* the 2 is for PL_fdpid and PL_strtab */ - while (PL_sv_count > 2 && sv_clean_all()) + while (sv_clean_all() > 2) ; - SvFLAGS(PL_fdpid) &= ~SVTYPEMASK; - SvFLAGS(PL_fdpid) |= SVt_PVAV; - SvFLAGS(PL_strtab) &= ~SVTYPEMASK; - SvFLAGS(PL_strtab) |= SVt_PVHV; - AvREAL_off(PL_fdpid); /* no surviving entries */ SvREFCNT_dec(PL_fdpid); /* needed in io_close() */ PL_fdpid = NULL; @@ -1220,7 +1154,7 @@ perl_destruct(pTHXx) SV* sv; register SV* svend; - for (sva = PL_sv_arenaroot; sva; sva = (SV*)SvANY(sva)) { + for (sva = PL_sv_arenaroot; sva; sva = MUTABLE_SV(SvANY(sva))) { svend = &sva[SvREFCNT(sva)]; for (sv = sva + 1; sv < svend; ++sv) { if (SvTYPE(sv) != SVTYPEMASK) { @@ -1521,11 +1455,6 @@ perl_parse(pTHXx_ XSINIT_t xsinit, int argc, char **argv, char **env) PERL_UNUSED_ARG(my_perl); #endif -#ifdef SETUID_SCRIPTS_ARE_SECURE_NOW_AND_IAMSUID - Perl_croak(aTHX_ "suidperl is no longer needed since the kernel can now " - "execute\nsetuid perl scripts securely.\n"); -#endif - #if defined(USE_HASH_SEED) || defined(USE_HASH_SEED_EXPLICIT) /* [perl #22371] Algorimic Complexity Attack on Perl 5.6.1, 5.8.0 * This MUST be done before any hash stores or fetches take place. @@ -1697,6 +1626,12 @@ perl_parse(pTHXx_ XSINIT_t xsinit, int argc, char **argv, char **env) return ret; } +#define INCPUSH_ADD_SUB_DIRS 0x01 +#define INCPUSH_ADD_OLD_VERS 0x02 +#define INCPUSH_NOT_BASEDIR 0x04 +#define INCPUSH_CAN_RELOCATE 0x08 +#define INCPUSH_UNSHIFT 0x10 + STATIC void * S_parse_body(pTHX_ char **env, XSINIT_t xsinit) { @@ -1706,9 +1641,6 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) char **argv = PL_origargv; const char *scriptname = NULL; VOL bool dosearch = FALSE; -#ifdef DOSUID - const char *validarg = ""; -#endif register SV *sv; register char c; const char *cddir = NULL; @@ -1719,7 +1651,7 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) bool add_read_e_script = FALSE; SvGROW(linestr_sv, 80); - sv_setpvn(linestr_sv,"",0); + sv_setpvs(linestr_sv,""); sv = newSVpvs(""); /* first used for -I flags */ SAVEFREESV(sv); @@ -1730,18 +1662,6 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) for (argc--,argv++; argc > 0; argc--,argv++) { if (argv[0][0] != '-' || !argv[0][1]) break; -#ifdef DOSUID - if (*validarg) - validarg = " PHOOEY "; - else - validarg = argv[0]; - /* - * Can we rely on the kernel to start scripts with argv[1] set to - * contain all #! line switches (the whole line)? (argv[0] is set to - * the interpreter name, argv[2] to the script name; argv[3] and - * above may contain other arguments.) - */ -#endif s = argv[0]+1; reswitch: switch ((c = *s)) { @@ -1828,12 +1748,10 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) } if (s && *s) { STRLEN len = strlen(s); - const char * const p = savepvn(s, len); - incpush(p, TRUE, TRUE, FALSE, FALSE); + incpush(s, len, INCPUSH_ADD_SUB_DIRS|INCPUSH_ADD_OLD_VERS); sv_catpvs(sv, "-I"); - sv_catpvn(sv, p, len); + sv_catpvn(sv, s, len); sv_catpvs(sv, " "); - Safefree(p); } else Perl_croak(aTHX_ "No directory specified for -I"); @@ -1879,12 +1797,18 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) # ifdef PERL_MEM_LOG_TIMESTAMP " PERL_MEM_LOG_TIMESTAMP" # endif +# ifdef PERL_USE_DEVEL + " PERL_USE_DEVEL" +# endif # ifdef PERL_USE_SAFE_PUTENV " PERL_USE_SAFE_PUTENV" # endif # ifdef USE_SITECUSTOMIZE " USE_SITECUSTOMIZE" # endif +# ifdef USE_FAST_STDIO + " USE_FAST_STDIO" +# endif , 0); sv_catpv(opts_prog, PL_bincompat_options); @@ -1895,7 +1819,6 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) #else sv_catpvs(opts_prog,"\"\\nCharacteristics of this binary (from libperl): \\n"); #endif - sv_catpvs(opts_prog," Compile-time options: $_\\n\","); #if defined(LOCAL_PATCH_COUNT) @@ -1906,7 +1829,7 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) for (i = 1; i <= LOCAL_PATCH_COUNT; i++) { if (PL_localpatches[i]) Perl_sv_catpvf(aTHX_ opts_prog,"q%c\t%s\n%c,", - 0, PL_localpatches[i], 0); + 0, PL_localpatches[i], 0); } } #endif @@ -1984,7 +1907,6 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) #endif (s = PerlEnv_getenv("PERL5OPT"))) { - const char *popt = s; while (isSPACE(*s)) s++; if (*s == '-' && *(s+1) == 'T') { @@ -1995,7 +1917,7 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) else { char *popt_copy = NULL; while (s && *s) { - char *d; + const char *d; while (isSPACE(*s)) s++; if (*s == '-') { @@ -2011,9 +1933,9 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) while (++s && *s) { if (isSPACE(*s)) { if (!popt_copy) { - popt_copy = SvPVX(sv_2mortal(newSVpv(popt,0))); - s = popt_copy + (s - popt); - d = popt_copy + (d - popt); + popt_copy = SvPVX(sv_2mortal(newSVpv(d,0))); + s = popt_copy + (s - d); + d = popt_copy; } *s++ = '\0'; break; @@ -2058,18 +1980,16 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) TAINT; S_set_caret_X(aTHX); TAINT_NOT; - init_perllib(); + init_perllib(0); + init_perllib(INCPUSH_ADD_OLD_VERS|INCPUSH_NOT_BASEDIR); { bool suidscript = FALSE; -#ifdef DOSUID - const int fdscript = -#endif - open_script(scriptname, dosearch, &suidscript, &rsfp); + open_script(scriptname, dosearch, &suidscript, &rsfp); validate_suid(validarg, scriptname, fdscript, suidscript, - linestr_sv, rsfp); + linestr_sv, rsfp); #ifndef PERL_MICRO # if defined(SIGCHLD) || defined(SIGCLD) @@ -2105,7 +2025,7 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) } } - PL_main_cv = PL_compcv = (CV*)newSV_type(SVt_PVCV); + PL_main_cv = PL_compcv = MUTABLE_CV(newSV_type(SVt_PVCV)); CvUNIQUE_on(PL_compcv); CvPADLIST(PL_compcv) = pad_new(0); @@ -2175,12 +2095,12 @@ S_parse_body(pTHX_ char **env, XSINIT_t xsinit) U32 out = PL_unicode & PERL_UNICODE_OUT_FLAG; if (in) { if (out) - sv_setpvn(sv, ":utf8\0:utf8", 11); + sv_setpvs(sv, ":utf8\0:utf8"); else - sv_setpvn(sv, ":utf8\0", 6); + sv_setpvs(sv, ":utf8\0"); } else if (out) - sv_setpvn(sv, "\0:utf8", 6); + sv_setpvs(sv, "\0:utf8"); SvSETMAGIC(sv); } } @@ -2408,21 +2328,22 @@ S_run_body(pTHX_ I32 oldscope) =for apidoc p||get_sv -Returns the SV of the specified Perl scalar. If C is set and the -Perl variable does not exist then it will be created. If C is not -set and the variable does not exist then NULL is returned. +Returns the SV of the specified Perl scalar. C are passed to +C. If C is set and the +Perl variable does not exist then it will be created. If C is zero +and the variable does not exist then NULL is returned. =cut */ SV* -Perl_get_sv(pTHX_ const char *name, I32 create) +Perl_get_sv(pTHX_ const char *name, I32 flags) { GV *gv; PERL_ARGS_ASSERT_GET_SV; - gv = gv_fetchpv(name, create, SVt_PV); + gv = gv_fetchpv(name, flags, SVt_PV); if (gv) return GvSV(gv); return NULL; @@ -2433,21 +2354,22 @@ Perl_get_sv(pTHX_ const char *name, I32 create) =for apidoc p||get_av -Returns the AV of the specified Perl array. If C is set and the -Perl variable does not exist then it will be created. If C is not -set and the variable does not exist then NULL is returned. +Returns the AV of the specified Perl array. C are passed to +C. If C is set and the +Perl variable does not exist then it will be created. If C is zero +and the variable does not exist then NULL is returned. =cut */ AV* -Perl_get_av(pTHX_ const char *name, I32 create) +Perl_get_av(pTHX_ const char *name, I32 flags) { - GV* const gv = gv_fetchpv(name, create, SVt_PVAV); + GV* const gv = gv_fetchpv(name, flags, SVt_PVAV); PERL_ARGS_ASSERT_GET_AV; - if (create) + if (flags) return GvAVn(gv); if (gv) return GvAV(gv); @@ -2459,21 +2381,22 @@ Perl_get_av(pTHX_ const char *name, I32 create) =for apidoc p||get_hv -Returns the HV of the specified Perl hash. If C is set and the -Perl variable does not exist then it will be created. If C is not -set and the variable does not exist then NULL is returned. +Returns the HV of the specified Perl hash. C are passed to +C. If C is set and the +Perl variable does not exist then it will be created. If C is zero +and the variable does not exist then NULL is returned. =cut */ HV* -Perl_get_hv(pTHX_ const char *name, I32 create) +Perl_get_hv(pTHX_ const char *name, I32 flags) { - GV* const gv = gv_fetchpv(name, create, SVt_PVHV); + GV* const gv = gv_fetchpv(name, flags, SVt_PVHV); PERL_ARGS_ASSERT_GET_HV; - if (create) + if (flags) return GvHVn(gv); if (gv) return GvHV(gv); @@ -2519,6 +2442,8 @@ Perl_get_cvn_flags(pTHX_ const char *name, STRLEN len, I32 flags) return NULL; } +/* Nothing in core calls this now, but we can't replace it with a macro and + move it to mathoms.c as a macro would evaluate name twice. */ CV* Perl_get_cv(pTHX_ const char *name, I32 flags) { @@ -2577,7 +2502,7 @@ Perl_call_pv(pTHX_ const char *sub_name, I32 flags) { PERL_ARGS_ASSERT_CALL_PV; - return call_sv((SV*)get_cv(sub_name, TRUE), flags); + return call_sv(MUTABLE_SV(get_cv(sub_name, GV_ADD)), flags); } /* @@ -2654,7 +2579,7 @@ Perl_call_sv(pTHX_ SV *sv, VOL I32 flags) && (PL_DBcv || (PL_DBcv = GvCV(PL_DBsub))) /* Try harder, since this may have been a sighandler, thus * curstash may be meaningless. */ - && (SvTYPE(sv) != SVt_PVCV || CvSTASH((CV*)sv) != PL_debstash) + && (SvTYPE(sv) != SVt_PVCV || CvSTASH((const CV *)sv) != PL_debstash) && !(flags & G_NODEBUG)) PL_op->op_private |= OPpENTERSUB_DB; @@ -2687,8 +2612,9 @@ Perl_call_sv(pTHX_ SV *sv, VOL I32 flags) redo_body: CALL_BODY_SUB((OP*)&myop); retval = PL_stack_sp - (PL_stack_base + oldmark); - if (!(flags & G_KEEPERR)) - sv_setpvn(ERRSV,"",0); + if (!(flags & G_KEEPERR)) { + CLEAR_ERRSV(); + } break; case 1: STATUS_ALL_FAILURE; @@ -2788,8 +2714,9 @@ Perl_eval_sv(pTHX_ SV *sv, I32 flags) redo_body: CALL_BODY_EVAL((OP*)&myop); retval = PL_stack_sp - (PL_stack_base + oldmark); - if (!(flags & G_KEEPERR)) - sv_setpvn(ERRSV,"",0); + if (!(flags & G_KEEPERR)) { + CLEAR_ERRSV(); + } break; case 1: STATUS_ALL_FAILURE; @@ -2855,7 +2782,7 @@ Perl_eval_pv(pTHX_ const char *p, I32 croak_on_error) PUTBACK; if (croak_on_error && SvTRUE(ERRSV)) { - Perl_croak(aTHX_ SvPVx_nolen_const(ERRSV)); + Perl_croak(aTHX_ "%s", SvPVx_nolen_const(ERRSV)); } return sv; @@ -2891,17 +2818,6 @@ Perl_require_pv(pTHX_ const char *pv) POPSTACK; } -void -Perl_magicname(pTHX_ const char *sym, const char *name, I32 namlen) -{ - register GV * const gv = gv_fetchpv(sym, GV_ADD, SVt_PV); - - PERL_ARGS_ASSERT_MAGICNAME; - - if (gv) - sv_magic(GvSV(gv), (SV*)gv, PERL_MAGIC_sv, name, namlen); -} - STATIC void S_usage(pTHX_ const char *name) /* XXX move this out into a module ? */ { @@ -2967,7 +2883,7 @@ Perl_get_debug_opts(pTHX_ const char **s, bool givehelp) " o Method and overloading resolution", " c String/numeric conversions", " P Print profiling info, source file input state", - " m Memory allocation", + " m Memory and SV allocation", " f Format processing", " r Regular expression parsing and execution", " x Syntax tree dump", @@ -3069,7 +2985,7 @@ Perl_moreswitches(pTHX_ const char *s) PL_rs = newSVpvn(&ch, 1); } } - sv_setsv(get_sv("/", TRUE), PL_rs); + sv_setsv(get_sv("/", GV_ADD), PL_rs); return s + numlen; } case 'C': @@ -3181,9 +3097,8 @@ Perl_moreswitches(pTHX_ const char *s) while (isSPACE(*p)) p++; } while (*p && *p != '-'); - e = savepvn(s, e-s); - incpush(e, TRUE, TRUE, FALSE, FALSE); - Safefree(e); + incpush(s, e-s, + INCPUSH_ADD_SUB_DIRS|INCPUSH_ADD_OLD_VERS|INCPUSH_UNSHIFT); s = p; if (*s == '-') s++; @@ -3247,7 +3162,7 @@ Perl_moreswitches(pTHX_ const char *s) if (colon) Perl_croak(aTHX_ "Invalid module name %.*s with -%c option: " "contains single ':'", - s - start, start, option); + (int)(s - start), start, option); end = s + strlen(s); if (*s != '=') { sv_catpvn(sv, start, end - start); @@ -3308,14 +3223,30 @@ Perl_moreswitches(pTHX_ const char *s) if (!sv_derived_from(PL_patchlevel, "version")) upg_version(PL_patchlevel, TRUE); #if !defined(DGUX) - PerlIO_printf(PerlIO_stdout(), - Perl_form(aTHX_ "\nThis is perl, %"SVf + { + SV* level= vstringify(PL_patchlevel); #ifdef PERL_PATCHNUM - " DEVEL" STRINGIFY(PERL_PATCHNUM) -#endif - " built for %s", - SVfARG(vstringify(PL_patchlevel)), - ARCHNAME)); +# ifdef PERL_GIT_UNCOMMITTED_CHANGES + SV *num = newSVpvs(PERL_PATCHNUM "*"); +# else + SV *num = newSVpvs(PERL_PATCHNUM); +# endif + + if (sv_len(num)>=sv_len(level) && strnEQ(SvPV_nolen(num),SvPV_nolen(level),sv_len(level))) { + SvREFCNT_dec(level); + level= num; + } else { + Perl_sv_catpvf(aTHX_ level, " (%"SVf")", num); + SvREFCNT_dec(num); + } + #endif + PerlIO_printf(PerlIO_stdout(), + "\nThis is perl, %"SVf + " built for %s", + level, + ARCHNAME); + SvREFCNT_dec(level); + } #else /* DGUX */ /* Adjust verbose output as in the perl that ships with the DG/UX OS from EMC */ PerlIO_printf(PerlIO_stdout(), @@ -3328,7 +3259,6 @@ Perl_moreswitches(pTHX_ const char *s) Perl_form(aTHX_ " OS Specific Release: %s\n", OSVERS)); #endif /* !DGUX */ - #if defined(LOCAL_PATCH_COUNT) if (LOCAL_PATCH_COUNT > 0) PerlIO_printf(PerlIO_stdout(), @@ -3339,7 +3269,7 @@ Perl_moreswitches(pTHX_ const char *s) #endif PerlIO_printf(PerlIO_stdout(), - "\n\nCopyright 1987-2007, Larry Wall\n"); + "\n\nCopyright 1987-2009, Larry Wall\n"); #ifdef MACOS_TRADITIONAL PerlIO_printf(PerlIO_stdout(), "\nMac OS port Copyright 1991-2002, Matthias Neeracher;\n" @@ -3437,8 +3367,10 @@ Internet, point your browser at http://www.perl.org/, the Perl Home Page.\n\n"); return s; case '*': case ' ': - if (s[1] == '-') /* Additional switches on #! line. */ - return s+2; + while( *s == ' ' ) + ++s; + if (s[0] == '-') /* Additional switches on #! line. */ + return s+1; break; case '-': case 0: @@ -3547,7 +3479,7 @@ S_init_main_stash(pTHX) of the SvREFCNT_dec, only to add it again with hv_name_set */ SvREFCNT_dec(GvHV(gv)); hv_name_set(PL_defstash, "main", 4, 0); - GvHV(gv) = (HV*)SvREFCNT_inc_simple(PL_defstash); + GvHV(gv) = MUTABLE_HV(SvREFCNT_inc_simple(PL_defstash)); SvREADONLY_on(gv); PL_incgv = gv_HVadd(gv_AVadd(gv_fetchpvs("INC", GV_ADD|GV_NOTQUAL, SVt_PVAV))); @@ -3567,14 +3499,14 @@ S_init_main_stash(pTHX) gv_SVadd(PL_errgv); #endif sv_grow(ERRSV, 240); /* Preallocate - for immediate signals. */ - sv_setpvn(ERRSV, "", 0); + CLEAR_ERRSV(); PL_curstash = PL_defstash; CopSTASH_set(&PL_compiling, PL_defstash); PL_debstash = GvHV(gv_fetchpvs("DB::", GV_ADDMULTI, SVt_PVHV)); PL_globalstash = GvHV(gv_fetchpvs("CORE::GLOBAL::", GV_ADDMULTI, SVt_PVHV)); /* We must init $/ before switches are processed. */ - sv_setpvn(get_sv("/", TRUE), "\n", 1); + sv_setpvs(get_sv("/", GV_ADD), "\n"); } STATIC int @@ -3638,23 +3570,6 @@ S_open_script(pTHX_ const char *scriptname, bool dosearch, fcntl(PerlIO_fileno(*rsfpp),F_SETFD,1); # endif } -#ifdef IAMSUID - else { - Perl_croak(aTHX_ "sperl needs fd script\n" - "You should not call sperl directly; do you need to " - "change a #! line\nfrom sperl to perl?\n"); - -/* PSz 11 Nov 03 - * Do not open (or do other fancy stuff) while setuid. - * Perl does the open, and hands script to suidperl on a fd; - * suidperl only does some checks, sets up UIDs and re-execs - * perl with that fd as it has always done. - */ - } - if (*suidscript) { - Perl_croak(aTHX_ "suidperl needs (suid) fd script\n"); - } -#else /* IAMSUID */ else if (!*scriptname) { forbid_setid(0, *suidscript); *rsfpp = PerlIO_stdin(); @@ -3707,7 +3622,6 @@ S_open_script(pTHX_ const char *scriptname, bool dosearch, fcntl(PerlIO_fileno(*rsfpp),F_SETFD,1); # endif } -#endif /* IAMSUID */ if (!*rsfpp) { /* PSz 16 Sep 03 Keep neat error message */ if (PL_e_script) @@ -3726,532 +3640,18 @@ S_open_script(pTHX_ const char *scriptname, bool dosearch, * I_MNTENT HAS_GETMNTENT HAS_HASMNTOPT * here so that metaconfig picks them up. */ -#ifdef IAMSUID -STATIC int -S_fd_on_nosuid_fs(pTHX_ int fd) -{ -/* PSz 27 Feb 04 - * We used to do this as "plain" user (after swapping UIDs with setreuid); - * but is needed also on machines without setreuid. - * Seems safe enough to run as root. - */ - int check_okay = 0; /* able to do all the required sys/libcalls */ - int on_nosuid = 0; /* the fd is on a nosuid fs */ - /* PSz 12 Nov 03 - * Need to check noexec also: nosuid might not be set, the average - * sysadmin would say that nosuid is irrelevant once he sets noexec. - */ - int on_noexec = 0; /* the fd is on a noexec fs */ - -/* - * Preferred order: fstatvfs(), fstatfs(), ustat()+getmnt(), getmntent(). - * fstatvfs() is UNIX98. - * fstatfs() is 4.3 BSD. - * ustat()+getmnt() is pre-4.3 BSD. - * getmntent() is O(number-of-mounted-filesystems) and can hang on - * an irrelevant filesystem while trying to reach the right one. - */ - -#undef FD_ON_NOSUID_CHECK_OKAY /* found the syscalls to do the check? */ - -# if !defined(FD_ON_NOSUID_CHECK_OKAY) && \ - defined(HAS_FSTATVFS) -# define FD_ON_NOSUID_CHECK_OKAY - struct statvfs stfs; - - check_okay = fstatvfs(fd, &stfs) == 0; - on_nosuid = check_okay && (stfs.f_flag & ST_NOSUID); -#ifdef ST_NOEXEC - /* ST_NOEXEC certainly absent on AIX 5.1, and doesn't seem to be documented - on platforms where it is present. */ - on_noexec = check_okay && (stfs.f_flag & ST_NOEXEC); -#endif -# endif /* fstatvfs */ - -# if !defined(FD_ON_NOSUID_CHECK_OKAY) && \ - defined(PERL_MOUNT_NOSUID) && \ - defined(PERL_MOUNT_NOEXEC) && \ - defined(HAS_FSTATFS) && \ - defined(HAS_STRUCT_STATFS) && \ - defined(HAS_STRUCT_STATFS_F_FLAGS) -# define FD_ON_NOSUID_CHECK_OKAY - struct statfs stfs; - - check_okay = fstatfs(fd, &stfs) == 0; - on_nosuid = check_okay && (stfs.f_flags & PERL_MOUNT_NOSUID); - on_noexec = check_okay && (stfs.f_flags & PERL_MOUNT_NOEXEC); -# endif /* fstatfs */ - -# if !defined(FD_ON_NOSUID_CHECK_OKAY) && \ - defined(PERL_MOUNT_NOSUID) && \ - defined(PERL_MOUNT_NOEXEC) && \ - defined(HAS_FSTAT) && \ - defined(HAS_USTAT) && \ - defined(HAS_GETMNT) && \ - defined(HAS_STRUCT_FS_DATA) && \ - defined(NOSTAT_ONE) -# define FD_ON_NOSUID_CHECK_OKAY - Stat_t fdst; - - if (fstat(fd, &fdst) == 0) { - struct ustat us; - if (ustat(fdst.st_dev, &us) == 0) { - struct fs_data fsd; - /* NOSTAT_ONE here because we're not examining fields which - * vary between that case and STAT_ONE. */ - if (getmnt((int*)0, &fsd, (int)0, NOSTAT_ONE, us.f_fname) == 0) { - size_t cmplen = sizeof(us.f_fname); - if (sizeof(fsd.fd_req.path) < cmplen) - cmplen = sizeof(fsd.fd_req.path); - if (strnEQ(fsd.fd_req.path, us.f_fname, cmplen) && - fdst.st_dev == fsd.fd_req.dev) { - check_okay = 1; - on_nosuid = fsd.fd_req.flags & PERL_MOUNT_NOSUID; - on_noexec = fsd.fd_req.flags & PERL_MOUNT_NOEXEC; - } - } - } - } -# endif /* fstat+ustat+getmnt */ - -# if !defined(FD_ON_NOSUID_CHECK_OKAY) && \ - defined(HAS_GETMNTENT) && \ - defined(HAS_HASMNTOPT) && \ - defined(MNTOPT_NOSUID) && \ - defined(MNTOPT_NOEXEC) -# define FD_ON_NOSUID_CHECK_OKAY - FILE *mtab = fopen("/etc/mtab", "r"); - struct mntent *entry; - Stat_t stb, fsb; - - if (mtab && (fstat(fd, &stb) == 0)) { - while (entry = getmntent(mtab)) { - if (stat(entry->mnt_dir, &fsb) == 0 - && fsb.st_dev == stb.st_dev) - { - /* found the filesystem */ - check_okay = 1; - if (hasmntopt(entry, MNTOPT_NOSUID)) - on_nosuid = 1; - if (hasmntopt(entry, MNTOPT_NOEXEC)) - on_noexec = 1; - break; - } /* A single fs may well fail its stat(). */ - } - } - if (mtab) - fclose(mtab); -# endif /* getmntent+hasmntopt */ - - if (!check_okay) - Perl_croak(aTHX_ "Can't check filesystem of script \"%s\" for nosuid/noexec", PL_origfilename); - if (on_nosuid) - Perl_croak(aTHX_ "Setuid script \"%s\" on nosuid filesystem", PL_origfilename); - if (on_noexec) - Perl_croak(aTHX_ "Setuid script \"%s\" on noexec filesystem", PL_origfilename); - return ((!check_okay) || on_nosuid || on_noexec); -} -#endif /* IAMSUID */ - -#ifdef DOSUID -STATIC void -S_validate_suid(pTHX_ const char *validarg, -# ifndef IAMSUID - const char *scriptname, -# endif - int fdscript, -# ifdef IAMSUID - bool suidscript, -# endif - SV *linestr_sv, PerlIO *rsfp) -{ - dVAR; - const char *s, *s2; - - PERL_ARGS_ASSERT_VALIDATE_SUID; - - /* do we need to emulate setuid on scripts? */ - - /* This code is for those BSD systems that have setuid #! scripts disabled - * in the kernel because of a security problem. Merely defining DOSUID - * in perl will not fix that problem, but if you have disabled setuid - * scripts in the kernel, this will attempt to emulate setuid and setgid - * on scripts that have those now-otherwise-useless bits set. The setuid - * root version must be called suidperl or sperlN.NNN. If regular perl - * discovers that it has opened a setuid script, it calls suidperl with - * the same argv that it had. If suidperl finds that the script it has - * just opened is NOT setuid root, it sets the effective uid back to the - * uid. We don't just make perl setuid root because that loses the - * effective uid we had before invoking perl, if it was different from the - * uid. - * PSz 27 Feb 04 - * Description/comments above do not match current workings: - * suidperl must be hardlinked to sperlN.NNN (that is what we exec); - * suidperl called with script open and name changed to /dev/fd/N/X; - * suidperl croaks if script is not setuid; - * making perl setuid would be a huge security risk (and yes, that - * would lose any euid we might have had). - * - * DOSUID must be defined in both perl and suidperl, and IAMSUID must - * be defined in suidperl only. suidperl must be setuid root. The - * Configure script will set this up for you if you want it. - */ - - if (PerlLIO_fstat(PerlIO_fileno(rsfp),&PL_statbuf) < 0) /* normal stat is insecure */ - Perl_croak(aTHX_ "Can't stat script \"%s\"",PL_origfilename); - if (PL_statbuf.st_mode & (S_ISUID|S_ISGID)) { - I32 len; - const char *linestr; - const char *s_end; - -# ifdef IAMSUID - if (fdscript < 0 || !suidscript) - Perl_croak(aTHX_ "Need (suid) fdscript in suidperl\n"); /* We already checked this */ - /* PSz 11 Nov 03 - * Since the script is opened by perl, not suidperl, some of these - * checks are superfluous. Leaving them in probably does not lower - * security(?!). - */ - /* PSz 27 Feb 04 - * Do checks even for systems with no HAS_SETREUID. - * We used to swap, then re-swap UIDs with -# ifdef HAS_SETREUID - if (setreuid(PL_euid,PL_uid) < 0 - || PerlProc_getuid() != PL_euid || PerlProc_geteuid() != PL_uid) - Perl_croak(aTHX_ "Can't swap uid and euid"); -# endif -# ifdef HAS_SETREUID - if (setreuid(PL_uid,PL_euid) < 0 - || PerlProc_getuid() != PL_uid || PerlProc_geteuid() != PL_euid) - Perl_croak(aTHX_ "Can't reswap uid and euid"); -# endif - */ - - /* On this access check to make sure the directories are readable, - * there is actually a small window that the user could use to make - * filename point to an accessible directory. So there is a faint - * chance that someone could execute a setuid script down in a - * non-accessible directory. I don't know what to do about that. - * But I don't think it's too important. The manual lies when - * it says access() is useful in setuid programs. - * - * So, access() is pretty useless... but not harmful... do anyway. - */ - if (PerlLIO_access(CopFILE(PL_curcop),1)) { /*double check*/ - Perl_croak(aTHX_ "Can't access() script\n"); - } - - /* If we can swap euid and uid, then we can determine access rights - * with a simple stat of the file, and then compare device and - * inode to make sure we did stat() on the same file we opened. - * Then we just have to make sure he or she can execute it. - * - * PSz 24 Feb 04 - * As the script is opened by perl, not suidperl, we do not need to - * care much about access rights. - * - * The 'script changed' check is needed, or we can get lied to - * about $0 with e.g. - * suidperl /dev/fd/4//bin/x 4 4000) - Perl_croak(aTHX_ "Very long #! line"); - /* Allow more than a single space after #! */ - while (isSPACE(*s)) s++; - /* Sanity check on buffer end */ - while ((*s) && !isSPACE(*s)) s++; - for (s2 = s; (s2 > linestr && - (isDIGIT(s2[-1]) || s2[-1] == '.' || s2[-1] == '_' - || s2[-1] == '-')); s2--) ; - /* Sanity check on buffer start */ - if ( (s2-4 < linestr || strnNE(s2-4,"perl",4)) && - (s-9 < linestr || strnNE(s-9,"perl",4)) ) - Perl_croak(aTHX_ "Not a perl script"); - while (*s == ' ' || *s == '\t') s++; - /* - * #! arg must be what we saw above. They can invoke it by - * mentioning suidperl explicitly, but they may not add any strange - * arguments beyond what #! says if they do invoke suidperl that way. - */ - /* - * The way validarg was set up, we rely on the kernel to start - * scripts with argv[1] set to contain all #! line switches (the - * whole line). - */ - /* - * Check that we got all the arguments listed in the #! line (not - * just that there are no extraneous arguments). Might not matter - * much, as switches from #! line seem to be acted upon (also), and - * so may be checked and trapped in perl. But, security checks must - * be done in suidperl and not deferred to perl. Note that suidperl - * does not get around to parsing (and checking) the switches on - * the #! line (but execs perl sooner). - * Allow (require) a trailing newline (which may be of two - * characters on some architectures?) (but no other trailing - * whitespace). - */ - len = strlen(validarg); - if (strEQ(validarg," PHOOEY ") || - strnNE(s,validarg,len) || !isSPACE(s[len]) || - !((s_end - s) == len+1 - || ((s_end - s) == len+2 && isSPACE(s[len+1])))) - Perl_croak(aTHX_ "Args must match #! line"); - -# ifndef IAMSUID - if (fdscript < 0 && - PL_euid != PL_uid && (PL_statbuf.st_mode & S_ISUID) && - PL_euid == PL_statbuf.st_uid) - if (!PL_do_undump) - Perl_croak(aTHX_ "YOU HAVEN'T DISABLED SET-ID SCRIPTS IN THE KERNEL YET!\n\ -FIX YOUR KERNEL, OR PUT A C WRAPPER AROUND THIS SCRIPT!\n"); -# endif /* IAMSUID */ - - if (fdscript < 0 && - PL_euid) { /* oops, we're not the setuid root perl */ - /* PSz 18 Feb 04 - * When root runs a setuid script, we do not go through the same - * steps of execing sperl and then perl with fd scripts, but - * simply set up UIDs within the same perl invocation; so do - * not have the same checks (on options, whatever) that we have - * for plain users. No problem really: would have to be a script - * that does not actually work for plain users; and if root is - * foolish and can be persuaded to run such an unsafe script, he - * might run also non-setuid ones, and deserves what he gets. - * - * Or, we might drop the PL_euid check above (and rely just on - * fdscript to avoid loops), and do the execs - * even for root. - */ -# ifndef IAMSUID - int which; - /* PSz 11 Nov 03 - * Pass fd script to suidperl. - * Exec suidperl, substituting fd script for scriptname. - * Pass script name as "subdir" of fd, which perl will grok; - * in fact will use that to distinguish this from "normal" - * usage, see comments above. - */ - PerlIO_rewind(rsfp); - PerlLIO_lseek(PerlIO_fileno(rsfp),(Off_t)0,0); /* just in case rewind didn't */ - /* PSz 27 Feb 04 Sanity checks on scriptname */ - if ((!scriptname) || (!*scriptname) ) { - Perl_croak(aTHX_ "No setuid script name\n"); - } - if (*scriptname == '-') { - Perl_croak(aTHX_ "Setuid script name may not begin with dash\n"); - /* Or we might confuse it with an option when replacing - * name in argument list, below (though we do pointer, not - * string, comparisons). - */ - } - for (which = 1; PL_origargv[which] && PL_origargv[which] != scriptname; which++) ; - if (!PL_origargv[which]) { - Perl_croak(aTHX_ "Can't change argv to have fd script\n"); - } - PL_origargv[which] = savepv(Perl_form(aTHX_ "/dev/fd/%d/%s", - PerlIO_fileno(rsfp), PL_origargv[which])); -# if defined(HAS_FCNTL) && defined(F_SETFD) - fcntl(PerlIO_fileno(rsfp),F_SETFD,0); /* ensure no close-on-exec */ -# endif - PERL_FPU_PRE_EXEC - PerlProc_execv(Perl_form(aTHX_ "%s/sperl"PERL_FS_VER_FMT, BIN_EXP, - (int)PERL_REVISION, (int)PERL_VERSION, - (int)PERL_SUBVERSION), PL_origargv); - PERL_FPU_POST_EXEC -# endif /* IAMSUID */ - Perl_croak(aTHX_ "Can't do setuid (cannot exec sperl)\n"); - } - if (PL_statbuf.st_mode & S_ISGID && PL_statbuf.st_gid != PL_egid) { -/* PSz 26 Feb 04 - * This seems back to front: we try HAS_SETEGID first; if not available - * then try HAS_SETREGID; as a last chance we try HAS_SETRESGID. May be OK - * in the sense that we only want to set EGID; but are there any machines - * with either of the latter, but not the former? Same with UID, later. - */ -# ifdef HAS_SETEGID - (void)setegid(PL_statbuf.st_gid); -# else -# ifdef HAS_SETREGID - (void)setregid((Gid_t)-1,PL_statbuf.st_gid); -# else -# ifdef HAS_SETRESGID - (void)setresgid((Gid_t)-1,PL_statbuf.st_gid,(Gid_t)-1); -# else - PerlProc_setgid(PL_statbuf.st_gid); -# endif -# endif -# endif - if (PerlProc_getegid() != PL_statbuf.st_gid) - Perl_croak(aTHX_ "Can't do setegid!\n"); - } - if (PL_statbuf.st_mode & S_ISUID) { - if (PL_statbuf.st_uid != PL_euid) -# ifdef HAS_SETEUID - (void)seteuid(PL_statbuf.st_uid); /* all that for this */ -# else -# ifdef HAS_SETREUID - (void)setreuid((Uid_t)-1,PL_statbuf.st_uid); -# else -# ifdef HAS_SETRESUID - (void)setresuid((Uid_t)-1,PL_statbuf.st_uid,(Uid_t)-1); -# else - PerlProc_setuid(PL_statbuf.st_uid); -# endif -# endif -# endif - if (PerlProc_geteuid() != PL_statbuf.st_uid) - Perl_croak(aTHX_ "Can't do seteuid!\n"); - } - else if (PL_uid) { /* oops, mustn't run as root */ -# ifdef HAS_SETEUID - (void)seteuid((Uid_t)PL_uid); -# else -# ifdef HAS_SETREUID - (void)setreuid((Uid_t)-1,(Uid_t)PL_uid); -# else -# ifdef HAS_SETRESUID - (void)setresuid((Uid_t)-1,(Uid_t)PL_uid,(Uid_t)-1); -# else - PerlProc_setuid((Uid_t)PL_uid); -# endif -# endif -# endif - if (PerlProc_geteuid() != PL_uid) - Perl_croak(aTHX_ "Can't do seteuid!\n"); - } - init_ids(); - if (!cando(S_IXUSR,TRUE,&PL_statbuf)) - Perl_croak(aTHX_ "Effective UID cannot exec script\n"); /* they can't do this */ - } -# ifdef IAMSUID - else if (fdscript < 0 || !suidscript) - /* PSz 13 Nov 03 Caught elsewhere, useless(?!) here */ - Perl_croak(aTHX_ "(suid) fdscript needed in suidperl\n"); - else { -/* PSz 16 Sep 03 Keep neat error message */ - Perl_croak(aTHX_ "Script is not setuid/setgid in suidperl\n"); - } - - /* We absolutely must clear out any saved ids here, so we */ - /* exec the real perl, substituting fd script for scriptname. */ - /* (We pass script name as "subdir" of fd, which perl will grok.) */ - /* - * It might be thought that using setresgid and/or setresuid (changed to - * set the saved IDs) above might obviate the need to exec, and we could - * go on to "do the perl thing". - * - * Is there such a thing as "saved GID", and is that set for setuid (but - * not setgid) execution like suidperl? Without exec, it would not be - * cleared for setuid (but not setgid) scripts (or might need a dummy - * setresgid). - * - * We need suidperl to do the exact same argument checking that perl - * does. Thus it cannot be very small; while it could be significantly - * smaller, it is safer (simpler?) to make it essentially the same - * binary as perl (but they are not identical). - Maybe could defer that - * check to the invoked perl, and suidperl be a tiny wrapper instead; - * but prefer to do thorough checks in suidperl itself. Such deferral - * would make suidperl security rely on perl, a design no-no. - * - * Setuid things should be short and simple, thus easy to understand and - * verify. They should do their "own thing", without influence by - * attackers. It may help if their internal execution flow is fixed, - * regardless of platform: it may be best to exec anyway. - * - * Suidperl should at least be conceptually simple: a wrapper only, - * never to do any real perl. Maybe we should put - * #ifdef IAMSUID - * Perl_croak(aTHX_ "Suidperl should never do real perl\n"); - * #endif - * into the perly bits. - */ - PerlIO_rewind(rsfp); - PerlLIO_lseek(PerlIO_fileno(rsfp),(Off_t)0,0); /* just in case rewind didn't */ - /* PSz 11 Nov 03 - * Keep original arguments: suidperl already has fd script. - */ -# if defined(HAS_FCNTL) && defined(F_SETFD) - fcntl(PerlIO_fileno(rsfp),F_SETFD,0); /* ensure no close-on-exec */ -# endif - PERL_FPU_PRE_EXEC - PerlProc_execv(Perl_form(aTHX_ "%s/perl"PERL_FS_VER_FMT, BIN_EXP, - (int)PERL_REVISION, (int)PERL_VERSION, - (int)PERL_SUBVERSION), PL_origargv);/* try again */ - PERL_FPU_POST_EXEC - Perl_croak(aTHX_ "Can't do setuid (suidperl cannot exec perl)\n"); -# endif /* IAMSUID */ -} - -#else /* !DOSUID */ - -# ifdef SETUID_SCRIPTS_ARE_SECURE_NOW +#ifdef SETUID_SCRIPTS_ARE_SECURE_NOW /* Don't even need this function. */ -# else +#else STATIC void S_validate_suid(pTHX_ PerlIO *rsfp) { PERL_ARGS_ASSERT_VALIDATE_SUID; if (PL_euid != PL_uid || PL_egid != PL_gid) { /* (suidperl doesn't exist, in fact) */ -# ifndef SETUID_SCRIPTS_ARE_SECURE_NOW + dVAR; + PerlLIO_fstat(PerlIO_fileno(rsfp),&PL_statbuf); /* may be either wrapped or real suid */ if ((PL_euid != PL_uid && PL_euid == PL_statbuf.st_uid && PL_statbuf.st_mode & S_ISUID) || @@ -4260,12 +3660,10 @@ S_validate_suid(pTHX_ PerlIO *rsfp) if (!PL_do_undump) Perl_croak(aTHX_ "YOU HAVEN'T DISABLED SET-ID SCRIPTS IN THE KERNEL YET!\n\ FIX YOUR KERNEL, PUT A C WRAPPER AROUND THIS SCRIPT, OR USE -u AND UNDUMP!\n"); -# endif /* SETUID_SCRIPTS_ARE_SECURE_NOW */ /* not set-id, must be wrapped */ } } -# endif /* SETUID_SCRIPTS_ARE_SECURE_NOW */ -#endif /* DOSUID */ +#endif /* SETUID_SCRIPTS_ARE_SECURE_NOW */ STATIC void S_find_beginning(pTHX_ SV* linestr_sv, PerlIO *rsfp) @@ -4423,34 +3821,8 @@ S_forbid_setid(pTHX_ const char flag, const bool suidscript) /* g */ if (PL_egid != PL_gid) Perl_croak(aTHX_ "No %s allowed while running setgid", message); #endif /* SETUID_SCRIPTS_ARE_SECURE_NOW */ - /* PSz 29 Feb 04 - * Checks for UID/GID above "wrong": why disallow - * perl -e 'print "Hello\n"' - * from within setuid things?? Simply drop them: replaced by - * fdscript/suidscript and #ifdef IAMSUID checks below. - * - * This may be too late for command-line switches. Will catch those on - * the #! line, after finding the script name and setting up - * fdscript/suidscript. Note that suidperl does not get around to - * parsing (and checking) the switches on the #! line, but checks that - * the two sets are identical. - * - * With SETUID_SCRIPTS_ARE_SECURE_NOW, could we use fdscript, also or - * instead, or would that be "too late"? (We never have suidscript, can - * we be sure to have fdscript?) - * - * Catch things with suidscript (in descendant of suidperl), even with - * right UID/GID. Was already checked in suidperl, with #ifdef IAMSUID, - * below; but I am paranoid. - * - * Also see comments about root running a setuid script, elsewhere. - */ if (suidscript) Perl_croak(aTHX_ "No %s allowed with (suid) fdscript", message); -#ifdef IAMSUID - /* PSz 11 Nov 03 Catch it in suidperl, always! */ - Perl_croak(aTHX_ "No %s allowed in suidperl", message); -#endif /* IAMSUID */ } void @@ -4545,7 +3917,9 @@ S_init_predump_symbols(pTHX) GV *tmpgv; IO *io; - sv_setpvn(get_sv("\"", TRUE), " ", 1); + sv_setpvs(get_sv("\"", GV_ADD), " "); + PL_ofsgv = (GV*)SvREFCNT_inc(gv_fetchpvs(",", GV_ADD|GV_NOTQUAL, SVt_PV)); + PL_stdingv = gv_fetchpvs("STDIN", GV_ADD|GV_NOTQUAL, SVt_PVIO); GvMULTI_on(PL_stdingv); io = GvIOp(PL_stdingv); @@ -4553,7 +3927,7 @@ S_init_predump_symbols(pTHX) IoIFP(io) = PerlIO_stdin(); tmpgv = gv_fetchpvs("stdin", GV_ADD|GV_NOTQUAL, SVt_PV); GvMULTI_on(tmpgv); - GvIOp(tmpgv) = (IO*)SvREFCNT_inc_simple(io); + GvIOp(tmpgv) = MUTABLE_IO(SvREFCNT_inc_simple(io)); tmpgv = gv_fetchpvs("STDOUT", GV_ADD|GV_NOTQUAL, SVt_PVIO); GvMULTI_on(tmpgv); @@ -4563,7 +3937,7 @@ S_init_predump_symbols(pTHX) setdefout(tmpgv); tmpgv = gv_fetchpvs("stdout", GV_ADD|GV_NOTQUAL, SVt_PV); GvMULTI_on(tmpgv); - GvIOp(tmpgv) = (IO*)SvREFCNT_inc_simple(io); + GvIOp(tmpgv) = MUTABLE_IO(SvREFCNT_inc_simple(io)); PL_stderrgv = gv_fetchpvs("STDERR", GV_ADD|GV_NOTQUAL, SVt_PVIO); GvMULTI_on(PL_stderrgv); @@ -4572,7 +3946,7 @@ S_init_predump_symbols(pTHX) IoOFP(io) = IoIFP(io) = PerlIO_stderr(); tmpgv = gv_fetchpvs("stderr", GV_ADD|GV_NOTQUAL, SVt_PV); GvMULTI_on(tmpgv); - GvIOp(tmpgv) = (IO*)SvREFCNT_inc_simple(io); + GvIOp(tmpgv) = MUTABLE_IO(SvREFCNT_inc_simple(io)); PL_statname = newSV(0); /* last filename we did stat on */ @@ -4632,9 +4006,9 @@ S_init_postdump_symbols(pTHX_ register int argc, register char **argv, register PERL_ARGS_ASSERT_INIT_POSTDUMP_SYMBOLS; PL_toptarget = newSV_type(SVt_PVFM); - sv_setpvn(PL_toptarget, "", 0); + sv_setpvs(PL_toptarget, ""); PL_bodytarget = newSV_type(SVt_PVFM); - sv_setpvn(PL_bodytarget, "", 0); + sv_setpvs(PL_bodytarget, ""); PL_formtarget = PL_bodytarget; TAINT; @@ -4647,7 +4021,11 @@ S_init_postdump_symbols(pTHX_ register int argc, register char **argv, register sv_setpv(GvSV(tmpgv),MacPerl_MPWFileName(PL_origfilename)); #else sv_setpv(GvSV(tmpgv),PL_origfilename); - magicname("0", "0", 1); + { + GV * const gv = gv_fetchpv("0", GV_ADD, SVt_PV); + if (gv) + sv_magic(GvSV(gv), MUTABLE_SV(gv), PERL_MAGIC_sv, "0", 1); + } #endif } if ((PL_envgv = gv_fetchpvs("ENV", GV_ADD|GV_NOTQUAL, SVt_PVHV))) { @@ -4675,18 +4053,21 @@ S_init_postdump_symbols(pTHX_ register int argc, register char **argv, register environ[0] = NULL; } if (env) { - char *s; + char *s, *old_var; SV *sv; for (; *env; env++) { - if (!(s = strchr(*env,'=')) || s == *env) + old_var = *env; + + if (!(s = strchr(old_var,'=')) || s == old_var) continue; + #if defined(MSDOS) && !defined(DJGPP) *s = '\0'; - (void)strupr(*env); + (void)strupr(old_var); *s = '='; #endif sv = newSVpv(s+1, 0); - (void)hv_store(hv, *env, s - *env, sv, 0); + (void)hv_store(hv, old_var, s - old_var, sv, 0); if (env_is_not_environ) mg_set(sv); } @@ -4706,12 +4087,12 @@ S_init_postdump_symbols(pTHX_ register int argc, register char **argv, register /* touch @F array to prevent spurious warnings 20020415 MJD */ if (PL_minus_a) { - (void) get_av("main::F", TRUE | GV_ADDMULTI); + (void) get_av("main::F", GV_ADD | GV_ADDMULTI); } } STATIC void -S_init_perllib(pTHX) +S_init_perllib(pTHX_ U32 old_vers) { dVAR; char *s; @@ -4728,9 +4109,9 @@ S_init_perllib(pTHX) #else if (s) #endif - incpush(s, TRUE, TRUE, TRUE, FALSE); - else - incpush(PerlEnv_getenv("PERLLIB"), FALSE, FALSE, TRUE, FALSE); + incpush_use_sep(s, old_vers ? old_vers : INCPUSH_ADD_SUB_DIRS); + else if (!old_vers) + incpush_use_sep(PerlEnv_getenv("PERLLIB"), 0); #else /* VMS */ /* Treat PERL5?LIB as a possible search list logical name -- the * "natural" VMS idiom for a Unix path string. We allow each @@ -4739,9 +4120,12 @@ S_init_perllib(pTHX) char buf[256]; int idx = 0; if (my_trnlnm("PERL5LIB",buf,0)) - do { incpush(buf,TRUE,TRUE,TRUE,FALSE); } while (my_trnlnm("PERL5LIB",buf,++idx)); - else - while (my_trnlnm("PERLLIB",buf,idx++)) incpush(buf,FALSE,FALSE,TRUE,FALSE); + do { + incpush_use_sep(buf, old_vers ? old_vers : INCPUSH_ADD_SUB_DIRS); + } while (my_trnlnm("PERL5LIB",buf,++idx)); + else if (!old_vers) + while (my_trnlnm("PERLLIB",buf,idx++)) + incpush_use_sep(buf, 0); #endif /* VMS */ } @@ -4749,90 +4133,114 @@ S_init_perllib(pTHX) ARCHLIB PRIVLIB SITEARCH SITELIB VENDORARCH and VENDORLIB */ #ifdef APPLLIB_EXP - incpush(APPLLIB_EXP, TRUE, TRUE, TRUE, TRUE); + if (!old_vers) { + incpush_use_sep(APPLLIB_EXP, INCPUSH_ADD_SUB_DIRS|INCPUSH_CAN_RELOCATE); + } else { + incpush_use_sep(APPLLIB_EXP, old_vers|INCPUSH_CAN_RELOCATE); + } #endif -#ifdef ARCHLIB_EXP - incpush(ARCHLIB_EXP, FALSE, FALSE, TRUE, TRUE); -#endif + if (!old_vers) { #ifdef MACOS_TRADITIONAL - { Stat_t tmpstatbuf; SV * privdir = newSV(0); char * macperl = PerlEnv_getenv("MACPERL"); if (!macperl) macperl = ""; + +# ifdef ARCHLIB_EXP + if (!old_vers) + incpush_use_sep(ARCHLIB_EXP, INCPUSH_CAN_RELOCATE); +# endif Perl_sv_setpvf(aTHX_ privdir, "%slib:", macperl); if (PerlLIO_stat(SvPVX(privdir), &tmpstatbuf) >= 0 && S_ISDIR(tmpstatbuf.st_mode)) - incpush(SvPVX(privdir), TRUE, FALSE, TRUE, FALSE); + incpush_use_sep(SvPVX(privdir), INCPUSH_ADD_SUB_DIRS); Perl_sv_setpvf(aTHX_ privdir, "%ssite_perl:", macperl); if (PerlLIO_stat(SvPVX(privdir), &tmpstatbuf) >= 0 && S_ISDIR(tmpstatbuf.st_mode)) - incpush(SvPVX(privdir), TRUE, FALSE, TRUE, FALSE); + incpush_use_sep(SvPVX(privdir), INCPUSH_ADD_SUB_DIRS); SvREFCNT_dec(privdir); - } - if (!PL_tainting) - incpush(":", FALSE, FALSE, TRUE, FALSE); -#else -#ifndef PRIVLIB_EXP -# define PRIVLIB_EXP "/usr/local/lib/perl5:/usr/local/lib/perl" -#endif -#if defined(WIN32) - incpush(PRIVLIB_EXP, TRUE, FALSE, TRUE, TRUE); + if (!PL_tainting) + S_incpush(aTHX_ STR_WITH_LEN(":"), 0); #else - incpush(PRIVLIB_EXP, FALSE, FALSE, TRUE, TRUE); -#endif #ifdef SITEARCH_EXP /* sitearch is always relative to sitelib on Windows for * DLL-based path intuition to work correctly */ # if !defined(WIN32) - incpush(SITEARCH_EXP, FALSE, FALSE, TRUE, TRUE); + incpush_use_sep(SITEARCH_EXP, INCPUSH_CAN_RELOCATE); # endif #endif #ifdef SITELIB_EXP # if defined(WIN32) /* this picks up sitearch as well */ - incpush(SITELIB_EXP, TRUE, FALSE, TRUE, TRUE); + incpush_use_sep(SITELIB_EXP, INCPUSH_ADD_SUB_DIRS|INCPUSH_CAN_RELOCATE); # else - incpush(SITELIB_EXP, FALSE, FALSE, TRUE, TRUE); + incpush_use_sep(SITELIB_EXP, INCPUSH_CAN_RELOCATE); # endif #endif + } #if defined(SITELIB_STEM) && defined(PERL_INC_VERSION_LIST) /* Search for version-specific dirs below here */ - incpush(SITELIB_STEM, FALSE, TRUE, TRUE, TRUE); + incpush_use_sep(SITELIB_STEM, old_vers|INCPUSH_CAN_RELOCATE); #endif + if (!old_vers) { #ifdef PERL_VENDORARCH_EXP /* vendorarch is always relative to vendorlib on Windows for * DLL-based path intuition to work correctly */ # if !defined(WIN32) - incpush(PERL_VENDORARCH_EXP, FALSE, FALSE, TRUE, TRUE); + incpush_use_sep(PERL_VENDORARCH_EXP, INCPUSH_CAN_RELOCATE); # endif #endif #ifdef PERL_VENDORLIB_EXP # if defined(WIN32) - incpush(PERL_VENDORLIB_EXP, TRUE, FALSE, TRUE, TRUE); /* this picks up vendorarch as well */ + /* this picks up vendorarch as well */ + incpush_use_sep(PERL_VENDORLIB_EXP, + INCPUSH_ADD_SUB_DIRS|INCPUSH_CAN_RELOCATE); # else - incpush(PERL_VENDORLIB_EXP, FALSE, FALSE, TRUE, TRUE); + incpush_use_sep(PERL_VENDORLIB_EXP, INCPUSH_CAN_RELOCATE); # endif #endif + } -#ifdef PERL_VENDORLIB_STEM /* Search for version-specific dirs below here */ - incpush(PERL_VENDORLIB_STEM, FALSE, TRUE, TRUE, TRUE); +#if defined(PERL_VENDORLIB_STEM) && defined(PERL_INC_VERSION_LIST) + /* Search for version-specific dirs below here */ + incpush_use_sep(PERL_VENDORLIB_STEM, old_vers|INCPUSH_CAN_RELOCATE); +#endif + + if (!old_vers) +#ifdef ARCHLIB_EXP + incpush_use_sep(ARCHLIB_EXP, INCPUSH_CAN_RELOCATE); +#endif + +#ifndef PRIVLIB_EXP +# define PRIVLIB_EXP "/usr/local/lib/perl5:/usr/local/lib/perl" +#endif + +#if defined(WIN32) + incpush_use_sep(PRIVLIB_EXP, INCPUSH_ADD_SUB_DIRS|INCPUSH_CAN_RELOCATE); +#else + incpush_use_sep(PRIVLIB_EXP, INCPUSH_CAN_RELOCATE); #endif #ifdef PERL_OTHERLIBDIRS - incpush(PERL_OTHERLIBDIRS, TRUE, TRUE, TRUE, TRUE); + if (!old_vers) { + incpush_use_sep(PERL_OTHERLIBDIRS, INCPUSH_ADD_SUB_DIRS + |INCPUSH_CAN_RELOCATE); + } else { + incpush_use_sep(PERL_OTHERLIBDIRS, old_vers|INCPUSH_CAN_RELOCATE); + } #endif - if (!PL_tainting) - incpush(".", FALSE, FALSE, TRUE, FALSE); + /* old_vers should be true, so that this last of all. */ + if (!PL_tainting && old_vers) + S_incpush(aTHX_ STR_WITH_LEN("."), 0); #endif /* MACOS_TRADITIONAL */ } @@ -4857,7 +4265,7 @@ S_init_perllib(pTHX) Generate a new SV if we do this, to save needing to copy the SV we push onto @INC */ STATIC SV * -S_incpush_if_exists(pTHX_ SV *dir) +S_incpush_if_exists(pTHX_ AV *const av, SV *dir) { dVAR; Stat_t tmpstatbuf; @@ -4866,50 +4274,56 @@ S_incpush_if_exists(pTHX_ SV *dir) if (PerlLIO_stat(SvPVX_const(dir), &tmpstatbuf) >= 0 && S_ISDIR(tmpstatbuf.st_mode)) { - av_push(GvAVn(PL_incgv), dir); + av_push(av, dir); dir = newSV(0); } return dir; } STATIC void -S_incpush(pTHX_ const char *dir, bool addsubdirs, bool addoldvers, bool usesep, - bool canrelocate) +S_incpush(pTHX_ const char *const dir, STRLEN len, U32 flags) { dVAR; + const U8 addsubdirs = flags & INCPUSH_ADD_SUB_DIRS; + const U8 addoldvers = flags & INCPUSH_ADD_OLD_VERS; + const U8 canrelocate = flags & INCPUSH_CAN_RELOCATE; + const U8 unshift = flags & INCPUSH_UNSHIFT; + const U8 push_basedir = (flags & INCPUSH_NOT_BASEDIR) ? 0 : 1; SV *subdir = NULL; - const char *p = dir; + AV *inc; - if (!p || !*p) + if (!dir || !*dir) return; + inc = GvAVn(PL_incgv); + if (addsubdirs || addoldvers) { subdir = newSV(0); } - /* Break at all separators */ - while (p && *p) { - SV *libdir = newSV(0); - const char *s; - - /* skip any consecutive separators */ - if (usesep) { - while ( *p == PERLLIB_SEP ) { - /* Uncomment the next line for PATH semantics */ - /* av_push(GvAVn(PL_incgv), newSVpvs(".")); */ - p++; - } + { + SV *libdir; + /* Change 20189146be79a0596543441fa369c6bf7f85103f, to fix RT#6665, + arranged to unshift #! line -I onto the front of @INC. However, + -I can add version and architecture specific libraries, and they + need to go first. The old code assumed that it was always + pushing. Hence to make it work, need to push the architecture + (etc) libraries onto a temporary array, then "unshift" that onto + the front of @INC. */ + AV *const av + = (addsubdirs || addoldvers) ? (unshift ? newAV() : inc) : NULL; + + if (len) { + /* I am not convinced that this is valid when PERLLIB_MANGLE is + defined to so something (in os2/os2.c), but the code has been + this way, ignoring any possible changed of length, since + 760ac839baf413929cd31cc32ffd6dba6b781a81 (5.003_02) so I'll leave + it be. */ + libdir = newSVpvn(PERLLIB_MANGLE(dir, len), len); + } else { + libdir = newSVpv(PERLLIB_MANGLE(dir, 0), 0); } - if ( usesep && (s = strchr(p, PERLLIB_SEP)) != NULL ) { - sv_setpvn(libdir, PERLLIB_MANGLE(p, (STRLEN)(s - p)), - (STRLEN)(s - p)); - p = s + 1; - } - else { - sv_setpv(libdir, PERLLIB_MANGLE(p, 0)); - p = NULL; /* break out */ - } #ifdef MACOS_TRADITIONAL if (!strchr(SvPVX(libdir), ':')) { char buf[256]; @@ -5058,19 +4472,19 @@ S_incpush(pTHX_ const char *dir, bool addsubdirs, bool addoldvers, bool usesep, SVfARG(libdir), (int)PERL_REVISION, (int)PERL_VERSION, (int)PERL_SUBVERSION, ARCHNAME); - subdir = S_incpush_if_exists(aTHX_ subdir); + subdir = S_incpush_if_exists(aTHX_ av, subdir); /* .../version if -d .../version */ Perl_sv_setpvf(aTHX_ subdir, "%"SVf PERL_ARCH_FMT_PATH, SVfARG(libdir), (int)PERL_REVISION, (int)PERL_VERSION, (int)PERL_SUBVERSION); - subdir = S_incpush_if_exists(aTHX_ subdir); + subdir = S_incpush_if_exists(aTHX_ av, subdir); /* .../archname if -d .../archname */ Perl_sv_setpvf(aTHX_ subdir, "%"SVf PERL_ARCH_FMT, SVfARG(libdir), ARCHNAME); - subdir = S_incpush_if_exists(aTHX_ subdir); + subdir = S_incpush_if_exists(aTHX_ av, subdir); } @@ -5080,14 +4494,41 @@ S_incpush(pTHX_ const char *dir, bool addsubdirs, bool addoldvers, bool usesep, /* .../xxx if -d .../xxx */ Perl_sv_setpvf(aTHX_ subdir, "%"SVf PERL_ARCH_FMT, SVfARG(libdir), *incver); - subdir = S_incpush_if_exists(aTHX_ subdir); + subdir = S_incpush_if_exists(aTHX_ av, subdir); } } #endif } - /* finally push this lib directory on the end of @INC */ - av_push(GvAVn(PL_incgv), libdir); + /* finally add this lib directory at the end of @INC */ + if (unshift) { + U32 extra = av_len(av) + 1; + av_unshift(inc, extra + push_basedir); + if (push_basedir) + av_store(inc, extra, libdir); + while (extra--) { + /* av owns a reference, av_store() expects to be donated a + reference, and av expects to be sane when it's cleared. + If I wanted to be naughty and wrong, I could peek inside the + implementation of av_clear(), realise that it uses + SvREFCNT_dec() too, so av's array could be a run of NULLs, + and so directly steal from it (with a memcpy() to inc, and + then memset() to NULL them out. But people copy code from the + core expecting it to be best practise, so let's use the API. + Although studious readers will note that I'm not checking any + return codes. */ + av_store(inc, extra, SvREFCNT_inc(*av_fetch(av, extra, FALSE))); + } + SvREFCNT_dec(av); + } + else if (push_basedir) { + av_push(inc, libdir); + } + + if (!push_basedir) { + assert (SvREFCNT(libdir) == 1); + SvREFCNT_dec(libdir); + } } if (subdir) { assert (SvREFCNT(subdir) == 1); @@ -5095,6 +4536,33 @@ S_incpush(pTHX_ const char *dir, bool addsubdirs, bool addoldvers, bool usesep, } } +STATIC void +S_incpush_use_sep(pTHX_ const char *p, U32 flags) +{ + /* This logic has been broken out from S_incpush(). It may be possible to + simplify it. */ + + /* Break at all separators */ + while (p && *p) { + const char *s; + + /* skip any consecutive separators */ + while ( *p == PERLLIB_SEP ) { + /* Uncomment the next line for PATH semantics */ + /* av_push(GvAVn(PL_incgv), newSVpvs(".")); */ + p++; + } + + if ((s = strchr(p, PERLLIB_SEP)) != NULL ) { + incpush(p, (STRLEN)(s - p), flags); + p = s + 1; + } + else { + incpush(p, 0, flags); + return; + } + } +} void Perl_call_list(pTHX_ I32 oldscope, AV *paramList) @@ -5110,19 +4578,19 @@ Perl_call_list(pTHX_ I32 oldscope, AV *paramList) PERL_ARGS_ASSERT_CALL_LIST; while (av_len(paramList) >= 0) { - cv = (CV*)av_shift(paramList); + cv = MUTABLE_CV(av_shift(paramList)); if (PL_savebegin) { if (paramList == PL_beginav) { /* save PL_beginav for compiler */ - Perl_av_create_and_push(aTHX_ &PL_beginav_save, (SV*)cv); + Perl_av_create_and_push(aTHX_ &PL_beginav_save, MUTABLE_SV(cv)); } else if (paramList == PL_checkav) { /* save PL_checkav for compiler */ - Perl_av_create_and_push(aTHX_ &PL_checkav_save, (SV*)cv); + Perl_av_create_and_push(aTHX_ &PL_checkav_save, MUTABLE_SV(cv)); } else if (paramList == PL_unitcheckav) { /* save PL_unitcheckav for compiler */ - Perl_av_create_and_push(aTHX_ &PL_unitcheckav_save, (SV*)cv); + Perl_av_create_and_push(aTHX_ &PL_unitcheckav_save, MUTABLE_SV(cv)); } } else { if (!PL_madskills) @@ -5230,22 +4698,34 @@ Perl_my_failure_exit(pTHX) */ if (MY_POSIX_EXIT) { - /* In POSIX_EXIT mode follow Perl documentations and use 255 for - * the exit code when there isn't an error. - */ + /* According to the die_exit.t tests, if errno is non-zero */ + /* It should be used for the error status. */ - if (STATUS_UNIX == 0) - STATUS_UNIX_EXIT_SET(255); - else { - STATUS_UNIX_EXIT_SET(STATUS_UNIX); + if (errno == EVMSERR) { + STATUS_NATIVE = vaxc$errno; + } else { - /* The exit code could have been set by $? or vmsish which - * means that it may not be fatal. So convert - * success/warning codes to fatal. - */ - if ((STATUS_NATIVE & (STS$K_SEVERE|STS$K_ERROR)) == 0) + /* According to die_exit.t tests, if the child_exit code is */ + /* also zero, then we need to exit with a code of 255 */ + if ((errno != 0) && (errno < 256)) + STATUS_UNIX_EXIT_SET(errno); + else if (STATUS_UNIX < 255) { STATUS_UNIX_EXIT_SET(255); + } + } + + /* The exit code could have been set by $? or vmsish which + * means that it may not have fatal set. So convert + * success/warning codes to fatal with out changing + * the POSIX status code. The severity makes VMS native + * status handling work, while UNIX mode programs use the + * the POSIX exit codes. + */ + if ((STATUS_NATIVE & (STS$K_SEVERE|STS$K_ERROR)) == 0) { + STATUS_NATIVE &= STS$M_COND_ID; + STATUS_NATIVE |= STS$K_ERROR | STS$M_INHIB_MSG; + } } else { /* Traditionally Perl on VMS always expects a Fatal Error. */