Re: perl@16083
[p5sagit/p5-mst-13.2.git] / ext / Storable / t / malice.t
CommitLineData
b8778c7c 1#!./perl -w
2
3#
4# Copyright 2002, Larry Wall.
5#
6# You may redistribute only under the same terms as Perl 5, as specified
7# in the README file that comes with the distribution.
8#
9
10# I'm trying to keep this test easily backwards compatible to 5.004, so no
11# qr//;
12# Currently using Test not Test::More, as Test is in core that far back.
13
14# This test tries to craft malicious data to test out as many different
15# error traps in Storable as possible
16# It also acts as a test for read_header
17
18sub BEGIN {
19 if ($ENV{PERL_CORE}){
20 chdir('t') if -d 't';
21 @INC = '.';
22 push @INC, '../lib';
23 }
24 require Config; import Config;
25 if ($ENV{PERL_CORE} and $Config{'extensions'} !~ /\bStorable\b/) {
26 print "1..0 # Skip: Storable was not built\n";
27 exit 0;
28 }
29 # require 'lib/st-dump.pl';
30}
31
32use strict;
291cf09c 33use vars qw($file_magic_str $other_magic $network_magic $major $minor
34 $C_visible_byteorder);
b8778c7c 35$file_magic_str = 'pst0';
291cf09c 36$other_magic = 7 + $Config{longsize};
b8778c7c 37$network_magic = 2;
291cf09c 38$major = 2;
39$minor = 5;
40
41# Config.pm does games to figure out byteorder dynamically. In the process
42# it creates an 8 digit entry on long long builds on 32 bit long systems.
43# config.sh, config.h and therefore Storable.xs have a 4 digit entry.
44$C_visible_byteorder = $Config{byteorder};
45if ($Config{longsize} != $Config{ivsize}) {
46 if ($C_visible_byteorder =~ /^1234/) {
47 # Little endian
48 substr ($C_visible_byteorder, $Config{longsize}) = '';
49 } elsif ($C_visible_byteorder =~ /4321$/) {
50 # Big endian
51 $C_visible_byteorder = substr ($C_visible_byteorder, -$Config{longsize});
52 } else {
53 die "longs are $Config{longsize} bytes, IVs are $Config{ivsize}, byte order $C_visible_byteorder not regonised";
54 }
55}
b8778c7c 56
57use Test;
291cf09c 58BEGIN { plan tests => 334 + $Config{longsize} * 4}
b8778c7c 59
60use Storable qw (store retrieve freeze thaw nstore nfreeze);
61
62my $file = "malice.$$";
63die "Temporary file 'malice.$$' already exists" if -e $file;
64
65END { while (-f $file) {unlink $file or die "Can't unlink '$file': $!" }}
66
67my %hash = (perl => 'rules');
68
69sub test_hash {
70 my $clone = shift;
71 ok (ref $clone, "HASH", "Get hash back");
72 ok (scalar keys %$clone, 1, "with 1 key");
73 ok ((keys %$clone)[0], "perl", "which is correct");
74 ok ($clone->{perl}, "rules");
75}
76
77sub test_header {
78 my ($header, $isfile, $isnetorder) = @_;
79 ok (!!$header->{file}, !!$isfile, "is file");
291cf09c 80 ok ($header->{major}, $major, "major number");
81 ok ($header->{minor}, $minor, "minor number");
b8778c7c 82 ok (!!$header->{netorder}, !!$isnetorder, "is network order");
83 if ($isnetorder) {
84 # Skip these
85 for (1..5) {
86 ok (1, 1, "Network order header has no sizes");
87 }
88 } else {
291cf09c 89 ok ($header->{byteorder}, $C_visible_byteorder, "byte order");
b8778c7c 90 ok ($header->{intsize}, $Config{intsize}, "int size");
91 ok ($header->{longsize}, $Config{longsize}, "long size");
291cf09c 92 ok ($header->{ptrsize}, $Config{ptrsize}, "long size");
b8778c7c 93 ok ($header->{nvsize}, $Config{nvsize} || $Config{doublesize} || 8,
94 "nv size"); # 5.00405 doesn't even have doublesize in config.
95 }
96}
97
98sub store_and_retrieve {
99 my $data = shift;
100 unlink $file or die "Can't unlink '$file': $!";
101 open FH, ">$file" or die "Can't open '$file': $!";
102 binmode FH;
103 print FH $data or die "Can't print to '$file': $!";
104 close FH or die "Can't close '$file': $!";
105
106 return eval {retrieve $file};
107}
108
109sub freeze_and_thaw {
110 my $data = shift;
111 return eval {thaw $data};
112}
113
114sub test_truncated {
115 my ($data, $sub, $magic_len, $what) = @_;
116 for my $i (0 .. length ($data) - 1) {
117 my $short = substr $data, 0, $i;
118
119 my $clone = &$sub($short);
120 ok (defined ($clone), '', "truncated $what to $i should fail");
121 if ($i < $magic_len) {
122 ok ($@, "/^Magic number checking on storable $what failed/",
123 "Should croak with magic number warning");
124 } else {
125 ok ($@, "", "Should not set \$\@");
126 }
127 }
128}
129
130sub test_corrupt {
131 my ($data, $sub, $what, $name) = @_;
132
133 my $clone = &$sub($data);
134 ok (defined ($clone), '', "$name $what should fail");
135 ok ($@, $what, $name);
136}
137
138sub test_things {
139 my ($contents, $sub, $what, $isnetwork) = @_;
140 my $isfile = $what eq 'file';
141 my $file_magic = $isfile ? length $file_magic_str : 0;
142
143 my $header = Storable::read_magic ($contents);
144 test_header ($header, $isfile, $isnetwork);
145
146 # Test that if we re-write it, everything still works:
147 my $clone = &$sub ($contents);
148
149 ok ($@, "", "There should be no error");
150
151 test_hash ($clone);
152
153 # Now lets check the short version:
154 test_truncated ($contents, $sub, $file_magic
155 + ($isnetwork ? $network_magic : $other_magic), $what);
156
157 my $copy;
158 if ($isfile) {
159 $copy = $contents;
160 substr ($copy, 0, 4) = 'iron';
161 test_corrupt ($copy, $sub, "/^File is not a perl storable/",
162 "magic number");
163 }
164
165 $copy = $contents;
166 my $minor1 = $header->{minor} + 1;
167 substr ($copy, $file_magic + 1, 1) = chr $minor1;
168 test_corrupt ($copy, $sub,
291cf09c 169 "/^Storable binary image v$header->{major}\.$minor1 more recent than I am \\(v$header->{major}\.$header->{minor}\\)/",
b8778c7c 170 "higher minor");
171
172 $copy = $contents;
173 my $major1 = $header->{major} + 1;
174 substr ($copy, $file_magic, 1) = chr 2*$major1;
175 test_corrupt ($copy, $sub,
291cf09c 176 "/^Storable binary image v$major1\.$header->{minor} more recent than I am \\(v$header->{major}\.$header->{minor}\\)/",
b8778c7c 177 "higher major");
178
179 # Continue messing with the previous copy
180 $minor1 = $header->{minor} - 1;
181 substr ($copy, $file_magic + 1, 1) = chr $minor1;
182 test_corrupt ($copy, $sub,
291cf09c 183 "/^Storable binary image v$major1\.$minor1 more recent than I am \\(v$header->{major}\.$header->{minor}\\)/",
b8778c7c 184 "higher major, lower minor");
185
186 my $where;
187 if (!$isnetwork) {
188 # All these are omitted from the network order header.
189 # I'm not sure if it's correct to omit the byte size stuff.
190 $copy = $contents;
191 substr ($copy, $file_magic + 3, length $header->{byteorder})
192 = reverse $header->{byteorder};
193
194 test_corrupt ($copy, $sub, "/^Byte order is not compatible/",
195 "byte order");
196 $where = $file_magic + 3 + length $header->{byteorder};
197 foreach (['intsize', "Integer"],
291cf09c 198 ['longsize', "Long integer"],
b8778c7c 199 ['ptrsize', "Pointer integer"],
200 ['nvsize', "Double"]) {
201 my ($key, $name) = @$_;
202 $copy = $contents;
203 substr ($copy, $where++, 1) = chr 0;
204 test_corrupt ($copy, $sub, "/^$name size is not compatible/",
205 "$name size");
206 }
207 } else {
208 $where = $file_magic + $network_magic;
209 }
210
211 # Just the header and a tag 255. As 26 is currently the highest tag, this
212 # is "unexpected"
213 $copy = substr ($contents, 0, $where) . chr 255;
214
215 test_corrupt ($copy, $sub,
216 "/^Corrupted storable $what \\(binary v$header->{major}.$header->{minor}\\)/",
217 "bogus tag");
218}
219
220sub slurp {
221 my $file = shift;
222 local (*FH, $/);
223 open FH, "<$file" or die "Can't open '$file': $!";
224 binmode FH;
225 my $contents = <FH>;
226 die "Can't read $file: $!" unless defined $contents;
227 return $contents;
228}
229
230
231ok (defined store(\%hash, $file));
232
233my $expected = 20 + length ($file_magic_str) + $other_magic;
234my $length = -s $file;
235
236die "Don't seem to have written file '$file' as I can't get its length: $!"
237 unless defined $file;
238
291cf09c 239die "Expected file to be $expected bytes (sizeof long is $Config{longsize}) but it is $length"
b8778c7c 240 unless $length == $expected;
241
242# Read the contents into memory:
243my $contents = slurp $file;
244
245# Test the original direct from disk
246my $clone = retrieve $file;
247test_hash ($clone);
248
249# Then test it.
250test_things($contents, \&store_and_retrieve, 'file');
251
252# And now try almost everything again with a Storable string
253my $stored = freeze \%hash;
254test_things($stored, \&freeze_and_thaw, 'string');
255
256# Network order.
257unlink $file or die "Can't unlink '$file': $!";
258
259ok (defined nstore(\%hash, $file));
260
261$expected = 20 + length ($file_magic_str) + $network_magic;
262$length = -s $file;
263
264die "Don't seem to have written file '$file' as I can't get its length: $!"
265 unless defined $file;
266
291cf09c 267die "Expected file to be $expected bytes (sizeof long is $Config{longsize}) but it is $length"
b8778c7c 268 unless $length == $expected;
269
270# Read the contents into memory:
271$contents = slurp $file;
272
273# Test the original direct from disk
274$clone = retrieve $file;
275test_hash ($clone);
276
277# Then test it.
278test_things($contents, \&store_and_retrieve, 'file', 1);
279
280# And now try almost everything again with a Storable string
281$stored = nfreeze \%hash;
282test_things($stored, \&freeze_and_thaw, 'string', 1);