Merge branch 'PHP-7.4'

* PHP-7.4:
  Throw Error when referencing uninit typed prop in __sleep
This commit is contained in:
Nikita Popov 2020-01-06 18:47:44 +01:00
commit b22daa3a06
2 changed files with 99 additions and 9 deletions

View file

@ -0,0 +1,60 @@
--TEST--
Referencing an uninitialized typed property in __sleep() should result in Error
--FILE--
<?php
class Test {
public int $x;
protected int $y;
private int $z;
public function __sleep() {
return ['x', 'y', 'z'];
}
public function __set($name, $val) {
$this->$name = $val;
}
}
$t = new Test;
try {
serialize($t);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
$t->x = 1;
try {
serialize($t);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
$t->y = 2;
try {
serialize($t);
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
$t->z = 3;
try {
var_dump(unserialize(serialize($t)));
} catch (Error $e) {
echo $e->getMessage(), "\n";
}
?>
--EXPECT--
Typed property Test::$x must not be accessed before initialization (in __sleep)
Typed property Test::$y must not be accessed before initialization (in __sleep)
Typed property Test::$z must not be accessed before initialization (in __sleep)
object(Test)#3 (3) {
["x"]=>
int(1)
["y":protected]=>
int(2)
["z":"Test":private]=>
int(3)
}

View file

@ -765,7 +765,7 @@ static int php_var_serialize_call_magic_serialize(zval *retval, zval *obj) /* {{
/* }}} */
static int php_var_serialize_try_add_sleep_prop(
HashTable *ht, HashTable *props, zend_string *name, zend_string *error_name) /* {{{ */
HashTable *ht, HashTable *props, zend_string *name, zend_string *error_name, zval *struc) /* {{{ */
{
zval *val = zend_hash_find(props, name);
if (val == NULL) {
@ -775,6 +775,12 @@ static int php_var_serialize_try_add_sleep_prop(
if (Z_TYPE_P(val) == IS_INDIRECT) {
val = Z_INDIRECT_P(val);
if (Z_TYPE_P(val) == IS_UNDEF) {
zend_property_info *info = zend_get_typed_property_info_for_slot(Z_OBJ_P(struc), val);
if (info) {
zend_throw_error(NULL,
"Typed property %s::$%s must not be accessed before initialization (in __sleep)",
ZSTR_VAL(Z_OBJCE_P(struc)->name), ZSTR_VAL(error_name));
}
return FAILURE;
}
}
@ -790,14 +796,17 @@ static int php_var_serialize_try_add_sleep_prop(
}
/* }}} */
static void php_var_serialize_get_sleep_props(
static int php_var_serialize_get_sleep_props(
HashTable *ht, zval *struc, HashTable *sleep_retval) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(struc);
HashTable *props = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_SERIALIZE);
zval *name_val;
int retval = SUCCESS;
zend_hash_init(ht, zend_hash_num_elements(sleep_retval), NULL, ZVAL_PTR_DTOR, 0);
/* TODO: Rewrite this by fetching the property info instead of trying out different
* name manglings? */
ZEND_HASH_FOREACH_VAL(sleep_retval, name_val) {
zend_string *name, *tmp_name, *priv_name, *prot_name;
@ -808,36 +817,56 @@ static void php_var_serialize_get_sleep_props(
}
name = zval_get_tmp_string(name_val, &tmp_name);
if (php_var_serialize_try_add_sleep_prop(ht, props, name, name) == SUCCESS) {
if (php_var_serialize_try_add_sleep_prop(ht, props, name, name, struc) == SUCCESS) {
zend_tmp_string_release(tmp_name);
continue;
}
if (EG(exception)) {
zend_tmp_string_release(tmp_name);
retval = FAILURE;
break;
}
priv_name = zend_mangle_property_name(
ZSTR_VAL(ce->name), ZSTR_LEN(ce->name),
ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS);
if (php_var_serialize_try_add_sleep_prop(ht, props, priv_name, name) == SUCCESS) {
if (php_var_serialize_try_add_sleep_prop(ht, props, priv_name, name, struc) == SUCCESS) {
zend_tmp_string_release(tmp_name);
zend_string_release(priv_name);
continue;
}
zend_string_release(priv_name);
if (EG(exception)) {
zend_tmp_string_release(tmp_name);
retval = FAILURE;
break;
}
prot_name = zend_mangle_property_name(
"*", 1, ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS);
if (php_var_serialize_try_add_sleep_prop(ht, props, prot_name, name) == SUCCESS) {
if (php_var_serialize_try_add_sleep_prop(ht, props, prot_name, name, struc) == SUCCESS) {
zend_tmp_string_release(tmp_name);
zend_string_release(prot_name);
continue;
}
zend_string_release(prot_name);
if (EG(exception)) {
zend_tmp_string_release(tmp_name);
retval = FAILURE;
break;
}
php_error_docref(NULL, E_NOTICE,
"\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(name));
zend_hash_add(ht, name, &EG(uninitialized_zval));
zend_tmp_string_release(tmp_name);
} ZEND_HASH_FOREACH_END();
zend_release_properties(props);
return retval;
}
/* }}} */
@ -893,10 +922,11 @@ static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable
static void php_var_serialize_class(smart_str *buf, zval *struc, zval *retval_ptr, php_serialize_data_t var_hash) /* {{{ */
{
HashTable props;
php_var_serialize_get_sleep_props(&props, struc, HASH_OF(retval_ptr));
if (php_var_serialize_get_sleep_props(&props, struc, HASH_OF(retval_ptr)) == SUCCESS) {
php_var_serialize_class_name(buf, struc);
php_var_serialize_nested_data(
buf, struc, &props, zend_hash_num_elements(&props), /* incomplete_class */ 0, var_hash);
}
zend_hash_destroy(&props);
}
/* }}} */