Skip to content

Commit b22daa3

Browse files
committed
Merge branch 'PHP-7.4'
* PHP-7.4: Throw Error when referencing uninit typed prop in __sleep
2 parents d9caf35 + 846b647 commit b22daa3

File tree

2 files changed

+99
-9
lines changed

2 files changed

+99
-9
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
--TEST--
2+
Referencing an uninitialized typed property in __sleep() should result in Error
3+
--FILE--
4+
<?php
5+
6+
class Test {
7+
public int $x;
8+
protected int $y;
9+
private int $z;
10+
11+
public function __sleep() {
12+
return ['x', 'y', 'z'];
13+
}
14+
15+
public function __set($name, $val) {
16+
$this->$name = $val;
17+
}
18+
}
19+
20+
$t = new Test;
21+
try {
22+
serialize($t);
23+
} catch (Error $e) {
24+
echo $e->getMessage(), "\n";
25+
}
26+
27+
$t->x = 1;
28+
try {
29+
serialize($t);
30+
} catch (Error $e) {
31+
echo $e->getMessage(), "\n";
32+
}
33+
34+
$t->y = 2;
35+
try {
36+
serialize($t);
37+
} catch (Error $e) {
38+
echo $e->getMessage(), "\n";
39+
}
40+
41+
$t->z = 3;
42+
try {
43+
var_dump(unserialize(serialize($t)));
44+
} catch (Error $e) {
45+
echo $e->getMessage(), "\n";
46+
}
47+
48+
?>
49+
--EXPECT--
50+
Typed property Test::$x must not be accessed before initialization (in __sleep)
51+
Typed property Test::$y must not be accessed before initialization (in __sleep)
52+
Typed property Test::$z must not be accessed before initialization (in __sleep)
53+
object(Test)#3 (3) {
54+
["x"]=>
55+
int(1)
56+
["y":protected]=>
57+
int(2)
58+
["z":"Test":private]=>
59+
int(3)
60+
}

ext/standard/var.c

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,7 @@ static int php_var_serialize_call_magic_serialize(zval *retval, zval *obj) /* {{
765765
/* }}} */
766766

