1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

ext/standard: Improve checking of allowed_classes option (#15267)

* ext/standard: Add some unserializing tests

* ext/standard: Add proper type checking for values of the allowed_classes option array

* ext/standard: Check that class names are somewhat sensible for the allowed_classes option array

* Indicate type of value

* Add test for Stringable objects
This commit is contained in:
Gina Peter Banyard
2024-08-20 15:24:25 +01:00
committed by GitHub
parent efe0e73c91
commit 3059adae06
5 changed files with 161 additions and 11 deletions

View File

@@ -0,0 +1,61 @@
--TEST--
Test unserialize() with array allowed_classes and nonsensical values
--FILE--
<?php
class foo {
public $x = "bar";
}
$z = array(new foo(), 2, "3");
$s = serialize($z);
try {
unserialize($s, ["allowed_classes" => [null]]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
try {
unserialize($s, ["allowed_classes" => [false]]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
try {
unserialize($s, ["allowed_classes" => [true]]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
try {
unserialize($s, ["allowed_classes" => [42]]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
try {
unserialize($s, ["allowed_classes" => [15.2]]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
try {
unserialize($s, ["allowed_classes" => [[]]]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
try {
unserialize($s, ["allowed_classes" => [STDERR]]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
try {
unserialize($s, ["allowed_classes" => [new stdClass]]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
?>
--EXPECT--
TypeError: unserialize(): Option "allowed_classes" must be an array of class names, null given
TypeError: unserialize(): Option "allowed_classes" must be an array of class names, false given
TypeError: unserialize(): Option "allowed_classes" must be an array of class names, true given
TypeError: unserialize(): Option "allowed_classes" must be an array of class names, int given
TypeError: unserialize(): Option "allowed_classes" must be an array of class names, float given
TypeError: unserialize(): Option "allowed_classes" must be an array of class names, array given
TypeError: unserialize(): Option "allowed_classes" must be an array of class names, resource given
Error: Object of class stdClass could not be converted to string

View File

@@ -0,0 +1,48 @@
--TEST--
Test unserialize() with array allowed_classes and nonsensical class names
--FILE--
<?php
class foo {
public $x = "bar";
}
$z = array(new foo(), 2, "3");
$s = serialize($z);
try {
unserialize($s, ["allowed_classes" => [""]]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
try {
unserialize($s, ["allowed_classes" => ["245blerg"]]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
try {
unserialize($s, ["allowed_classes" => [" whitespace "]]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
try {
unserialize($s, ["allowed_classes" => ["name\nwith whitespace"]]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
try {
unserialize($s, ["allowed_classes" => ['$dollars']]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
try {
unserialize($s, ["allowed_classes" => ["have\0nul_byte"]]);
} catch (Throwable $e) {
echo $e::class, ': ', $e->getMessage(), "\n";
}
?>
--EXPECT--
ValueError: unserialize(): Option "allowed_classes" must be an array of class names, " whitespace " given
ValueError: unserialize(): Option "allowed_classes" must be an array of class names, "name
with whitespace" given
ValueError: unserialize(): Option "allowed_classes" must be an array of class names, "$dollars" given
ValueError: unserialize(): Option "allowed_classes" must be an array of class names, "have" given

View File

@@ -0,0 +1,32 @@
--TEST--
Test unserialize() with Stringable object in allowed_classes
--FILE--
<?php
class foo {
public $x = "bar";
}
$z = array(new foo(), 2, "3");
$s = serialize($z);
class Name {
public function __toString(): string {
return 'Foo';
}
}
$o = new Name();
var_dump(unserialize($s, ["allowed_classes" => [$o]]));
?>
--EXPECT--
array(3) {
[0]=>
object(foo)#3 (1) {
["x"]=>
string(3) "bar"
}
[1]=>
int(2)
[2]=>
string(1) "3"
}

View File

@@ -1370,25 +1370,34 @@ PHPAPI void php_unserialize_with_options(zval *return_value, const char *buf, co
goto cleanup;
}
if(classes && (Z_TYPE_P(classes) == IS_ARRAY || !zend_is_true(classes))) {
if (classes && (Z_TYPE_P(classes) == IS_ARRAY || !zend_is_true(classes))) {
ALLOC_HASHTABLE(class_hash);
zend_hash_init(class_hash, (Z_TYPE_P(classes) == IS_ARRAY)?zend_hash_num_elements(Z_ARRVAL_P(classes)):0, NULL, NULL, 0);
}
if(class_hash && Z_TYPE_P(classes) == IS_ARRAY) {
if (class_hash && Z_TYPE_P(classes) == IS_ARRAY) {
zval *entry;
zend_string *lcname;
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(classes), entry) {
convert_to_string(entry);
lcname = zend_string_tolower(Z_STR_P(entry));
ZVAL_DEREF(entry);
if (UNEXPECTED(Z_TYPE_P(entry) != IS_STRING && Z_TYPE_P(entry) != IS_OBJECT)) {
zend_type_error("%s(): Option \"allowed_classes\" must be an array of class names, %s given",
function_name, zend_zval_value_name(entry));
goto cleanup;
}
zend_string *name = zval_try_get_string(entry);
if (UNEXPECTED(name == NULL)) {
goto cleanup;
}
if (UNEXPECTED(!zend_is_valid_class_name(name))) {
zend_value_error("%s(): Option \"allowed_classes\" must be an array of class names, \"%s\" given", function_name, ZSTR_VAL(name));
zend_string_release_ex(name, false);
goto cleanup;
}
zend_string *lcname = zend_string_tolower(name);
zend_hash_add_empty_element(class_hash, lcname);
zend_string_release_ex(lcname, 0);
zend_string_release_ex(name, false);
zend_string_release_ex(lcname, false);
} ZEND_HASH_FOREACH_END();
/* Exception during string conversion. */
if (EG(exception)) {
goto cleanup;
}
}
php_var_unserialize_set_allowed_classes(var_hash, class_hash);