summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2020-01-02 11:11:01 +0100
committerNikita Popov <nikita.ppv@gmail.com>2020-01-02 11:14:01 +0100
commit90fea675462252c668b8c0c8b5b94ece8d4f24a7 (patch)
treee135911080a5a3f6465b6453f927a9d62233262a
parentde0ca4734e2e8b19fabe06027439ceb81ea2eb13 (diff)
downloadphp-git-90fea675462252c668b8c0c8b5b94ece8d4f24a7.tar.gz
Populate hashtable of __sleep() properties
Instead of populating a hashtable of property names and then directly serializing. This has the advantage of a) detecting duplicate properties more precisely and b) gives us the ability to discard values without rewriting the serialization string after the fact for GH-5027.
-rw-r--r--ext/standard/tests/serialize/sleep_mangled_name_clash.phpt16
-rw-r--r--ext/standard/var.c142
2 files changed, 76 insertions, 82 deletions
diff --git a/ext/standard/tests/serialize/sleep_mangled_name_clash.phpt b/ext/standard/tests/serialize/sleep_mangled_name_clash.phpt
new file mode 100644
index 0000000000..df9db6a473
--- /dev/null
+++ b/ext/standard/tests/serialize/sleep_mangled_name_clash.phpt
@@ -0,0 +1,16 @@
+--TEST--
+__sleep() returns properties clashing only after mangling
+--FILE--
+<?php
+class Test {
+ private $priv;
+ public function __sleep() {
+ return ["\0Test\0priv", "priv"];
+ }
+}
+$s = serialize(new Test);
+var_dump(str_replace("\0", '\0', $s));
+?>
+--EXPECTF--
+Notice: serialize(): "priv" is returned from __sleep multiple times in %s on line %d
+string(37) "O:4:"Test":1:{s:10:"\0Test\0priv";N;}"
diff --git a/ext/standard/var.c b/ext/standard/var.c
index aafc35e8bf..b611b121c9 100644
--- a/ext/standard/var.c
+++ b/ext/standard/var.c
@@ -771,113 +771,80 @@ static int php_var_serialize_call_magic_serialize(zval *retval, zval *obj) /* {{
}
/* }}} */
-static void php_var_serialize_collect_names(HashTable *ht, HashTable *src) /* {{{ */
+static int php_var_serialize_try_add_sleep_prop(
+ HashTable *ht, HashTable *props, zend_string *name, zend_string *error_name) /* {{{ */
{
- zval *val;
- zend_string *name, *tmp_name;
+ zval *val = zend_hash_find(props, name);
+ if (val == NULL) {
+ return FAILURE;
+ }
- zend_hash_init(ht, zend_hash_num_elements(src), NULL, NULL, 0);
- ZEND_HASH_FOREACH_VAL(src, val) {
- ZVAL_DEREF(val);
- if (Z_TYPE_P(val) != IS_STRING) {
- php_error_docref(NULL, E_NOTICE,
- "__sleep should return an array only containing the names of instance-variables to serialize.");
+ if (Z_TYPE_P(val) == IS_INDIRECT) {
+ val = Z_INDIRECT_P(val);
+ if (Z_TYPE_P(val) == IS_UNDEF) {
+ return FAILURE;
}
+ }
- name = zval_get_tmp_string(val, &tmp_name);
- if (zend_hash_exists(ht, name)) {
- php_error_docref(NULL, E_NOTICE,
- "\"%s\" is returned from __sleep multiple times", ZSTR_VAL(name));
- zend_tmp_string_release(tmp_name);
- continue;
- }
- zend_hash_add_empty_element(ht, name);
- zend_tmp_string_release(tmp_name);
- } ZEND_HASH_FOREACH_END();
+ if (!zend_hash_add(ht, name, val)) {
+ php_error_docref(NULL, E_NOTICE,
+ "\"%s\" is returned from __sleep multiple times", ZSTR_VAL(error_name));
+ return SUCCESS;
+ }
+
+ Z_TRY_ADDREF_P(val);
+ return SUCCESS;
}
/* }}} */
-static void php_var_serialize_class(smart_str *buf, zval *struc, zval *retval_ptr, php_serialize_data_t var_hash) /* {{{ */
+static void php_var_serialize_get_sleep_props(
+ HashTable *ht, zval *struc, HashTable *sleep_retval) /* {{{ */
{
zend_class_entry *ce = Z_OBJCE_P(struc);
- HashTable names, *propers;
- zval nval;
- zend_string *name;
+ HashTable *props = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_SERIALIZE);
+ zval *name_val;
- php_var_serialize_class_name(buf, struc);
- php_var_serialize_collect_names(&names, HASH_OF(retval_ptr));
+ zend_hash_init(ht, zend_hash_num_elements(sleep_retval), NULL, ZVAL_PTR_DTOR, 0);
+ ZEND_HASH_FOREACH_VAL(sleep_retval, name_val) {
+ zend_string *name, *tmp_name, *priv_name, *prot_name;
- smart_str_append_unsigned(buf, zend_hash_num_elements(&names));
- smart_str_appendl(buf, ":{", 2);
-
- ZVAL_NULL(&nval);
- propers = zend_get_properties_for(struc, ZEND_PROP_PURPOSE_SERIALIZE);
-
- ZEND_HASH_FOREACH_STR_KEY(&names, name) {
- zend_string *prot_name, *priv_name;
-
- zval *val = zend_hash_find_ex(propers, name, 1);
- if (val != NULL) {
- if (Z_TYPE_P(val) == IS_INDIRECT) {
- val = Z_INDIRECT_P(val);
- if (Z_TYPE_P(val) == IS_UNDEF) {
- goto undef_prop;
- }
- }
+ ZVAL_DEREF(name_val);
+ if (Z_TYPE_P(name_val) != IS_STRING) {
+ php_error_docref(NULL, E_NOTICE,
+ "__sleep should return an array only containing the names of instance-variables to serialize.");
+ }
- php_var_serialize_string(buf, ZSTR_VAL(name), ZSTR_LEN(name));
- php_var_serialize_intern(buf, val, var_hash);
+ name = zval_get_tmp_string(name_val, &tmp_name);
+ if (php_var_serialize_try_add_sleep_prop(ht, props, name, name) == SUCCESS) {
+ zend_tmp_string_release(tmp_name);
continue;
}
priv_name = zend_mangle_property_name(
- ZSTR_VAL(ce->name), ZSTR_LEN(ce->name), ZSTR_VAL(name), ZSTR_LEN(name), 0);
- val = zend_hash_find(propers, priv_name);
- if (val != NULL) {
- if (Z_TYPE_P(val) == IS_INDIRECT) {
- val = Z_INDIRECT_P(val);
- if (Z_ISUNDEF_P(val)) {
- zend_string_free(priv_name);
- goto undef_prop;
- }
- }
-
- php_var_serialize_string(buf, ZSTR_VAL(priv_name), ZSTR_LEN(priv_name));
- zend_string_free(priv_name);
- php_var_serialize_intern(buf, val, var_hash);
+ 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) {
+ zend_tmp_string_release(tmp_name);
+ zend_string_release(priv_name);
continue;
}
- zend_string_free(priv_name);
+ zend_string_release(priv_name);
prot_name = zend_mangle_property_name(
- "*", 1, ZSTR_VAL(name), ZSTR_LEN(name), 0);
- val = zend_hash_find(propers, prot_name);
- if (val != NULL) {
- if (Z_TYPE_P(val) == IS_INDIRECT) {
- val = Z_INDIRECT_P(val);
- if (Z_TYPE_P(val) == IS_UNDEF) {
- zend_string_free(prot_name);
- goto undef_prop;
- }
- }
-
- php_var_serialize_string(buf, ZSTR_VAL(prot_name), ZSTR_LEN(prot_name));
- zend_string_free(prot_name);
- php_var_serialize_intern(buf, val, var_hash);
+ "*", 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) {
+ zend_tmp_string_release(tmp_name);
+ zend_string_release(prot_name);
continue;
}
- zend_string_free(prot_name);
+ zend_string_release(prot_name);
-undef_prop:
- php_var_serialize_string(buf, ZSTR_VAL(name), ZSTR_LEN(name));
- php_var_serialize_intern(buf, &nval, var_hash);
php_error_docref(NULL, E_NOTICE,
- "\"%s\" returned as member variable from __sleep() but does not exist", ZSTR_VAL(name));
+ "\"%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();
- smart_str_appendc(buf, '}');
-
- zend_hash_destroy(&names);
- zend_release_properties(propers);
+ zend_release_properties(props);
}
/* }}} */
@@ -930,6 +897,17 @@ 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);
+ zend_hash_destroy(&props);
+}
+/* }}} */
+
static void php_var_serialize_intern(smart_str *buf, zval *struc, php_serialize_data_t var_hash) /* {{{ */
{
zend_long var_already;