test appears to be ass_u_ming sizeof(long) == sizeof(IV), which
[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;
33use vars qw($file_magic_str $other_magic $network_magic);
34$file_magic_str = 'pst0';
c37c92b7 35$other_magic = 7 + $Config{ivsize};
b8778c7c 36$network_magic = 2;
37
38use Test;
c37c92b7 39BEGIN { plan tests => 334 + $Config{ivsize} * 4}
b8778c7c 40
41use Storable qw (store retrieve freeze thaw nstore nfreeze);
42
43my $file = "malice.$$";
44die "Temporary file 'malice.$$' already exists" if -e $file;
45
46END { while (-f $file) {unlink $file or die "Can't unlink '$file': $!" }}
47
48my %hash = (perl => 'rules');
49
50sub test_hash {
51 my $clone = shift;
52 ok (ref $clone, "HASH", "Get hash back");
53 ok (scalar keys %$clone, 1, "with 1 key");
54 ok ((keys %$clone)[0], "perl", "which is correct");
55 ok ($clone->{perl}, "rules");
56}
57
58sub test_header {
59 my ($header, $isfile, $isnetorder) = @_;
60 ok (!!$header->{file}, !!$isfile, "is file");
61 ok ($header->{major}, 2, "major number");
62 ok ($header->{minor}, 5, "minor number");
63 ok (!!$header->{netorder}, !!$isnetorder, "is network order");
64 if ($isnetorder) {
65 # Skip these
66 for (1..5) {
67 ok (1, 1, "Network order header has no sizes");
68 }
69 } else {
70 ok ($header->{byteorder}, $Config{byteorder}, "byte order");
71 ok ($header->{intsize}, $Config{intsize}, "int size");
72 ok ($header->{longsize}, $Config{longsize}, "long size");
c37c92b7 73 ok ($header->{ptrsize}, $Config{ptrsize}, "ptr size");
b8778c7c 74 ok ($header->{nvsize}, $Config{nvsize} || $Config{doublesize} || 8,
75 "nv size"); # 5.00405 doesn't even have doublesize in config.
76 }
77}
78
79sub store_and_retrieve {
80 my $data = shift;
81 unlink $file or die "Can't unlink '$file': $!";
82 open FH, ">$file" or die "Can't open '$file': $!";
83 binmode FH;
84 print FH $data or die "Can't print to '$file': $!";
85 close FH or die "Can't close '$file': $!";
86
87 return eval {retrieve $file};
88}
89
90sub freeze_and_thaw {
91 my $data = shift;
92 return eval {thaw $data};
93}
94
95sub test_truncated {
96 my ($data, $sub, $magic_len, $what) = @_;
97 for my $i (0 .. length ($data) - 1) {
98 my $short = substr $data, 0, $i;
99
100 my $clone = &$sub($short);
101 ok (defined ($clone), '', "truncated $what to $i should fail");
102 if ($i < $magic_len) {
103 ok ($@, "/^Magic number checking on storable $what failed/",
104 "Should croak with magic number warning");
105 } else {
106 ok ($@, "", "Should not set \$\@");
107 }
108 }
109}
110
111sub test_corrupt {
112 my ($data, $sub, $what, $name) = @_;
113
114 my $clone = &$sub($data);
115 ok (defined ($clone), '', "$name $what should fail");
116 ok ($@, $what, $name);
117}
118
119sub test_things {
120 my ($contents, $sub, $what, $isnetwork) = @_;
121 my $isfile = $what eq 'file';
122 my $file_magic = $isfile ? length $file_magic_str : 0;
123
124 my $header = Storable::read_magic ($contents);
125 test_header ($header, $isfile, $isnetwork);
126
127 # Test that if we re-write it, everything still works:
128 my $clone = &$sub ($contents);
129
130 ok ($@, "", "There should be no error");
131
132 test_hash ($clone);
133
134 # Now lets check the short version:
135 test_truncated ($contents, $sub, $file_magic
136 + ($isnetwork ? $network_magic : $other_magic), $what);
137
138 my $copy;
139 if ($isfile) {
140 $copy = $contents;
141 substr ($copy, 0, 4) = 'iron';
142 test_corrupt ($copy, $sub, "/^File is not a perl storable/",
143 "magic number");
144 }
145
146 $copy = $contents;
147 my $minor1 = $header->{minor} + 1;
148 substr ($copy, $file_magic + 1, 1) = chr $minor1;
149 test_corrupt ($copy, $sub,
150 "/^Storable binary image v$header->{major}.$minor1 more recent than I am \\(v$header->{major}.$header->{minor}\\)/",
151 "higher minor");
152
153 $copy = $contents;
154 my $major1 = $header->{major} + 1;
155 substr ($copy, $file_magic, 1) = chr 2*$major1;
156 test_corrupt ($copy, $sub,
157 "/^Storable binary image v$major1.$header->{minor} more recent than I am \\(v$header->{major}.$header->{minor}\\)/",
158 "higher major");
159
160 # Continue messing with the previous copy
161 $minor1 = $header->{minor} - 1;
162 substr ($copy, $file_magic + 1, 1) = chr $minor1;
163 test_corrupt ($copy, $sub,
164 "/^Storable binary image v$major1.$minor1 more recent than I am \\(v$header->{major}.$header->{minor}\\)/",
165 "higher major, lower minor");
166
167 my $where;
168 if (!$isnetwork) {
169 # All these are omitted from the network order header.
170 # I'm not sure if it's correct to omit the byte size stuff.
171 $copy = $contents;
172 substr ($copy, $file_magic + 3, length $header->{byteorder})
173 = reverse $header->{byteorder};
174
175 test_corrupt ($copy, $sub, "/^Byte order is not compatible/",
176 "byte order");
177 $where = $file_magic + 3 + length $header->{byteorder};
178 foreach (['intsize', "Integer"],
c37c92b7 179 ['ivsize', "Long integer"],
b8778c7c 180 ['ptrsize', "Pointer integer"],
181 ['nvsize', "Double"]) {
182 my ($key, $name) = @$_;
183 $copy = $contents;
184 substr ($copy, $where++, 1) = chr 0;
185 test_corrupt ($copy, $sub, "/^$name size is not compatible/",
186 "$name size");
187 }
188 } else {
189 $where = $file_magic + $network_magic;
190 }
191
192 # Just the header and a tag 255. As 26 is currently the highest tag, this
193 # is "unexpected"
194 $copy = substr ($contents, 0, $where) . chr 255;
195
196 test_corrupt ($copy, $sub,
197 "/^Corrupted storable $what \\(binary v$header->{major}.$header->{minor}\\)/",
198 "bogus tag");
199}
200
201sub slurp {
202 my $file = shift;
203 local (*FH, $/);
204 open FH, "<$file" or die "Can't open '$file': $!";
205 binmode FH;
206 my $contents = <FH>;
207 die "Can't read $file: $!" unless defined $contents;
208 return $contents;
209}
210
211
212ok (defined store(\%hash, $file));
213
214my $expected = 20 + length ($file_magic_str) + $other_magic;
215my $length = -s $file;
216
217die "Don't seem to have written file '$file' as I can't get its length: $!"
218 unless defined $file;
219
c37c92b7 220die "Expected file to be $expected bytes (sizeof IV is $Config{ivsize}) but it is $length"
b8778c7c 221 unless $length == $expected;
222
223# Read the contents into memory:
224my $contents = slurp $file;
225
226# Test the original direct from disk
227my $clone = retrieve $file;
228test_hash ($clone);
229
230# Then test it.
231test_things($contents, \&store_and_retrieve, 'file');
232
233# And now try almost everything again with a Storable string
234my $stored = freeze \%hash;
235test_things($stored, \&freeze_and_thaw, 'string');
236
237# Network order.
238unlink $file or die "Can't unlink '$file': $!";
239
240ok (defined nstore(\%hash, $file));
241
242$expected = 20 + length ($file_magic_str) + $network_magic;
243$length = -s $file;
244
245die "Don't seem to have written file '$file' as I can't get its length: $!"
246 unless defined $file;
247
c37c92b7 248die "Expected file to be $expected bytes (sizeof IV is $Config{ivsize}) but it is $length"
b8778c7c 249 unless $length == $expected;
250
251# Read the contents into memory:
252$contents = slurp $file;
253
254# Test the original direct from disk
255$clone = retrieve $file;
256test_hash ($clone);
257
258# Then test it.
259test_things($contents, \&store_and_retrieve, 'file', 1);
260
261# And now try almost everything again with a Storable string
262$stored = nfreeze \%hash;
263test_things($stored, \&freeze_and_thaw, 'string', 1);