767767
static int php_var_serialize_try_add_sleep_prop(
768-
HashTable *ht, HashTable *props, zend_string *name, zend_string *error_name) /* {{{ */
768+
HashTable *ht, HashTable *props, zend_string *name, zend_string *error_name, zval *struc) /* {{{ */
769769
{
770770
zval *val = zend_hash_find(props, name);
771771
if (val == NULL) {
@@ -775,6 +775,12 @@ static int php_var_serialize_try_add_sleep_prop(
775775
if (Z_TYPE_P(val) == IS_INDIRECT) {
776776
val = Z_INDIRECT_P(val);
777777
if (Z_TYPE_P(val) == IS_UNDEF) {
778+
zend_property_info *info = zend_get_typed_property_info_for_slot(Z_OBJ_P(struc), val);
779+
if (info) {
780+
zend_throw_error(NULL,
781+
"Typed property %s::$%s must not be accessed before initialization (in __sleep)",
782+
ZSTR_VAL(Z_OBJCE_P(struc)->name), ZSTR_VAL(error_name));
783+
}
778784
return FAILURE;
779785
}
780786
}
@@ -790,14 +796,17 @@ static int php_var_serialize_try_add_sleep_prop(
790796
}
791797
/* }}} */
792798

793-
static void php_var_serialize_get_sleep_props(
799+
static int php_var_serialize_get_sleep_props(
794800
HashTable *ht, zval *struc, HashTable *sleep_retval) /* {{{ */
795801
{
796802
zend_class_entry *ce = Z_OBJCE_P(struc);
797803
HashTable *props = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_SERIALIZE);
798804
zval *name_val;
805+
int retval = SUCCESS;
799806

800807
zend_hash_init(ht, zend_hash_num_elements(sleep_retval), NULL, ZVAL_PTR_DTOR, 0);
808+
/* TODO: Rewrite this by fetching the property info instead of trying out different
809+
* name manglings? */
801810
ZEND_HASH_FOREACH_VAL(sleep_retval, name_val) {
802811
zend_string *name, *tmp_name, *priv_name, *prot_name;
803812

@@ -808,36 +817,56 @@ static void php_var_serialize_get_sleep_props(
808817
}
809818

810819
name = zval_get_tmp_string(name_val, &tmp_name);
811-
if (php_var_serialize_try_add_sleep_prop(ht, props, name, name) == SUCCESS) {
820+
if (php_var_serialize_try_add_sleep_prop(ht, props, name, name, struc) == SUCCESS) {
812821
zend_tmp_string_release(tmp_name);
813822
continue;
814823
}
815824

825+
if (EG(exception)) {
826+
zend_tmp_string_release(tmp_name);
827+
retval = FAILURE;
828+
break;
829+
}
830+
816831
priv_name = zend_mangle_property_name(
817832
ZSTR_VAL(ce->name), ZSTR_LEN(ce->name),
818833
ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS);
819-
if (php_var_serialize_try_add_sleep_prop(ht, props, priv_name, name) == SUCCESS) {
834+
if (php_var_serialize_try_add_sleep_prop(ht, props, priv_name, name, struc) == SUCCESS) {
820835
zend_tmp_string_release(tmp_name);
821836
zend_string_release(priv_name);
822837
continue;
823838
}
824839
zend_string_release(priv_name);
825840

841+
if (EG(exception)) {
842+
zend_tmp_string_release(tmp_name);
843+
retval = FAILURE;
844+
break;
845+
}
846+
826847
prot_name = zend_mangle_property_name(
827848
"*", 1, ZSTR_VAL(name), ZSTR_LEN(name), ce->type & ZEND_INTERNAL_CLASS);
828-
if (php_var_serialize_try_add_sleep_prop(ht, props, prot_name, name) == SUCCESS) {
849+
if (php_var_serialize_try_add_sleep_prop(ht, props, prot_name, name, struc) == SUCCESS) {
829850
zend_tmp_string_release(tmp_name);
830851
zend_string_release(prot_name);
831852
continue;
832853
}
833854
zend_string_release(prot_name);
834855

856+
if (EG(exception)) {
857+
zend_tmp_string_release(tmp_name);
858+
retval = FAILURE;
859+
break;
860+
}
861+
835862
php_error_docref(NULL, E_NOTICE,
836863
"\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(name));
837864
zend_hash_add(ht, name, &EG(uninitialized_zval));
838865
zend_tmp_string_release(tmp_name);
839866
} ZEND_HASH_FOREACH_END();
867+
840868
zend_release_properties(props);
869+
return retval;
841870
}
842871
/* }}} */
843872

@@ -893,10 +922,11 @@ static void php_var_serialize_nested_data(smart_str *buf, zval *struc, HashTable
893922
static void php_var_serialize_class(smart_str *buf, zval *struc, zval *retval_ptr, php_serialize_data_t var_hash) /* {{{ */
894923
{
895924
HashTable props;
896-
php_var_serialize_get_sleep_props(&props, struc, HASH_OF(retval_ptr));
897-
php_var_serialize_class_name(buf, struc);
898-
php_var_serialize_nested_data(
899-
buf, struc, &props, zend_hash_num_elements(&props), /* incomplete_class */ 0, var_hash);
925+
if (php_var_serialize_get_sleep_props(&props, struc, HASH_OF(retval_ptr)) == SUCCESS) {
926+
php_var_serialize_class_name(buf, struc);
927+
php_var_serialize_nested_data(
928+
buf, struc, &props, zend_hash_num_elements(&props), /* incomplete_class */ 0, var_hash);
929+
}
900930
zend_hash_destroy(&props);
901931
}
902932
/* }}} */

0 commit comments

Comments
 (0)