Really check that sysread(I, $x, 1, -4) dies with "Offset outside string"
[p5sagit/p5-mst-13.2.git] / t / op / taint.t
old mode 100755 (executable)
new mode 100644 (file)
index b544262..0ac02a6
@@ -17,14 +17,17 @@ use Config;
 use File::Spec::Functions;
 
 BEGIN { require './test.pl'; }
-plan tests => 245;
-
+plan tests => 301;
 
 $| = 1;
 
 use vars qw($ipcsysv); # did we manage to load IPC::SysV?
 
+my ($old_env_path, $old_env_dcl_path, $old_env_term);
 BEGIN {
+   $old_env_path = $ENV{'PATH'};
+   $old_env_dcl_path = $ENV{'DCL$PATH'};
+   $old_env_term = $ENV{'TERM'};
   if ($^O eq 'VMS' && !defined($Config{d_setenv})) {
       $ENV{PATH} = $ENV{PATH};
       $ENV{TERM} = $ENV{TERM} ne ''? $ENV{TERM} : 'dummy';
@@ -45,7 +48,8 @@ my $Is_MSWin32  = $^O eq 'MSWin32';
 my $Is_NetWare  = $^O eq 'NetWare';
 my $Is_Dos      = $^O eq 'dos';
 my $Is_Cygwin   = $^O eq 'cygwin';
-my $Invoke_Perl = $Is_VMS      ? 'MCR Sys$Disk:[]Perl.' :
+my $Is_OpenBSD  = $^O eq 'openbsd';
+my $Invoke_Perl = $Is_VMS      ? 'MCR Sys$Disk:[]Perl.exe' :
                   $Is_MSWin32  ? '.\perl'               :
                   $Is_MacOS    ? ':perl'                :
                   $Is_NetWare  ? 'perl'                 : 
@@ -57,11 +61,22 @@ if ($Is_VMS) {
     for $x ('DCL$PATH', @MoreEnv) {
        ($old{$x}) = $ENV{$x} =~ /^(.*)$/ if exists $ENV{$x};
     }
+    # VMS note:  PATH and TERM are automatically created by the C
+    # library in VMS on reference to the their keys in %ENV.
+    # There is currently no way to determine if they did not exist
+    # before this test was run.
     eval <<EndOfCleanup;
        END {
-           \$ENV{PATH} = '' if $Config{d_setenv};
-           warn "# Note: logical name 'PATH' may have been deleted\n";
+           \$ENV{PATH} = \$old_env_path;
+           warn "# Note: logical name 'PATH' may have been created\n";
+           \$ENV{'TERM'} = \$old_env_term;
+           warn "# Note: logical name 'TERM' may have been created\n";
            \@ENV{keys %old} = values %old;
+           if (defined \$old_env_dcl_path) {
+               \$ENV{'DCL\$PATH'} = \$old_env_dcl_path;
+           } else {
+               delete \$ENV{'DCL\$PATH'};
+           }
        }
 EndOfCleanup
 }
@@ -150,27 +165,10 @@ my $TEST = catfile(curdir(), 'TEST');
            };
        }
     }
-
-    $ENV{PATH} = '';
+    $ENV{PATH} = ($Is_Cygwin) ? '/usr/bin' : '';
     delete @ENV{@MoreEnv};
     $ENV{TERM} = 'dumb';
 
