diff options
Diffstat (limited to 'ext/standard/var_unserializer.re')
-rw-r--r-- | ext/standard/var_unserializer.re | 68 |
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; } |