summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/standard/tests/serialize/sleep_uninitialized_typed_prop.phpt60
-rw-r--r--ext/standard/var.c48
2 files changed, 99 insertions, 9 deletions
diff --git a/ext/standard/tests/serialize/sleep_uninitialized_typed_prop.phpt b/ext/standard/tests/serialize/sleep_uninitialized_typed_prop.phpt
new file mode 100644
index 0000000000..3d78e11f28
--- /dev/null
+++ b/ext/standard/tests/serialize/sleep_uninitialized_typed_prop.phpt
@@ -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)
+}
diff --git a/ext/standard/var.c b/ext/standard/var.c
index 815caaff86..70a70b0f6e 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -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));
- 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);
+ 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);
}
/* }}} */