-    if ($Is_Cygwin && ! -f 'cygwin1.dll') {
-       system("/usr/bin/cp /usr/bin/cygwin1.dll .") &&
-           die "$0: failed to cp cygwin1.dll: $!\n";
-       eval q{
-           END { unlink "cygwin1.dll" }
-       };
-    }
-
-    if ($Is_Cygwin && ! -f 'cygcrypt-0.dll' && -f '/usr/bin/cygcrypt-0.dll') {
-       system("/usr/bin/cp /usr/bin/cygcrypt-0.dll .") &&
-           die "$0: failed to cp cygcrypt-0.dll: $!\n";
-       eval q{
-           END { unlink "cygcrypt-0.dll" }
-       };
-    }
-
     test eval { `$echo 1` } eq "1\n";
 
     SKIP: {
@@ -287,7 +285,7 @@ my $TEST = catfile(curdir(), 'TEST');
 # How about command-line arguments? The problem is that we don't
 # always get some, so we'll run another process with some.
 SKIP: {
-    my $arg = catfile(curdir(), "arg$$");
+    my $arg = tempfile();
     open PROG, "> $arg" or die "Can't create $arg: $!";
     print PROG q{
        eval { join('', @ARGV), kill 0 };
@@ -420,8 +418,7 @@ SKIP: {
     test !eval { require $foo }, 'require';
     test $@ =~ /^Insecure dependency/, $@;
 
-    my $filename = "./taintB$$";       # NB: $filename isn't tainted!
-    END { unlink $filename if defined $filename }
+    my $filename = tempfile(); # NB: $filename isn't tainted!
     $foo = $filename . $TAINT;
     unlink $filename;  # in any case
 
@@ -508,8 +505,7 @@ SKIP: {
        my $foo = "x" x 979;
        taint_these $foo;
        local *FOO;
-       my $temp = "./taintC$$";
-       END { unlink $temp }
+       my $temp = tempfile();
        test open(FOO, "> $temp"), "Couldn't open $temp for write: $!";
 
        test !eval { ioctl FOO, $TAINT0, $foo }, 'ioctl';
@@ -1148,3 +1144,206 @@ TERNARY_CONDITIONALS: {
     }
     cmp_ok $i, '<', 10000, "infinite m//g";
 }
+
+SKIP:
+{
+    my $got_dualvar;
+    eval 'use Scalar::Util "dualvar"; $got_dualvar++';
+    skip "No Scalar::Util::dualvar" unless $got_dualvar;
+    my $a = Scalar::Util::dualvar(3, $^X);
+    my $b = $a + 5;
+    is ($b, 8, "Arithmetic on tainted dualvars works");
+}
+
+# opening '|-' should not trigger $ENV{PATH} check
+
+{
+    SKIP: {
+       skip "fork() is not available", 3 unless $Config{'d_fork'};
+       skip "opening |- is not stable on threaded OpenBSD with taint", 3
+            if $Config{useithreads} && $Is_OpenBSD;
+
+       $ENV{'PATH'} = $TAINT;
+       local $SIG{'PIPE'} = 'IGNORE';
+       eval {
+           my $pid = open my $pipe, '|-';
+           if (!defined $pid) {
+               die "open failed: $!";
+           }
+           if (!$pid) {
+               kill 'KILL', $$;        # child suicide
+           }
+           close $pipe;
+       };
+       test $@ !~ /Insecure \$ENV/, 'fork triggers %ENV check';
+       test $@ eq '',               'pipe/fork/open/close failed';
+       eval {
+           open my $pipe, "|$Invoke_Perl -e 1";
+           close $pipe;
+       };
+       test $@ =~ /Insecure \$ENV/, 'popen neglects %ENV check';
+    }
+}
+
+{
+    package AUTOLOAD_TAINT;
+    sub AUTOLOAD {
+        our $AUTOLOAD;
+        return if $AUTOLOAD =~ /DESTROY/;
+        if ($AUTOLOAD =~ /untainted/) {
+            main::ok(!main::tainted($AUTOLOAD), '$AUTOLOAD can be untainted');
+        } else {
+            main::ok(main::tainted($AUTOLOAD), '$AUTOLOAD can be tainted');
+        }
+    }
+
+    package main;
+    my $o = bless [], 'AUTOLOAD_TAINT';
+    $o->$TAINT;
+    $o->untainted;
+}
+
+{
+    # tests for tainted format in s?printf
+    eval { printf($TAINT . "# %s\n", "foo") };
+    like($@, qr/^Insecure dependency in printf/, q/printf doesn't like tainted formats/);
+    eval { printf("# %s\n", $TAINT . "foo") };
+    ok(!$@, q/printf accepts other tainted args/);
+    eval { sprintf($TAINT . "# %s\n", "foo") };
+    like($@, qr/^Insecure dependency in sprintf/, q/sprintf doesn't like tainted formats/);
+    eval { sprintf("# %s\n", $TAINT . "foo") };
+    ok(!$@, q/sprintf accepts other tainted args/);
+}
+
+{
+    # 40708
+    my $n  = 7e9;
+    8e9 - $n;
+
+    my $val = $n;
+    is ($val, '7000000000', 'Assignment to untainted variable');
+    $val = $TAINT;
+    $val = $n;
+    is ($val, '7000000000', 'Assignment to tainted variable');
+}
+
+{
+    my $val = 0;
+    my $tainted = '1' . $TAINT;
+    eval '$val = eval $tainted;';
+    is ($val, 0, "eval doesn't like tainted strings");
+    like ($@, qr/^Insecure dependency in eval/);
+
+    # Rather nice code to get a tainted undef by from Rick Delaney
+    open FH, "test.pl" or die $!;
+    seek FH, 0, 2 or die $!;
+    $tainted = <FH>;
+
+    eval 'eval $tainted';
+    like ($@, qr/^Insecure dependency in eval/);
+}
+
+foreach my $ord (78, 163, 256) {
+    # 47195
+    my $line = 'A1' . $TAINT . chr $ord;
+    chop $line;
+    is($line, 'A1');
+    $line =~ /(A\S*)/;
+    ok(!tainted($1), "\\S match with chr $ord");
+}
+
+{
+    # 59998
+    sub cr { my $x = crypt($_[0], $_[1]); $x }
+    sub co { my $x = ~$_[0]; $x }
+    my ($a, $b);
+    $a = cr('hello', 'foo' . $TAINT);
+    $b = cr('hello', 'foo');
+    ok(tainted($a),  "tainted crypt");
+    ok(!tainted($b), "untainted crypt");
+    $a = co('foo' . $TAINT);
+    $b = co('foo');
+    ok(tainted($a),  "tainted complement");
+    ok(!tainted($b), "untainted complement");
+}
+
+{
+    my @data = qw(bonk zam zlonk qunckkk);
+    # Clearly some sort of usenet bang-path
+    my $string = $TAINT . join "!", @data;
+
+    ok(tainted($string), "tainted data");
+
+    my @got = split /!|,/, $string;
+
+    # each @got would be useful here, but I want the test for earlier perls
+    for my $i (0 .. $#data) {
+       ok(tainted($got[$i]), "tainted result $i");
+       is($got[$i], $data[$i], "correct content $i");
+    }
+
+    ok(tainted($string), "still tainted data");
+
+    my @got = split /[!,]/, $string;
+
+    # each @got would be useful here, but I want the test for earlier perls
+    for my $i (0 .. $#data) {
+       ok(tainted($got[$i]), "tainted result $i");
+       is($got[$i], $data[$i], "correct content $i");
+    }
+
+    ok(tainted($string), "still tainted data");
+
+    my @got = split /!/, $string;
+
+    # each @got would be useful here, but I want the test for earlier perls
+    for my $i (0 .. $#data) {
+       ok(tainted($got[$i]), "tainted result $i");
+       is($got[$i], $data[$i], "correct content $i");
+    }
+}
+
+# Bug RT #52552 - broken by change at git commit id f337b08
+{
+    my $x = $TAINT. q{print "Hello world\n"};
+    my $y = pack "a*", $x;
+    ok(tainted($y), "pack a* preserves tainting");
+
+    my $z = pack "A*", q{print "Hello world\n"}.$TAINT;
+    ok(tainted($z), "pack A* preserves tainting");
+
+    my $zz = pack "a*a*", q{print "Hello world\n"}, $TAINT;
+    ok(tainted($zz), "pack a*a* preserves tainting");
+}
+
+# This may bomb out with the alarm signal so keep it last
+SKIP: {
+    skip "No alarm()"  unless $Config{d_alarm};
+    # Test from RT #41831]
+    # [PATCH] Bug & fix: hang when using study + taint mode (perl 5.6.1, 5.8.x)
+
+    my $DATA = <<'END' . $TAINT;
+line1 is here
+line2 is here
+line3 is here
+line4 is here
+
+END
+
+    #study $DATA;
+
+    ## don't set $SIG{ALRM}, since we'd never get to a user-level handler as
+    ## perl is stuck in a regexp infinite loop!
+
+    alarm(10);
+
+    if ($DATA =~ /^line2.*line4/m) {
+       fail("Should not be a match")
+    } else {
+       pass("Match on tainted multiline data should fail promptly");
+    }
+
+    alarm(0);
+}
+__END__
+# Keep the previous test last