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