summaryrefslogtreecommitdiff
path: root/ext/standard/var_unserializer.re
diff options
context:
space:
mode:
Diffstat (limited to 'ext/standard/var_unserializer.re')
-rw-r--r--ext/standard/var_unserializer.re68
1 files changed, 54 insertions, 14 deletions
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index d07d657719..8dad71450e 100644
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@ -46,6 +46,7 @@ struct php_unserialize_data {
var_dtor_entries *first_dtor;
var_dtor_entries *last_dtor;
HashTable *allowed_classes;
+ HashTable *ref_props;
var_entries entries;
};
@@ -57,6 +58,7 @@ PHPAPI php_unserialize_data_t php_var_unserialize_init() {
d->last = &d->entries;
d->first_dtor = d->last_dtor = NULL;
d->allowed_classes = NULL;
+ d->ref_props = NULL;
d->entries.used_slots = 0;
d->entries.next = NULL;
if (!BG(serialize_lock)) {
@@ -239,6 +241,11 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
}
zval_ptr_dtor_nogc(&wakeup_name);
+
+ if ((*var_hashx)->ref_props) {
+ zend_hash_destroy((*var_hashx)->ref_props);
+ FREE_HASHTABLE((*var_hashx)->ref_props);
+ }
}
/* }}} */
@@ -395,11 +402,12 @@ static inline size_t parse_uiv(const unsigned char *p)
static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key);
-static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, int objprops)
+static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, zend_object *obj)
{
while (elements-- > 0) {
zval key, *data, d, *old_data;
zend_ulong idx;
+ zend_property_info *info = NULL;
ZVAL_UNDEF(&key);
@@ -411,7 +419,7 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab
data = NULL;
ZVAL_UNDEF(&d);
- if (!objprops) {
+ if (!obj) {
if (Z_TYPE(key) == IS_LONG) {
idx = Z_LVAL(key);
numeric_key:
@@ -440,8 +448,7 @@ numeric_key:
} else {
if (EXPECTED(Z_TYPE(key) == IS_STRING)) {
string_key:
- if (Z_TYPE_P(rval) == IS_OBJECT
- && zend_hash_num_elements(&Z_OBJCE_P(rval)->properties_info) > 0) {
+ if (obj && zend_hash_num_elements(&obj->ce->properties_info) > 0) {
zend_property_info *existing_propinfo;
zend_string *new_key;
const char *unmangled_class = NULL;
@@ -456,8 +463,8 @@ string_key:
unmangled = zend_string_init(unmangled_prop, unmangled_prop_len, 0);
- existing_propinfo = zend_hash_find_ptr(&Z_OBJCE_P(rval)->properties_info, unmangled);
- if ((unmangled_class == NULL || !strcmp(unmangled_class, "*") || !strcasecmp(unmangled_class, ZSTR_VAL(Z_OBJCE_P(rval)->name)))
+ existing_propinfo = zend_hash_find_ptr(&obj->ce->properties_info, unmangled);
+ if ((unmangled_class == NULL || !strcmp(unmangled_class, "*") || !strcasecmp(unmangled_class, ZSTR_VAL(obj->ce->name)))
&& (existing_propinfo != NULL)
&& (existing_propinfo->flags & ZEND_ACC_PPP_MASK)) {
if (existing_propinfo->flags & ZEND_ACC_PROTECTED) {
@@ -491,9 +498,24 @@ string_key:
if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) {
if (Z_TYPE_P(old_data) == IS_INDIRECT) {
old_data = Z_INDIRECT_P(old_data);
+ info = zend_get_typed_property_info_for_slot(obj, old_data);
+ var_push_dtor(var_hash, old_data);
+ data = zend_hash_update_ind(ht, Z_STR(key), &d);
+
+ if (UNEXPECTED(info)) {
+ /* Remember to which property this slot belongs, so we can add a
+ * type source if it is turned into a reference lateron. */
+ if (!(*var_hash)->ref_props) {
+ (*var_hash)->ref_props = emalloc(sizeof(HashTable));
+ zend_hash_init((*var_hash)->ref_props, 8, NULL, NULL, 0);
+ }
+ zend_hash_index_update_ptr(
+ (*var_hash)->ref_props, (zend_uintptr_t) data, info);
+ }
+ } else {
+ var_push_dtor(var_hash, old_data);
+ data = zend_hash_update_ind(ht, Z_STR(key), &d);
}
- var_push_dtor(var_hash, old_data);
- data = zend_hash_update_ind(ht, Z_STR(key), &d);
} else {
data = zend_hash_add_new(ht, Z_STR(key), &d);
}
@@ -512,6 +534,18 @@ string_key:
return 0;
}
+ if (UNEXPECTED(info)) {
+ if (!zend_verify_prop_assignable_by_ref(info, data, /* strict */ 1)) {
+ zval_ptr_dtor(data);
+ ZVAL_UNDEF(data);
+ zval_dtor(&key);
+ return 0;
+ }
+ if (Z_ISREF_P(data)) {
+ ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), info);
+ }
+ }
+
if (BG(unserialize).level > 1) {
var_push_dtor(var_hash, data);
}
@@ -613,7 +647,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements)
}
zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, HT_FLAGS(ht) & HASH_FLAG_PACKED);
- if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, 1)) {
+ if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJ_P(rval))) {
if (has_wakeup) {
ZVAL_DEREF(rval);
GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED);
@@ -697,13 +731,19 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER, int as_key)
return 0;
}
- if (Z_ISREF_P(rval_ref)) {
- ZVAL_COPY(rval, rval_ref);
- } else {
+ if (!Z_ISREF_P(rval_ref)) {
+ zend_property_info *info = NULL;
+ if ((*var_hash)->ref_props) {
+ info = zend_hash_index_find_ptr((*var_hash)->ref_props, (zend_uintptr_t) rval_ref);
+ }
ZVAL_NEW_REF(rval_ref, rval_ref);
- ZVAL_COPY(rval, rval_ref);
+ if (info) {
+ ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(rval_ref), info);
+ }
}
+ ZVAL_COPY(rval, rval_ref);
+
return 1;
}
@@ -901,7 +941,7 @@ use_double:
* prohibit "r:" references to non-objects, as we only generate them for objects. */
HT_ALLOW_COW_VIOLATION(Z_ARRVAL_P(rval));
- if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, 0)) {
+ if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements, NULL)) {
return 0;
}