This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
[perl #38709] Opening '|-' triggers unjustified taint check
[perl5.git] / t / op / taint.t
1 #!./perl -T
2 #
3 # Taint tests by Tom Phoenix <rootbeer@teleport.com>.
4 #
5 # I don't claim to know all about tainting. If anyone sees
6 # tests that I've missed here, please add them. But this is
7 # better than having no tests at all, right?
8 #
9
10 BEGIN {
11     chdir 't' if -d 't';
12     @INC = '../lib';
13 }
14
15 use strict;
16 use Config;
17 use File::Spec::Functions;
18
19 BEGIN { require './test.pl'; }
20 plan tests => 249;
21
22 $| = 1;
23
24 use vars qw($ipcsysv); # did we manage to load IPC::SysV?
25
26 BEGIN {
27   if ($^O eq 'VMS' && !defined($Config{d_setenv})) {
28       $ENV{PATH} = $ENV{PATH};
29       $ENV{TERM} = $ENV{TERM} ne ''? $ENV{TERM} : 'dummy';
30   }
31   if ($Config{'extensions'} =~ /\bIPC\/SysV\b/
32       && ($Config{d_shm} || $Config{d_msg})) {
33       eval { require IPC::SysV };
34       unless ($@) {
35           $ipcsysv++;
36           IPC::SysV->import(qw(IPC_PRIVATE IPC_RMID IPC_CREAT S_IRWXU IPC_NOWAIT));
37       }
38   }
39 }
40
41 my $Is_MacOS    = $^O eq 'MacOS';
42 my $Is_VMS      = $^O eq 'VMS';
43 my $Is_MSWin32  = $^O eq 'MSWin32';
44 my $Is_NetWare  = $^O eq 'NetWare';
45 my $Is_Dos      = $^O eq 'dos';
46 my $Is_Cygwin   = $^O eq 'cygwin';
47 my $Invoke_Perl = $Is_VMS      ? 'MCR Sys$Disk:[]Perl.' :
48                   $Is_MSWin32  ? '.\perl'               :
49                   $Is_MacOS    ? ':perl'                :
50                   $Is_NetWare  ? 'perl'                 : 
51                                  './perl'               ;
52 my @MoreEnv = qw/IFS CDPATH ENV BASH_ENV/;
53
54 if ($Is_VMS) {
55     my (%old, $x);
56     for $x ('DCL$PATH', @MoreEnv) {
57         ($old{$x}) = $ENV{$x} =~ /^(.*)$/ if exists $ENV{$x};
58     }
59     eval <<EndOfCleanup;
60         END {
61             \$ENV{PATH} = '' if $Config{d_setenv};
62             warn "# Note: logical name 'PATH' may have been deleted\n";
63             \@ENV{keys %old} = values %old;
64         }
65 EndOfCleanup
66 }
67
68 # Sources of taint:
69 #   The empty tainted value, for tainting strings
70 my $TAINT = substr($^X, 0, 0);
71 #   A tainted zero, useful for tainting numbers
72 my $TAINT0;
73 {
74     no warnings;
75     $TAINT0 = 0 + $TAINT;
76 }
77
78 # This taints each argument passed. All must be lvalues.
79 # Side effect: It also stringifies them. :-(
80 sub taint_these (@) {
81     for (@_) { $_ .= $TAINT }
82 }
83
84 # How to identify taint when you see it
85 sub any_tainted (@) {
86     not eval { join("",@_), kill 0; 1 };
87 }
88 sub tainted ($) {
89     any_tainted @_;
90 }
91 sub all_tainted (@) {
92     for (@_) { return 0 unless tainted $_ }
93     1;
94 }
95
96
97 sub test ($;$) {
98     my($ok, $diag) = @_;
99
100     my $curr_test = curr_test();
101
102     if ($ok) {
103         print "ok $curr_test\n";
104     } else {
105         print "not ok $curr_test\n";
106         printf "# Failed test at line %d\n", (caller)[2];
107         for (split m/^/m, $diag) {
108             print "# $_";
109         }
110         print "\n" unless
111             $diag eq ''
112             or substr($diag, -1) eq "\n";
113     }
114
115     next_test();
116
117     return $ok;
118 }
119
120 # We need an external program to call.
121 my $ECHO = ($Is_MSWin32 ? ".\\echo$$" : $Is_MacOS ? ":echo$$" : ($Is_NetWare ? "echo$$" : "./echo$$"));
122 END { unlink $ECHO }
123 open PROG, "> $ECHO" or die "Can't create $ECHO: $!";
124 print PROG 'print "@ARGV\n"', "\n";
125 close PROG;
126 my $echo = "$Invoke_Perl $ECHO";
127
128 my $TEST = catfile(curdir(), 'TEST');
129
130 # First, let's make sure that Perl is checking the dangerous
131 # environment variables. Maybe they aren't set yet, so we'll
132 # taint them ourselves.
133 {
134     $ENV{'DCL$PATH'} = '' if $Is_VMS;
135
136     if ($Is_MSWin32 && $Config{ccname} =~ /bcc32/ && ! -f 'cc3250mt.dll') {
137         my $bcc_dir;
138         foreach my $dir (split /$Config{path_sep}/, $ENV{PATH}) {
139             if (-f "$dir/cc3250mt.dll") {
140                 $bcc_dir = $dir and last;
141             }
142         }
143         if (defined $bcc_dir) {
144             require File::Copy;
145             File::Copy::copy("$bcc_dir/cc3250mt.dll", '.') or
146                 die "$0: failed to copy cc3250mt.dll: $!\n";
147             eval q{
148                 END { unlink "cc3250mt.dll" }
149             };
150         }
151     }
152
153     $ENV{PATH} = '';
154     delete @ENV{@MoreEnv};
155     $ENV{TERM} = 'dumb';
156
157     if ($Is_Cygwin && ! -f 'cygwin1.dll') {
158         system("/usr/bin/cp /usr/bin/cygwin1.dll .") &&
159             die "$0: failed to cp cygwin1.dll: $!\n";
160         eval q{
161             END { unlink "cygwin1.dll" }
162         };
163     }
164
165     if ($Is_Cygwin && ! -f 'cygcrypt-0.dll' && -f '/usr/bin/cygcrypt-0.dll') {
166         system("/usr/bin/cp /usr/bin/cygcrypt-0.dll .") &&
167             die "$0: failed to cp cygcrypt-0.dll: $!\n";
168         eval q{
169             END { unlink "cygcrypt-0.dll" }
170         };
171     }
172
173     test eval { `$echo 1` } eq "1\n";
174
175     SKIP: {
176         skip "Environment tainting tests skipped", 4
177           if $Is_MSWin32 || $Is_NetWare || $Is_VMS || $Is_Dos || $Is_MacOS;
178
179         my @vars = ('PATH', @MoreEnv);
180         while (my $v = $vars[0]) {
181             local $ENV{$v} = $TAINT;
182             last if eval { `$echo 1` };
183             last unless $@ =~ /^Insecure \$ENV{$v}/;
184             shift @vars;
185         }
186         test !@vars, "@vars";
187
188         # tainted $TERM is unsafe only if it contains metachars
189         local $ENV{TERM};
190         $ENV{TERM} = 'e=mc2';
191         test eval { `$echo 1` } eq "1\n";
192         $ENV{TERM} = 'e=mc2' . $TAINT;
193         test !eval { `$echo 1` };
194         test $@ =~ /^Insecure \$ENV{TERM}/, $@;
195     }
196
197     my $tmp;
198     if ($^O eq 'os2' || $^O eq 'amigaos' || $Is_MSWin32 || $Is_NetWare || $Is_Dos) {
199         print "# all directories are writeable\n";
200     }
201     else {
202         $tmp = (grep { defined and -d and (stat _)[2] & 2 }
203                      qw(sys$scratch /tmp /var/tmp /usr/tmp),
204                      @ENV{qw(TMP TEMP)})[0]
205             or print "# can't find world-writeable directory to test PATH\n";
206     }
207
208     SKIP: {
209         skip "all directories are writeable", 2 unless $tmp;
210
211         local $ENV{PATH} = $tmp;
212         test !eval { `$echo 1` };
213         test $@ =~ /^Insecure directory in \$ENV{PATH}/, $@;
214     }
215
216     SKIP: {
217         skip "This is not VMS", 4 unless $Is_VMS;
218
219         $ENV{'DCL$PATH'} = $TAINT;
220         test  eval { `$echo 1` } eq '';
221         test $@ =~ /^Insecure \$ENV{DCL\$PATH}/, $@;
222         SKIP: {
223             skip q[can't find world-writeable directory to test DCL$PATH], 2
224               unless $tmp;
225
226             $ENV{'DCL$PATH'} = $tmp;
227             test eval { `$echo 1` } eq '';
228             test $@ =~ /^Insecure directory in \$ENV{DCL\$PATH}/, $@;
229         }
230         $ENV{'DCL$PATH'} = '';
231     }
232 }
233
234 # Let's see that we can taint and untaint as needed.
235 {
236     my $foo = $TAINT;
237     test tainted $foo;
238
239     # That was a sanity check. If it failed, stop the insanity!
240     die "Taint checks don't seem to be enabled" unless tainted $foo;
241
242     $foo = "foo";
243     test not tainted $foo;
244
245     taint_these($foo);
246     test tainted $foo;
247
248     my @list = 1..10;
249     test not any_tainted @list;
250     taint_these @list[1,3,5,7,9];
251     test any_tainted @list;
252     test all_tainted @list[1,3,5,7,9];
253     test not any_tainted @list[0,2,4,6,8];
254
255     ($foo) = $foo =~ /(.+)/;
256     test not tainted $foo;
257
258     $foo = $1 if ('bar' . $TAINT) =~ /(.+)/;
259     test not tainted $foo;
260     test $foo eq 'bar';
261
262     {
263       use re 'taint';
264
265       ($foo) = ('bar' . $TAINT) =~ /(.+)/;
266       test tainted $foo;
267       test $foo eq 'bar';
268
269       $foo = $1 if ('bar' . $TAINT) =~ /(.+)/;
270       test tainted $foo;
271       test $foo eq 'bar';
272     }
273
274     $foo = $1 if 'bar' =~ /(.+)$TAINT/;
275     test tainted $foo;
276     test $foo eq 'bar';
277
278     my $pi = 4 * atan2(1,1) + $TAINT0;
279     test tainted $pi;
280
281     ($pi) = $pi =~ /(\d+\.\d+)/;
282     test not tainted $pi;
283     test sprintf("%.5f", $pi) eq '3.14159';
284 }
285
286 # How about command-line arguments? The problem is that we don't
287 # always get some, so we'll run another process with some.
288 SKIP: {
289     my $arg = catfile(curdir(), "arg$$");
290     open PROG, "> $arg" or die "Can't create $arg: $!";
291     print PROG q{
292         eval { join('', @ARGV), kill 0 };
293         exit 0 if $@ =~ /^Insecure dependency/;
294         print "# Oops: \$@ was [$@]\n";
295         exit 1;
296     };
297     close PROG;
298     print `$Invoke_Perl "-T" $arg and some suspect arguments`;
299     test !$?, "Exited with status $?";
300     unlink $arg;
301 }
302
303 # Reading from a file should be tainted
304 {
305     test open(FILE, $TEST), "Couldn't open '$TEST': $!";
306
307     my $block;
308     sysread(FILE, $block, 100);
309     my $line = <FILE>;
310     close FILE;
311     test tainted $block;
312     test tainted $line;
313 }
314
315 # Globs should be forbidden, except under VMS,
316 #   which doesn't spawn an external program.
317 SKIP: {
318     skip "globs should be forbidden", 2 if 1 or $Is_VMS;
319
320     my @globs = eval { <*> };
321     test @globs == 0 && $@ =~ /^Insecure dependency/;
322
323     @globs = eval { glob '*' };
324     test @globs == 0 && $@ =~ /^Insecure dependency/;
325 }
326
327 # Output of commands should be tainted
328 {
329     my $foo = `$echo abc`;
330     test tainted $foo;
331 }
332
333 # Certain system variables should be tainted
334 {
335     test all_tainted $^X, $0;
336 }
337
338 # Results of matching should all be untainted
339 {
340     my $foo = "abcdefghi" . $TAINT;
341     test tainted $foo;
342
343     $foo =~ /def/;
344     test not any_tainted $`, $&, $';
345
346     $foo =~ /(...)(...)(...)/;
347     test not any_tainted $1, $2, $3, $+;
348
349     my @bar = $foo =~ /(...)(...)(...)/;
350     test not any_tainted @bar;
351
352     test tainted $foo;  # $foo should still be tainted!
353     test $foo eq "abcdefghi";
354 }
355
356 # Operations which affect files can't use tainted data.
357 {
358     test !eval { chmod 0, $TAINT }, 'chmod';
359     test $@ =~ /^Insecure dependency/, $@;
360
361     # There is no feature test in $Config{} for truncate,
362     #   so we allow for the possibility that it's missing.
363     test !eval { truncate 'NoSuChFiLe', $TAINT0 }, 'truncate';
364     test $@ =~ /^(?:Insecure dependency|truncate not implemented)/, $@;
365
366     test !eval { rename '', $TAINT }, 'rename';
367     test $@ =~ /^Insecure dependency/, $@;
368
369     test !eval { unlink $TAINT }, 'unlink';
370     test $@ =~ /^Insecure dependency/, $@;
371
372     test !eval { utime $TAINT }, 'utime';
373     test $@ =~ /^Insecure dependency/, $@;
374
375     SKIP: {
376         skip "chown() is not available", 2 unless $Config{d_chown};
377
378         test !eval { chown -1, -1, $TAINT }, 'chown';
379         test $@ =~ /^Insecure dependency/, $@;
380     }
381
382     SKIP: {
383         skip "link() is not available", 2 unless $Config{d_link};
384
385         test !eval { link $TAINT, '' }, 'link';
386         test $@ =~ /^Insecure dependency/, $@;
387     }
388
389     SKIP: {
390         skip "symlink() is not available", 2 unless $Config{d_symlink};
391
392         test !eval { symlink $TAINT, '' }, 'symlink';
393         test $@ =~ /^Insecure dependency/, $@;
394     }
395 }
396
397 # Operations which affect directories can't use tainted data.
398 {
399     test !eval { mkdir "foo".$TAINT, 0755.$TAINT0 }, 'mkdir';
400     test $@ =~ /^Insecure dependency/, $@;
401
402     test !eval { rmdir $TAINT }, 'rmdir';
403     test $@ =~ /^Insecure dependency/, $@;
404
405     test !eval { chdir "foo".$TAINT }, 'chdir';
406     test $@ =~ /^Insecure dependency/, $@;
407
408     SKIP: {
409         skip "chroot() is not available", 2 unless $Config{d_chroot};
410
411         test !eval { chroot $TAINT }, 'chroot';
412         test $@ =~ /^Insecure dependency/, $@;
413     }
414 }
415
416 # Some operations using files can't use tainted data.
417 {
418     my $foo = "imaginary library" . $TAINT;
419     test !eval { require $foo }, 'require';
420     test $@ =~ /^Insecure dependency/, $@;
421
422     my $filename = "./taintB$$";        # NB: $filename isn't tainted!
423     END { unlink $filename if defined $filename }
424     $foo = $filename . $TAINT;
425     unlink $filename;   # in any case
426
427     test !eval { open FOO, $foo }, 'open for read';
428     test $@ eq '', $@;          # NB: This should be allowed
429
430     # Try first new style but allow also old style.
431     # We do not want the whole taint.t to fail
432     # just because Errno possibly failing.
433     test eval('$!{ENOENT}') ||
434         $! == 2 || # File not found
435         ($Is_Dos && $! == 22) ||
436         ($^O eq 'mint' && $! == 33);
437
438     test !eval { open FOO, "> $foo" }, 'open for write';
439     test $@ =~ /^Insecure dependency/, $@;
440 }
441
442 # Commands to the system can't use tainted data
443 {
444     my $foo = $TAINT;
445
446     SKIP: {
447         skip "open('|') is not available", 4 if $^O eq 'amigaos';
448
449         test !eval { open FOO, "| x$foo" }, 'popen to';
450         test $@ =~ /^Insecure dependency/, $@;
451
452         test !eval { open FOO, "x$foo |" }, 'popen from';
453         test $@ =~ /^Insecure dependency/, $@;
454     }
455
456     test !eval { exec $TAINT }, 'exec';
457     test $@ =~ /^Insecure dependency/, $@;
458
459     test !eval { system $TAINT }, 'system';
460     test $@ =~ /^Insecure dependency/, $@;
461
462     $foo = "*";
463     taint_these $foo;
464
465     test !eval { `$echo 1$foo` }, 'backticks';
466     test $@ =~ /^Insecure dependency/, $@;
467
468     SKIP: {
469         # wildcard expansion doesn't invoke shell on VMS, so is safe
470         skip "This is not VMS", 2 unless $Is_VMS;
471     
472         test join('', eval { glob $foo } ) ne '', 'globbing';
473         test $@ eq '', $@;
474     }
475 }
476
477 # Operations which affect processes can't use tainted data.
478 {
479     test !eval { kill 0, $TAINT }, 'kill';
480     test $@ =~ /^Insecure dependency/, $@;
481
482     SKIP: {
483         skip "setpgrp() is not available", 2 unless $Config{d_setpgrp};
484
485         test !eval { setpgrp 0, $TAINT0 }, 'setpgrp';
486         test $@ =~ /^Insecure dependency/, $@;
487     }
488
489     SKIP: {
490         skip "setpriority() is not available", 2 unless $Config{d_setprior};
491
492         test !eval { setpriority 0, $TAINT0, $TAINT0 }, 'setpriority';
493         test $@ =~ /^Insecure dependency/, $@;
494     }
495 }
496
497 # Some miscellaneous operations can't use tainted data.
498 {
499     SKIP: {
500         skip "syscall() is not available", 2 unless $Config{d_syscall};
501
502         test !eval { syscall $TAINT }, 'syscall';
503         test $@ =~ /^Insecure dependency/, $@;
504     }
505
506     {
507         my $foo = "x" x 979;
508         taint_these $foo;
509         local *FOO;
510         my $temp = "./taintC$$";
511         END { unlink $temp }
512         test open(FOO, "> $temp"), "Couldn't open $temp for write: $!";
513
514         test !eval { ioctl FOO, $TAINT0, $foo }, 'ioctl';
515         test $@ =~ /^Insecure dependency/, $@;
516
517         SKIP: {
518             skip "fcntl() is not available", 2 unless $Config{d_fcntl};
519
520             test !eval { fcntl FOO, $TAINT0, $foo }, 'fcntl';
521             test $@ =~ /^Insecure dependency/, $@;
522         }
523
524         close FOO;
525     }
526 }
527
528 # Some tests involving references
529 {
530     my $foo = 'abc' . $TAINT;
531     my $fooref = \$foo;
532     test not tainted $fooref;
533     test tainted $$fooref;
534     test tainted $foo;
535 }
536
537 # Some tests involving assignment
538 {
539     my $foo = $TAINT0;
540     my $bar = $foo;
541     test all_tainted $foo, $bar;
542     test tainted($foo = $bar);
543     test tainted($bar = $bar);
544     test tainted($bar += $bar);
545     test tainted($bar -= $bar);
546     test tainted($bar *= $bar);
547     test tainted($bar++);
548     test tainted($bar /= $bar);
549     test tainted($bar += 0);
550     test tainted($bar -= 2);
551     test tainted($bar *= -1);
552     test tainted($bar /= 1);
553     test tainted($bar--);
554     test $bar == 0;
555 }
556
557 # Test assignment and return of lists
558 {
559     my @foo = ("A", "tainted" . $TAINT, "B");
560     test not tainted $foo[0];
561     test     tainted $foo[1];
562     test not tainted $foo[2];
563     my @bar = @foo;
564     test not tainted $bar[0];
565     test     tainted $bar[1];
566     test not tainted $bar[2];
567     my @baz = eval { "A", "tainted" . $TAINT, "B" };
568     test not tainted $baz[0];
569     test     tainted $baz[1];
570     test not tainted $baz[2];
571     my @plugh = eval q[ "A", "tainted" . $TAINT, "B" ];
572     test not tainted $plugh[0];
573     test     tainted $plugh[1];
574     test not tainted $plugh[2];
575     my $nautilus = sub { "A", "tainted" . $TAINT, "B" };
576     test not tainted ((&$nautilus)[0]);
577     test     tainted ((&$nautilus)[1]);
578     test not tainted ((&$nautilus)[2]);
579     my @xyzzy = &$nautilus;
580     test not tainted $xyzzy[0];
581     test     tainted $xyzzy[1];
582     test not tainted $xyzzy[2];
583     my $red_october = sub { return "A", "tainted" . $TAINT, "B" };
584     test not tainted ((&$red_october)[0]);
585     test     tainted ((&$red_october)[1]);
586     test not tainted ((&$red_october)[2]);
587     my @corge = &$red_october;
588     test not tainted $corge[0];
589     test     tainted $corge[1];
590     test not tainted $corge[2];
591 }
592
593 # Test for system/library calls returning string data of dubious origin.
594 {
595     # No reliable %Config check for getpw*
596     SKIP: {
597         skip "getpwent() is not available", 1 unless 
598           eval { setpwent(); getpwent() };
599
600         setpwent();
601         my @getpwent = getpwent();
602         die "getpwent: $!\n" unless (@getpwent);
603         test (    not tainted $getpwent[0]
604                   and     tainted $getpwent[1]
605                   and not tainted $getpwent[2]
606                   and not tainted $getpwent[3]
607                   and not tainted $getpwent[4]
608                   and not tainted $getpwent[5]
609                   and     tainted $getpwent[6]          # ge?cos
610                   and not tainted $getpwent[7]
611                   and     tainted $getpwent[8]);        # shell
612         endpwent();
613     }
614
615     SKIP: {
616         # pretty hard to imagine not
617         skip "readdir() is not available", 1 unless $Config{d_readdir};
618
619         local(*D);
620         opendir(D, "op") or die "opendir: $!\n";
621         my $readdir = readdir(D);
622         test tainted $readdir;
623         closedir(D);
624     }
625
626     SKIP: {
627         skip "readlink() or symlink() is not available" unless 
628           $Config{d_readlink} && $Config{d_symlink};
629
630         my $symlink = "sl$$";
631         unlink($symlink);
632         my $sl = "/something/naughty";
633         # it has to be a real path on Mac OS
634         $sl = MacPerl::MakePath((MacPerl::Volumes())[0]) if $Is_MacOS;
635         symlink($sl, $symlink) or die "symlink: $!\n";
636         my $readlink = readlink($symlink);
637         test tainted $readlink;
638         unlink($symlink);
639     }
640 }
641
642 # test bitwise ops (regression bug)
643 {
644     my $why = "y";
645     my $j = "x" | $why;
646     test not tainted $j;
647     $why = $TAINT."y";
648     $j = "x" | $why;
649     test     tainted $j;
650 }
651
652 # test target of substitution (regression bug)
653 {
654     my $why = $TAINT."y";
655     $why =~ s/y/z/;
656     test     tainted $why;
657
658     my $z = "[z]";
659     $why =~ s/$z/zee/;
660     test     tainted $why;
661
662     $why =~ s/e/'-'.$$/ge;
663     test     tainted $why;
664 }
665
666
667 SKIP: {
668     skip "no IPC::SysV", 2 unless $ipcsysv;
669
670     # test shmread
671     SKIP: {
672         skip "shm*() not available", 1 unless $Config{d_shm};
673
674         no strict 'subs';
675         my $sent = "foobar";
676         my $rcvd;
677         my $size = 2000;
678         my $id = shmget(IPC_PRIVATE, $size, S_IRWXU);
679
680         if (defined $id) {
681             if (shmwrite($id, $sent, 0, 60)) {
682                 if (shmread($id, $rcvd, 0, 60)) {
683                     substr($rcvd, index($rcvd, "\0")) = '';
684                 } else {
685                     warn "# shmread failed: $!\n";
686                 }
687             } else {
688                 warn "# shmwrite failed: $!\n";
689             }
690             shmctl($id, IPC_RMID, 0) or warn "# shmctl failed: $!\n";
691         } else {
692             warn "# shmget failed: $!\n";
693         }
694
695         skip "SysV shared memory operation failed", 1 unless 
696           $rcvd eq $sent;
697
698         test tainted $rcvd;
699     }
700
701
702     # test msgrcv
703     SKIP: {
704         skip "msg*() not available", 1 unless $Config{d_msg};
705
706         no strict 'subs';
707         my $id = msgget(IPC_PRIVATE, IPC_CREAT | S_IRWXU);
708
709         my $sent      = "message";
710         my $type_sent = 1234;
711         my $rcvd;
712         my $type_rcvd;
713
714         if (defined $id) {
715             if (msgsnd($id, pack("l! a*", $type_sent, $sent), IPC_NOWAIT)) {
716                 if (msgrcv($id, $rcvd, 60, 0, IPC_NOWAIT)) {
717                     ($type_rcvd, $rcvd) = unpack("l! a*", $rcvd);
718                 } else {
719                     warn "# msgrcv failed: $!\n";
720                 }
721             } else {
722                 warn "# msgsnd failed: $!\n";
723             }
724             msgctl($id, IPC_RMID, 0) or warn "# msgctl failed: $!\n";
725         } else {
726             warn "# msgget failed\n";
727         }
728
729         SKIP: {
730             skip "SysV message queue operation failed", 1
731               unless $rcvd eq $sent && $type_sent == $type_rcvd;
732
733             test tainted $rcvd;
734         }
735     }
736 }
737
738 {
739     # bug id 20001004.006
740
741     open IN, $TEST or warn "$0: cannot read $TEST: $!" ;
742     local $/;
743     my $a = <IN>;
744     my $b = <IN>;
745
746     ok tainted($a) && tainted($b) && !defined($b);
747
748     close IN;
749 }
750
751 {
752     # bug id 20001004.007
753
754     open IN, $TEST or warn "$0: cannot read $TEST: $!" ;
755     my $a = <IN>;
756
757     my $c = { a => 42,
758               b => $a };
759
760     ok !tainted($c->{a}) && tainted($c->{b});
761
762
763     my $d = { a => $a,
764               b => 42 };
765     ok tainted($d->{a}) && !tainted($d->{b});
766
767
768     my $e = { a => 42,
769               b => { c => $a, d => 42 } };
770     ok !tainted($e->{a}) &&
771        !tainted($e->{b}) &&
772         tainted($e->{b}->{c}) &&
773        !tainted($e->{b}->{d});
774
775     close IN;
776 }
777
778 {
779     # bug id 20010519.003
780
781     BEGIN {
782         use vars qw($has_fcntl);
783         eval { require Fcntl; import Fcntl; };
784         unless ($@) {
785             $has_fcntl = 1;
786         }
787     }
788
789     SKIP: {
790         skip "no Fcntl", 18 unless $has_fcntl;
791
792         my $evil = "foo" . $TAINT;
793
794         eval { sysopen(my $ro, $evil, &O_RDONLY) };
795         test $@ !~ /^Insecure dependency/, $@;
796         
797         eval { sysopen(my $wo, $evil, &O_WRONLY) };
798         test $@ =~ /^Insecure dependency/, $@;
799         
800         eval { sysopen(my $rw, $evil, &O_RDWR) };
801         test $@ =~ /^Insecure dependency/, $@;
802         
803         eval { sysopen(my $ap, $evil, &O_APPEND) };
804         test $@ =~ /^Insecure dependency/, $@;
805         
806         eval { sysopen(my $cr, $evil, &O_CREAT) };
807         test $@ =~ /^Insecure dependency/, $@;
808         
809         eval { sysopen(my $tr, $evil, &O_TRUNC) };
810         test $@ =~ /^Insecure dependency/, $@;
811         
812         eval { sysopen(my $ro, "foo", &O_RDONLY | $TAINT0) };
813         test $@ !~ /^Insecure dependency/, $@;
814         
815         eval { sysopen(my $wo, "foo", &O_WRONLY | $TAINT0) };
816         test $@ =~ /^Insecure dependency/, $@;
817
818         eval { sysopen(my $rw, "foo", &O_RDWR | $TAINT0) };
819         test $@ =~ /^Insecure dependency/, $@;
820
821         eval { sysopen(my $ap, "foo", &O_APPEND | $TAINT0) };
822         test $@ =~ /^Insecure dependency/, $@;
823         
824         eval { sysopen(my $cr, "foo", &O_CREAT | $TAINT0) };
825         test $@ =~ /^Insecure dependency/, $@;
826
827         eval { sysopen(my $tr, "foo", &O_TRUNC | $TAINT0) };
828         test $@ =~ /^Insecure dependency/, $@;
829
830         eval { sysopen(my $ro, "foo", &O_RDONLY, $TAINT0) };
831         test $@ !~ /^Insecure dependency/, $@;
832         
833         eval { sysopen(my $wo, "foo", &O_WRONLY, $TAINT0) };
834         test $@ =~ /^Insecure dependency/, $@;
835         
836         eval { sysopen(my $rw, "foo", &O_RDWR, $TAINT0) };
837         test $@ =~ /^Insecure dependency/, $@;
838         
839         eval { sysopen(my $ap, "foo", &O_APPEND, $TAINT0) };
840         test $@ =~ /^Insecure dependency/, $@;
841         
842         eval { sysopen(my $cr, "foo", &O_CREAT, $TAINT0) };
843         test $@ =~ /^Insecure dependency/, $@;
844
845         eval { sysopen(my $tr, "foo", &O_TRUNC, $TAINT0) };
846         test $@ =~ /^Insecure dependency/, $@;
847         
848         unlink("foo"); # not unlink($evil), because that would fail...
849     }
850 }
851
852 {
853     # bug 20010526.004
854
855     use warnings;
856
857     my $saw_warning = 0;
858     local $SIG{__WARN__} = sub { $saw_warning = 1 };
859
860     sub fmi {
861         my $divnum = shift()/1;
862         sprintf("%1.1f\n", $divnum);
863     }
864
865     fmi(21 . $TAINT);
866     fmi(37);
867     fmi(248);
868
869     test !$saw_warning;
870 }
871
872
873 {
874     # Bug ID 20010730.010
875
876     my $i = 0;
877
878     sub Tie::TIESCALAR {
879         my $class =  shift;
880         my $arg   =  shift;
881
882         bless \$arg => $class;
883     }
884
885     sub Tie::FETCH {
886         $i ++;
887         ${$_ [0]}
888     }
889
890  
891     package main;
892  
893     my $bar = "The Big Bright Green Pleasure Machine";
894     taint_these $bar;
895     tie my ($foo), Tie => $bar;
896
897     my $baz = $foo;
898
899     ok $i == 1;
900 }
901
902 {
903     # Check that all environment variables are tainted.
904     my @untainted;
905     while (my ($k, $v) = each %ENV) {
906         if (!tainted($v) &&
907             # These we have explicitly untainted or set earlier.
908             $k !~ /^(BASH_ENV|CDPATH|ENV|IFS|PATH|PERL_CORE|TEMP|TERM|TMP)$/) {
909             push @untainted, "# '$k' = '$v'\n";
910         }
911     }
912     test @untainted == 0, "untainted:\n @untainted";
913 }
914
915
916 ok( ${^TAINT} == 1, '$^TAINT is on' );
917
918 eval { ${^TAINT} = 0 };
919 ok( ${^TAINT},  '$^TAINT is not assignable' );
920 ok( $@ =~ /^Modification of a read-only value attempted/,
921                                 'Assigning to ${^TAINT} fails' );
922
923 {
924     # bug 20011111.105
925     
926     my $re1 = qr/x$TAINT/;
927     test tainted $re1;
928     
929     my $re2 = qr/^$re1\z/;
930     test tainted $re2;
931     
932     my $re3 = "$re2";
933     test tainted $re3;
934 }
935
936 SKIP: {
937     skip "system {} has different semantics on Win32", 1 if $Is_MSWin32;
938
939     # bug 20010221.005
940     local $ENV{PATH} .= $TAINT;
941     eval { system { "echo" } "/arg0", "arg1" };
942     test $@ =~ /^Insecure \$ENV/;
943 }
944
945 TODO: {
946     todo_skip 'tainted %ENV warning occludes tainted arguments warning', 22
947       if $Is_VMS;
948
949     # bug 20020208.005 plus some single arg exec/system extras
950     my $err = qr/^Insecure dependency/ ;
951     test !eval { exec $TAINT, $TAINT }, 'exec';
952     test $@ =~ $err, $@;
953     test !eval { exec $TAINT $TAINT }, 'exec';
954     test $@ =~ $err, $@;
955     test !eval { exec $TAINT $TAINT, $TAINT }, 'exec';
956     test $@ =~ $err, $@;
957     test !eval { exec $TAINT 'notaint' }, 'exec';
958     test $@ =~ $err, $@;
959     test !eval { exec {'notaint'} $TAINT }, 'exec';
960     test $@ =~ $err, $@;
961
962     test !eval { system $TAINT, $TAINT }, 'system';
963     test $@ =~ $err, $@;
964     test !eval { system $TAINT $TAINT }, 'system';
965     test $@ =~ $err, $@;
966     test !eval { system $TAINT $TAINT, $TAINT }, 'system';
967     test $@ =~ $err, $@;
968     test !eval { system $TAINT 'notaint' }, 'system';
969     test $@ =~ $err, $@;
970     test !eval { system {'notaint'} $TAINT }, 'system';
971     test $@ =~ $err, $@;
972
973     eval { 
974         no warnings;
975         system("lskdfj does not exist","with","args"); 
976     };
977     test !$@;
978
979     SKIP: {
980         skip "no exec() on MacOS Classic" if $Is_MacOS;
981
982         eval { 
983             no warnings;
984             exec("lskdfj does not exist","with","args"); 
985         };
986         test !$@;
987     }
988
989     # If you add tests here update also the above skip block for VMS.
990 }
991
992 {
993     # [ID 20020704.001] taint propagation failure
994     use re 'taint';
995     $TAINT =~ /(.*)/;
996     test tainted(my $foo = $1);
997 }
998
999 {
1000     # [perl #24291] this used to dump core
1001     our %nonmagicalenv = ( PATH => "util" );
1002     local *ENV = \%nonmagicalenv;
1003     eval { system("lskdfj"); };
1004     test $@ =~ /^%ENV is aliased to another variable while running with -T switch/;
1005     local *ENV = *nonmagicalenv;
1006     eval { system("lskdfj"); };
1007     test $@ =~ /^%ENV is aliased to %nonmagicalenv while running with -T switch/;
1008 }
1009 {
1010     # [perl #24248]
1011     $TAINT =~ /(.*)/;
1012     test !tainted($1);
1013     my $notaint = $1;
1014     test !tainted($notaint);
1015
1016     my $l;
1017     $notaint =~ /($notaint)/;
1018     $l = $1;
1019     test !tainted($1);
1020     test !tainted($l);
1021     $notaint =~ /($TAINT)/;
1022     $l = $1;
1023     test tainted($1);
1024     test tainted($l);
1025
1026     $TAINT =~ /($notaint)/;
1027     $l = $1;
1028     test !tainted($1);
1029     test !tainted($l);
1030     $TAINT =~ /($TAINT)/;
1031     $l = $1;
1032     test tainted($1);
1033     test tainted($l);
1034
1035     my $r;
1036     ($r = $TAINT) =~ /($notaint)/;
1037     test !tainted($1);
1038     ($r = $TAINT) =~ /($TAINT)/;
1039     test tainted($1);
1040
1041     #  [perl #24674]
1042     # accessing $^O  shoudn't taint it as a side-effect;
1043     # assigning tainted data to it is now an error
1044
1045     test !tainted($^O);
1046     if (!$^X) { } elsif ($^O eq 'bar') { }
1047     test !tainted($^O);
1048     eval '$^O = $^X';
1049     test $@ =~ /Insecure dependency in/;
1050 }
1051
1052 EFFECTIVELY_CONSTANTS: {
1053     my $tainted_number = 12 + $TAINT0;
1054     test tainted( $tainted_number );
1055
1056     # Even though it's always 0, it's still tainted
1057     my $tainted_product = $tainted_number * 0;
1058     test tainted( $tainted_product );
1059     test $tainted_product == 0;
1060 }
1061
1062 TERNARY_CONDITIONALS: {
1063     my $tainted_true  = $TAINT . "blah blah blah";
1064     my $tainted_false = $TAINT0;
1065     test tainted( $tainted_true );
1066     test tainted( $tainted_false );
1067
1068     my $result = $tainted_true ? "True" : "False";
1069     test $result eq "True";
1070     test !tainted( $result );
1071
1072     $result = $tainted_false ? "True" : "False";
1073     test $result eq "False";
1074     test !tainted( $result );
1075
1076     my $untainted_whatever = "The Fabulous Johnny Cash";
1077     my $tainted_whatever = "Soft Cell" . $TAINT;
1078
1079     $result = $tainted_true ? $tainted_whatever : $untainted_whatever;
1080     test $result eq "Soft Cell";
1081     test tainted( $result );
1082
1083     $result = $tainted_false ? $tainted_whatever : $untainted_whatever;
1084     test $result eq "The Fabulous Johnny Cash";
1085     test !tainted( $result );
1086 }
1087
1088 {
1089     # rt.perl.org 5900  $1 remains tainted if...
1090     # 1) The regular expression contains a scalar variable AND
1091     # 2) The regular expression appears in an elsif clause
1092
1093     my $foo = "abcdefghi" . $TAINT;
1094
1095     my $valid_chars = 'a-z';
1096     if ( $foo eq '' ) {
1097     }
1098     elsif ( $foo =~ /([$valid_chars]+)/o ) {
1099         test not tainted $1;
1100     }
1101
1102     if ( $foo eq '' ) {
1103     }
1104     elsif ( my @bar = $foo =~ /([$valid_chars]+)/o ) {
1105         test not any_tainted @bar;
1106     }
1107 }
1108
1109 # at scope exit, a restored localised value should have its old
1110 # taint status, not the taint status of the current statement
1111
1112 {
1113     our $x99 = $^X;
1114     test tainted $x99;
1115
1116     $x99 = '';
1117     test not tainted $x99;
1118
1119     my $c = do { local $x99; $^X };
1120     test not tainted $x99;
1121 }
1122 {
1123     our $x99 = $^X;
1124     test tainted $x99;
1125
1126     my $c = do { local $x99; '' };
1127     test tainted $x99;
1128 }
1129
1130 # an mg_get of a tainted value during localization shouldn't taint the
1131 # statement
1132
1133 {
1134     eval { local $0, eval '1' };
1135     test $@ eq '';
1136 }
1137
1138 # [perl #8262] //g loops infinitely on tainted data
1139
1140 {
1141     my @a;
1142     local $::TODO = 1;
1143     $a[0] = $^X;
1144     my $i = 0;
1145     while($a[0]=~ m/(.)/g ) {
1146         last if $i++ > 10000;
1147     }
1148     cmp_ok $i, '<', 10000, "infinite m//g";
1149 }
1150
1151 SKIP:
1152 {
1153     my $got_dualvar;
1154     eval 'use Scalar::Util "dualvar"; $got_dualvar++';
1155     skip "No Scalar::Util::dualvar" unless $got_dualvar;
1156     my $a = Scalar::Util::dualvar(3, $^X);
1157     my $b = $a + 5;
1158     is ($b, 8, "Arithmetic on tainted dualvars works");
1159 }
1160
1161 # opening '|-' should not trigger $ENV{PATH} check
1162
1163 {
1164     SKIP: {
1165         skip "fork() is not available", 3 unless $Config{'d_fork'};
1166
1167         $ENV{'PATH'} = $TAINT;
1168         local $SIG{'PIPE'} = 'IGNORE';
1169         eval {
1170             my $pid = open my $pipe, '|-';
1171             if (!defined $pid) {
1172                 die "open failed: $!";
1173             }
1174             if (!$pid) {
1175                 kill 'KILL', $$;        # child suicide
1176             }
1177             close $pipe;
1178         };
1179         test $@ !~ /Insecure \$ENV/, 'fork triggers %ENV check';
1180         test $@ eq '',               'pipe/fork/open/close failed';
1181         eval {
1182             open my $pipe, "|$Invoke_Perl -e 1";
1183             close $pipe;
1184         };
1185         test $@ =~ /Insecure \$ENV/, 'popen neglects %ENV check';
1186     }
1187 }