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
