1--TEST--
2Bug #78549: Stack overflow due to nested serialized input
3--FILE--
4<?php
5
6function create_nested_data($depth, $prefix, $suffix, $inner = 'i:0;') {
7    return str_repeat($prefix, $depth) . $inner . str_repeat($suffix, $depth);
8}
9
10echo "Invalid max_depth:\n";
11try {
12    unserialize('i:0;', ['max_depth' => 'foo']);
13} catch (TypeError $exception) {
14    echo $exception->getMessage() . "\n";
15}
16
17try {
18    unserialize('i:0;', ['max_depth' => -1]);
19} catch (ValueError $exception) {
20    echo $exception->getMessage() . "\n";
21}
22
23echo "Array:\n";
24var_dump(unserialize(
25    create_nested_data(128, 'a:1:{i:0;', '}'),
26    ['max_depth' => 128]
27) !== false);
28var_dump(unserialize(
29    create_nested_data(129, 'a:1:{i:0;', '}'),
30    ['max_depth' => 128]
31));
32
33echo "Object:\n";
34var_dump(unserialize(
35    create_nested_data(128, 'O:8:"stdClass":1:{i:0;', '}'),
36    ['max_depth' => 128]
37) !== false);
38var_dump(unserialize(
39    create_nested_data(129, 'O:8:"stdClass":1:{i:0;', '}'),
40    ['max_depth' => 128]
41));
42
43// Depth can also be adjusted using ini setting
44echo "Ini setting:\n";
45ini_set("unserialize_max_depth", 128);
46var_dump(unserialize(create_nested_data(128, 'a:1:{i:0;', '}')) !== false);
47var_dump(unserialize(create_nested_data(129, 'a:1:{i:0;', '}')));
48
49// But an explicitly specified depth still takes precedence
50echo "Ini setting overridden:\n";
51var_dump(unserialize(
52    create_nested_data(256, 'a:1:{i:0;', '}'),
53    ['max_depth' => 256]
54) !== false);
55var_dump(unserialize(
56    create_nested_data(257, 'a:1:{i:0;', '}'),
57    ['max_depth' => 256]
58));
59
60// Reset ini setting to a large value,
61// so it's clear that it won't be used in the following.
62ini_set("unserialize_max_depth", 4096);
63
64class Test implements Serializable {
65    public function serialize() {
66        return '';
67    }
68    public function unserialize($str) {
69        // Should fail, due to combined nesting level
70        var_dump(unserialize(create_nested_data(129, 'a:1:{i:0;', '}')));
71        // Should succeed, below combined nesting level
72        var_dump(unserialize(create_nested_data(128, 'a:1:{i:0;', '}')) !== false);
73    }
74}
75echo "Nested unserialize combined depth limit:\n";
76var_dump(is_array(unserialize(
77    create_nested_data(128, 'a:1:{i:0;', '}', 'C:4:"Test":0:{}'),
78    ['max_depth' => 256]
79)));
80
81class Test2 implements Serializable {
82    public function serialize() {
83        return '';
84    }
85    public function unserialize($str) {
86        // If depth limit is overridden, the depth should be counted
87        // from zero again.
88        var_dump(unserialize(
89            create_nested_data(257, 'a:1:{i:0;', '}'),
90            ['max_depth' => 256]
91        ));
92        var_dump(unserialize(
93            create_nested_data(256, 'a:1:{i:0;', '}'),
94            ['max_depth' => 256]
95        ) !== false);
96    }
97}
98echo "Nested unserialize overridden depth limit:\n";
99var_dump(is_array(unserialize(
100    create_nested_data(64, 'a:1:{i:0;', '}', 'C:5:"Test2":0:{}'),
101    ['max_depth' => 128]
102)));
103
104?>
105--EXPECTF--
106Invalid max_depth:
107unserialize(): Option "max_depth" must be of type int, string given
108unserialize(): Option "max_depth" must be greater than or equal to 0
109Array:
110bool(true)
111
112Warning: unserialize(): Maximum depth of 128 exceeded. The depth limit can be changed using the max_depth unserialize() option or the unserialize_max_depth ini setting in %s on line %d
113
114Notice: unserialize(): Error at offset 1157 of 1294 bytes in %s on line %d
115bool(false)
116Object:
117bool(true)
118
119Warning: unserialize(): Maximum depth of 128 exceeded. The depth limit can be changed using the max_depth unserialize() option or the unserialize_max_depth ini setting in %s on line %d
120
121Notice: unserialize(): Error at offset 2834 of 2971 bytes in %s on line %d
122bool(false)
123Ini setting:
124bool(true)
125
126Warning: unserialize(): Maximum depth of 128 exceeded. The depth limit can be changed using the max_depth unserialize() option or the unserialize_max_depth ini setting in %s on line %d
127
128Notice: unserialize(): Error at offset 1157 of 1294 bytes in %s on line %d
129bool(false)
130Ini setting overridden:
131bool(true)
132
133Warning: unserialize(): Maximum depth of 256 exceeded. The depth limit can be changed using the max_depth unserialize() option or the unserialize_max_depth ini setting in %s on line %d
134
135Notice: unserialize(): Error at offset 2309 of 2574 bytes in %s on line %d
136bool(false)
137Nested unserialize combined depth limit:
138
139Warning: unserialize(): Maximum depth of 256 exceeded. The depth limit can be changed using the max_depth unserialize() option or the unserialize_max_depth ini setting in %s on line %d
140
141Notice: unserialize(): Error at offset 1157 of 1294 bytes in %s on line %d
142bool(false)
143bool(true)
144bool(true)
145Nested unserialize overridden depth limit:
146
147Warning: unserialize(): Maximum depth of 256 exceeded. The depth limit can be changed using the max_depth unserialize() option or the unserialize_max_depth ini setting in %s on line %d
148
149Notice: unserialize(): Error at offset 2309 of 2574 bytes in %s on line %d
150bool(false)
151bool(true)
152bool(true)
153