diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2020-01-02 11:11:01 +0100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2020-01-02 11:14:01 +0100 |
commit | 90fea675462252c668b8c0c8b5b94ece8d4f24a7 (patch) | |
tree | e135911080a5a3f6465b6453f927a9d62233262a | |
parent | de0ca4734e2e8b19fabe06027439ceb81ea2eb13 (diff) | |
download | php-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.phpt | 16 | ||||
-rw-r--r-- | ext/standard/var.c | 142 |
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; |