mirror of
https://github.com/php/php-src.git
synced 2026-04-25 00:48:25 +02:00
1806ce9cb0
Add a max_depth option to unserialize and an unserialize_max_depth ini setting, which can be used to control the depth limit. The default value is 4096. This option is intended to prevent stack overflows during the unserialization of deeply nested structures. This fixes bug #78549 and addresses oss-fuzz #17581, #17589, #17664, and #17788.
160 lines
5.3 KiB
PHP
160 lines
5.3 KiB
PHP
--TEST--
|
|
Bug #78549: Stack overflow due to nested serialized input
|
|
--FILE--
|
|
<?php
|
|
|
|
function create_nested_data($depth, $prefix, $suffix, $inner = 'i:0;') {
|
|
return str_repeat($prefix, $depth) . $inner . str_repeat($suffix, $depth);
|
|
}
|
|
|
|
echo "Invalid max_depth:\n";
|
|
var_dump(unserialize('i:0;', ['max_depth' => 'foo']));
|
|
var_dump(unserialize('i:0;', ['max_depth' => -1]));
|
|
|
|
echo "Array:\n";
|
|
var_dump(unserialize(
|
|
create_nested_data(128, 'a:1:{i:0;', '}'),
|
|
['max_depth' => 128]
|
|
) !== false);
|
|
var_dump(unserialize(
|
|
create_nested_data(129, 'a:1:{i:0;', '}'),
|
|
['max_depth' => 128]
|
|
));
|
|
|
|
echo "Object:\n";
|
|
var_dump(unserialize(
|
|
create_nested_data(128, 'O:8:"stdClass":1:{i:0;', '}'),
|
|
['max_depth' => 128]
|
|
) !== false);
|
|
var_dump(unserialize(
|
|
create_nested_data(129, 'O:8:"stdClass":1:{i:0;', '}'),
|
|
['max_depth' => 128]
|
|
));
|
|
|
|
// Default depth is 4096
|
|
echo "Default depth:\n";
|
|
var_dump(unserialize(create_nested_data(4096, 'a:1:{i:0;', '}')) !== false);
|
|
var_dump(unserialize(create_nested_data(4097, 'a:1:{i:0;', '}')));
|
|
|
|
// Depth can also be adjusted using ini setting
|
|
echo "Ini setting:\n";
|
|
ini_set("unserialize_max_depth", 128);
|
|
var_dump(unserialize(create_nested_data(128, 'a:1:{i:0;', '}')) !== false);
|
|
var_dump(unserialize(create_nested_data(129, 'a:1:{i:0;', '}')));
|
|
|
|
// But an explicitly specified depth still takes precedence
|
|
echo "Ini setting overridden:\n";
|
|
var_dump(unserialize(
|
|
create_nested_data(256, 'a:1:{i:0;', '}'),
|
|
['max_depth' => 256]
|
|
) !== false);
|
|
var_dump(unserialize(
|
|
create_nested_data(257, 'a:1:{i:0;', '}'),
|
|
['max_depth' => 256]
|
|
));
|
|
|
|
// Reset ini setting to a large value,
|
|
// so it's clear that it won't be used in the following.
|
|
ini_set("unserialize_max_depth", 4096);
|
|
|
|
class Test implements Serializable {
|
|
public function serialize() {
|
|
return '';
|
|
}
|
|
public function unserialize($str) {
|
|
// Should fail, due to combined nesting level
|
|
var_dump(unserialize(create_nested_data(129, 'a:1:{i:0;', '}')));
|
|
// Should succeeed, below combined nesting level
|
|
var_dump(unserialize(create_nested_data(128, 'a:1:{i:0;', '}')) !== false);
|
|
}
|
|
}
|
|
echo "Nested unserialize combined depth limit:\n";
|
|
var_dump(is_array(unserialize(
|
|
create_nested_data(128, 'a:1:{i:0;', '}', 'C:4:"Test":0:{}'),
|
|
['max_depth' => 256]
|
|
)));
|
|
|
|
class Test2 implements Serializable {
|
|
public function serialize() {
|
|
return '';
|
|
}
|
|
public function unserialize($str) {
|
|
// If depth limit is overridden, the depth should be counted
|
|
// from zero again.
|
|
var_dump(unserialize(
|
|
create_nested_data(257, 'a:1:{i:0;', '}'),
|
|
['max_depth' => 256]
|
|
));
|
|
var_dump(unserialize(
|
|
create_nested_data(256, 'a:1:{i:0;', '}'),
|
|
['max_depth' => 256]
|
|
) !== false);
|
|
}
|
|
}
|
|
echo "Nested unserialize overridden depth limit:\n";
|
|
var_dump(is_array(unserialize(
|
|
create_nested_data(64, 'a:1:{i:0;', '}', 'C:5:"Test2":0:{}'),
|
|
['max_depth' => 128]
|
|
)));
|
|
|
|
?>
|
|
--EXPECTF--
|
|
Invalid max_depth:
|
|
|
|
Warning: unserialize(): max_depth should be int in %s on line %d
|
|
bool(false)
|
|
|
|
Warning: unserialize(): max_depth cannot be negative in %s on line %d
|
|
bool(false)
|
|
Array:
|
|
bool(true)
|
|
|
|
Warning: 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
|
|
|
|
Notice: unserialize(): Error at offset 1157 of 1294 bytes in %s on line %d
|
|
bool(false)
|
|
Object:
|
|
bool(true)
|
|
|
|
Warning: 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
|
|
|
|
Notice: unserialize(): Error at offset 2834 of 2971 bytes in %s on line %d
|
|
bool(false)
|
|
Default depth:
|
|
bool(true)
|
|
|
|
Warning: unserialize(): Maximum depth of 4096 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
|
|
|
|
Notice: unserialize(): Error at offset 36869 of 40974 bytes in %s on line %d
|
|
bool(false)
|
|
Ini setting:
|
|
bool(true)
|
|
|
|
Warning: 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
|
|
|
|
Notice: unserialize(): Error at offset 1157 of 1294 bytes in %s on line %d
|
|
bool(false)
|
|
Ini setting overridden:
|
|
bool(true)
|
|
|
|
Warning: 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
|
|
|
|
Notice: unserialize(): Error at offset 2309 of 2574 bytes in %s on line %d
|
|
bool(false)
|
|
Nested unserialize combined depth limit:
|
|
|
|
Warning: 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
|
|
|
|
Notice: unserialize(): Error at offset 1157 of 1294 bytes in %s on line %d
|
|
bool(false)
|
|
bool(true)
|
|
bool(true)
|
|
Nested unserialize overridden depth limit:
|
|
|
|
Warning: 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
|
|
|
|
Notice: unserialize(): Error at offset 2309 of 2574 bytes in %s on line %d
|
|
bool(false)
|
|
bool(true)
|
|
bool(true)
|