In perl.git, the branch maint-5.24 has been updated <http://perl5.git.perl.org/perl.git/commitdiff/1addf2f85380133ce4aa5f2f1d35bac377e0d90a?hp=f8379d9f8dd3aac0eabe12edb11ed26281903ce7>
- Log ----------------------------------------------------------------- commit 1addf2f85380133ce4aa5f2f1d35bac377e0d90a Author: Father Chrysostomos <[email protected]> Date: Thu Feb 23 08:34:07 2017 +0000 Fix checks for tainted dir in $ENV{PATH} $ cat > foo print "What?!\n" ^D $ chmod +x foo $ ./perl -Ilib -Te '$ENV{PATH}="."; exec "foo"' Insecure directory in $ENV{PATH} while running with -T switch at -e line 1. That is what I expect to see. But: $ ./perl -Ilib -Te '$ENV{PATH}="/\\:."; exec "foo"' What?! Perl is allowing the \ to escape the :, but the \ is not treated as an escape by the system, allowing a relative path in PATH to be consid- ered safe. (cherry picked from commit ba0a4150f6f1604df236035adf6df18bd43de88e) M embed.fnc M embed.h M mg.c M proto.h M t/op/taint.t M util.c commit 93e39480947573cb85e287907a745faf061002f6 Author: Karl Williamson <[email protected]> Date: Sat Aug 27 19:16:17 2016 -0600 PATCH: [perl #129038] Crash with s///l The cause of this was bad logic. It thought it was dealing with UTF-8 when it wasn't. (cherry picked from commit 109ac342a6bc5a3a67c3b52341607100cedafdf7) M regexec.c M t/re/subst.t ----------------------------------------------------------------------- Summary of changes: embed.fnc | 4 ++++ embed.h | 1 + mg.c | 2 +- proto.h | 3 +++ regexec.c | 27 +++++++++++++++------------ t/op/taint.t | 18 +++++++++++++++++- t/re/subst.t | 19 ++++++++++++++++++- util.c | 25 ++++++++++++++++++++++--- 8 files changed, 81 insertions(+), 18 deletions(-) diff --git a/embed.fnc b/embed.fnc index a64ffbac74..d59eb35bc9 100644 --- a/embed.fnc +++ b/embed.fnc @@ -344,6 +344,10 @@ Ap |I32 |debstackptrs pR |SV * |defelem_target |NN SV *sv|NULLOK MAGIC *mg Anp |char* |delimcpy |NN char* to|NN const char* toend|NN const char* from \ |NN const char* fromend|int delim|NN I32* retlen +np |char* |delimcpy_no_escape|NN char* to|NN const char* toend \ + |NN const char* from \ + |NN const char* fromend|int delim \ + |NN I32* retlen : Used in op.c, perl.c pM |void |delete_eval_scope Aprd |OP* |die_sv |NN SV *baseex diff --git a/embed.h b/embed.h index 42c65b2eb0..5b2998d79e 100644 --- a/embed.h +++ b/embed.h @@ -1206,6 +1206,7 @@ #define deb_stack_all() Perl_deb_stack_all(aTHX) #define defelem_target(a,b) Perl_defelem_target(aTHX_ a,b) #define delete_eval_scope() Perl_delete_eval_scope(aTHX) +#define delimcpy_no_escape Perl_delimcpy_no_escape #define die_unwind(a) Perl_die_unwind(aTHX_ a) #define do_aexec5(a,b,c,d,e) Perl_do_aexec5(aTHX_ a,b,c,d,e) #define do_dump_pad(a,b,c,d) Perl_do_dump_pad(aTHX_ a,b,c,d) diff --git a/mg.c b/mg.c index 4321a401ad..1c43c9dedb 100644 --- a/mg.c +++ b/mg.c @@ -1259,7 +1259,7 @@ Perl_magic_setenv(pTHX_ SV *sv, MAGIC *mg) #else const char path_sep = ':'; #endif - s = delimcpy(tmpbuf, tmpbuf + sizeof tmpbuf, + s = delimcpy_no_escape(tmpbuf, tmpbuf + sizeof tmpbuf, s, strend, path_sep, &i); s++; if (i >= (I32)sizeof tmpbuf /* too long -- assume the worst */ diff --git a/proto.h b/proto.h index fb4ee29508..8d4713b096 100644 --- a/proto.h +++ b/proto.h @@ -659,6 +659,9 @@ PERL_CALLCONV void Perl_delete_eval_scope(pTHX); PERL_CALLCONV char* Perl_delimcpy(char* to, const char* toend, const char* from, const char* fromend, int delim, I32* retlen); #define PERL_ARGS_ASSERT_DELIMCPY \ assert(to); assert(toend); assert(from); assert(fromend); assert(retlen) +PERL_CALLCONV char* Perl_delimcpy_no_escape(char* to, const char* toend, const char* from, const char* fromend, int delim, I32* retlen); +#define PERL_ARGS_ASSERT_DELIMCPY_NO_ESCAPE \ + assert(to); assert(toend); assert(from); assert(fromend); assert(retlen) PERL_CALLCONV void Perl_despatch_signals(pTHX); PERL_CALLCONV_NO_RET OP* Perl_die(pTHX_ const char* pat, ...) __attribute__noreturn__ diff --git a/regexec.c b/regexec.c index cdaa95cce5..5735b997fd 100644 --- a/regexec.c +++ b/regexec.c @@ -6191,23 +6191,26 @@ S_regmatch(pTHX_ regmatch_info *reginfo, char *startpos, regnode *prog) if (! (to_complement ^ cBOOL(isFOO_lc(FLAGS(scan), (U8) nextchr)))) { sayNO; } + + locinput++; + break; } - else if (UTF8_IS_DOWNGRADEABLE_START(nextchr)) { - if (! (to_complement ^ cBOOL(isFOO_lc(FLAGS(scan), - EIGHT_BIT_UTF8_TO_NATIVE(nextchr, - *(locinput + 1)))))) - { - sayNO; - } - } - else { /* Here, must be an above Latin-1 code point */ + + if (! UTF8_IS_DOWNGRADEABLE_START(nextchr)) { /* An above Latin-1 code point */ _CHECK_AND_OUTPUT_WIDE_LOCALE_UTF8_MSG(locinput, reginfo->strend); goto utf8_posix_above_latin1; } - /* Here, must be utf8 */ - locinput += UTF8SKIP(locinput); - break; + /* Here is a UTF-8 variant code point below 256 and the target is + * UTF-8 */ + if (! (to_complement ^ cBOOL(isFOO_lc(FLAGS(scan), + EIGHT_BIT_UTF8_TO_NATIVE(nextchr, + *(locinput + 1)))))) + { + sayNO; + } + + goto increment_locinput; case NPOSIXD: /* \W or [:^punct:] etc. under /d */ to_complement = 1; diff --git a/t/op/taint.t b/t/op/taint.t index 101c6da427..846ac23f0d 100644 --- a/t/op/taint.t +++ b/t/op/taint.t @@ -17,7 +17,7 @@ BEGIN { use strict; use Config; -plan tests => 808; +plan tests => 812; $| = 1; @@ -187,6 +187,22 @@ my $TEST = 'TEST'; like($@, qr/^Insecure (?:directory in )?\$ENV\{PATH}/); } + # Relative paths in $ENV{PATH} are always implicitly tainted. + SKIP: { + skip "Do these work on VMS?", 4 if $Is_VMS; + skip "Not applicable to DOSish systems", 4 if! $tmp; + + local $ENV{PATH} = '.'; + is(eval { `$echo 1` }, undef); + like($@, qr/^Insecure (?:directory in )?\$ENV\{PATH}/); + + # Backslash should not fool perl into thinking that this is one + # path. + local $ENV{PATH} = '/\:.'; + is(eval { `$echo 1` }, undef); + like($@, qr/^Insecure (?:directory in )?\$ENV\{PATH}/); + } + SKIP: { skip "This is not VMS", 4 unless $Is_VMS; diff --git a/t/re/subst.t b/t/re/subst.t index 26a78c774d..49d28e9689 100644 --- a/t/re/subst.t +++ b/t/re/subst.t @@ -11,7 +11,7 @@ BEGIN { require './loc_tools.pl'; } -plan( tests => 270 ); +plan( tests => 271 ); $_ = 'david'; $a = s/david/rules/r; @@ -1102,3 +1102,20 @@ SKIP: { $s =~ s/..\G//g; is($s, "\x{123}", "#RT 126260 gofs"); } + +SKIP: { + if (! locales_enabled('LC_CTYPE')) { + skip "Can't test locale", 1; + } + + # To cause breakeage, we need a locale in which \xff matches whatever + # POSIX class is used in the pattern. Easiest is C, with \W. + fresh_perl_is(' use POSIX qw(locale_h); + setlocale(&POSIX::LC_CTYPE, "C"); + my $s = "\xff"; + $s =~ s/\W//l; + print qq(ok$s\n)', + "ok\n", + {stderr => 1 }, + '[perl #129038 ] s/\xff//l no longer crashes'); +} diff --git a/util.c b/util.c index 89c44e735d..b64e87dd32 100644 --- a/util.c +++ b/util.c @@ -524,15 +524,17 @@ Free_t Perl_mfree (Malloc_t where) /* copy a string up to some (non-backslashed) delimiter, if any */ -char * -Perl_delimcpy(char *to, const char *toend, const char *from, const char *fromend, int delim, I32 *retlen) +static char * +S_delimcpy(char *to, const char *toend, const char *from, + const char *fromend, int delim, I32 *retlen, + const bool allow_escape) { I32 tolen; PERL_ARGS_ASSERT_DELIMCPY; for (tolen = 0; from < fromend; from++, tolen++) { - if (*from == '\\') { + if (allow_escape && *from == '\\') { if (from[1] != delim) { if (to < toend) *to++ = *from; @@ -551,6 +553,23 @@ Perl_delimcpy(char *to, const char *toend, const char *from, const char *fromend return (char *)from; } +char * +Perl_delimcpy(char *to, const char *toend, const char *from, const char *fromend, int delim, I32 *retlen) +{ + PERL_ARGS_ASSERT_DELIMCPY; + + return S_delimcpy(to, toend, from, fromend, delim, retlen, 1); +} + +char * +Perl_delimcpy_no_escape(char *to, const char *toend, const char *from, + const char *fromend, int delim, I32 *retlen) +{ + PERL_ARGS_ASSERT_DELIMCPY_NO_ESCAPE; + + return S_delimcpy(to, toend, from, fromend, delim, retlen, 0); +} + /* return ptr to little string in big string, NULL if not found */ /* This routine was donated by Corey Satten. */ -- Perl5 Master Repository
