In perl.git, the branch blead has been updated

<http://perl5.git.perl.org/perl.git/commitdiff/c73f612f8535d5db75d096a430a7938ce1c28a10?hp=9ce5bf4c39e28441410672f39b5ee1c4569967f8>

- Log -----------------------------------------------------------------
commit c73f612f8535d5db75d096a430a7938ce1c28a10
Author: David Mitchell <[email protected]>
Date:   Wed Nov 2 16:05:54 2016 +0000

    fix taint handling in list assignment
    
    My recent commit v5.25.6-79-gb09ed99 reworked list assignment, and
    accidentally broke taint handling at the same time.
    
    The basic idea is that each element is independent; in:
    
        ($a, $b, ...) = ($tainted, $untainted, ...);
    
    $a should end up tainted, $b should end up untainted, the statement
    containing the assign should remain untainted, and if the statement was
    already tainted it shouldn't affect the assign.
    
    Surprisingly this is completely untested, which is why I failed to spot it
    when I broke it.
    
    Now fixed. In fact in addition I spotted something that had always been
    broken, and fixed that too: it was tainting the rest of the statement; in:
    
        (($a) = ($TAINT. "x")), ($b = $b . "x");
    
    The taint in the list assign to $a was lingering to mess up and taint $b.
    
    Prior to v5.25.6-79-gb09ed99 , pp_assign looked roughly like:
    
        for (...each lhs elem...) {
            TAINT_NOT;
            switch (lhs type) {
            case scalar:
                assign a value to lhs;
                break;
            case SVt_PVAV:
                av_clear();
                for (...each rhs elem...)
                    sv = newSV(0);
                    sv_setsv(sv, rhs_elem);
                    av_store(av, i, sv);
                    TAINT_NOT;
                }
                break;
            }
            case SVt_PVHV:
                ...similarly...
        }
    
    Commit v5.25.6-79-gb09ed99 accidentally removed *all* the TAINT_NOT's.
    
    This commit re-adds the first TAINT_NOT, but doesn't re-add the
    per-array/hash TAINT_NOT's, on the grounds that the aggregates are first
    emptied, so any elements being assigned to will be fresh and can't have
    taint magic attached, so calling mg_set() on them won't set the taint
    value to 1 even if PL_tainted is set.
    
    But this commit does add an extra TAINT_NOT *after* the outer loop, which
    is what I think is fixing a longstanding bug.
-----------------------------------------------------------------------

Summary of changes:
 pp_hot.c     |  7 +++++++
 t/op/taint.t | 42 +++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 48 insertions(+), 1 deletion(-)

diff --git a/pp_hot.c b/pp_hot.c
index 068b902..3db6f5d 100644
--- a/pp_hot.c
+++ b/pp_hot.c
@@ -1321,6 +1321,8 @@ PP(pp_aassign)
        bool alias = FALSE;
        SV *lsv = *lelem++;
 
+        TAINT_NOT; /* Each item stands on its own, taintwise. */
+
         assert(relem <= lastrelem);
        if (UNLIKELY(!lsv)) {
            alias = TRUE;
@@ -1731,6 +1733,9 @@ PP(pp_aassign)
     /* simplified lelem loop for when there are no relems left */
     while (LIKELY(lelem <= lastlelem)) {
        SV *lsv = *lelem++;
+
+        TAINT_NOT; /* Each item stands on its own, taintwise. */
+
        if (UNLIKELY(!lsv)) {
            lsv = *lelem++;
            ASSUME(SvTYPE(lsv) == SVt_PVAV);
@@ -1760,6 +1765,8 @@ PP(pp_aassign)
         } /* switch */
     } /* while */
 
+    TAINT_NOT; /* result of list assign isn't tainted */
+
     if (UNLIKELY(PL_delaymagic & ~DM_DELAY)) {
        /* Will be used to set PL_tainting below */
        Uid_t tmp_uid  = PerlProc_getuid();
diff --git a/t/op/taint.t b/t/op/taint.t
index ca0a58b..cf9055b 100644
--- a/t/op/taint.t
+++ b/t/op/taint.t
@@ -17,7 +17,7 @@ BEGIN {
 use strict;
 use Config;
 
-plan tests => 812;
+plan tests => 826;
 
 $| = 1;
 
@@ -2407,6 +2407,46 @@ is eval { eval $::x.1 }, 1, 'reset does not taint undef';
     isnt($x, 1); # it should be 1.1, not 1
 }
 
+# RT #129996
+# every item in a list assignment is independent, even if the lvalue
+# has taint magic already
+{
+    my ($a, $b, $c, $d);
+    $d = "";
+    $b = $TAINT;
+    ($a, $b, $c) = ($TAINT, 0, 0);
+    is_tainted   $a, "list assign tainted a";
+    isnt_tainted $b, "list assign tainted b";
+    isnt_tainted $c, "list assign tainted c";
+
+    $b = $TAINT;
+    $b = ""; # untaint;
+    ($a, $b, $c) = ($TAINT, 0, 0);
+    is_tainted   $a, "list assign detainted a";
+    isnt_tainted $b, "list assign detainted b";
+    isnt_tainted $c, "list assign detainted c";
+
+    $b = $TAINT;
+    $b = ""; # untaint;
+    ($a, $b, $c) = ($TAINT);
+    is_tainted   $a, "list assign empty rhs a";
+    isnt_tainted $b, "list assign empty rhs b";
+    isnt_tainted $c, "list assign empty rhs c";
+
+    $b = $TAINT;
+    $b = ""; # untaint;
+    ($a = ($TAINT. "x")), (($b, $c) = (0));
+    is_tainted   $a, "list assign already tainted expression a";
+    isnt_tainted $b, "list assign already tainted expression b";
+    isnt_tainted $c, "list assign already tainted expression c";
+
+    $b = $TAINT;
+    $b = ""; # untaint;
+    (($a) = ($TAINT. "x")), ($b = $b . "x");
+    is_tainted   $a, "list assign post tainted expression a";
+    isnt_tainted $b, "list assign post tainted expression b";
+}
+
 
 # This may bomb out with the alarm signal so keep it last
 SKIP: {

--
Perl5 Master Repository

Reply via email to