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