diff options
Diffstat (limited to 'ext/standard/var_unserializer.re')
-rw-r--r-- | ext/standard/var_unserializer.re | 458 |
1 files changed, 323 insertions, 135 deletions
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 1b191a0367..d3a3b91ede 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -29,6 +29,10 @@ #define VAR_WAKEUP_FLAG 1 #define VAR_UNSERIALIZE_FLAG 2 +/* Each element is encoded using at least 2 characters. */ +#define IS_FAKE_ELEM_COUNT(num_elems, serialized_len) \ + ((num_elems) > (serialized_len) / 2) + typedef struct { zend_long used_slots; void *next; @@ -217,9 +221,7 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) zend_long i; var_entries *var_hash = (*var_hashx)->entries.next; var_dtor_entries *var_dtor_hash = (*var_hashx)->first_dtor; - zend_bool delayed_call_failed = 0; - zval wakeup_name; - ZVAL_UNDEF(&wakeup_name); + bool delayed_call_failed = 0; #if VAR_ENTRIES_DBG fprintf(stderr, "var_destroy( " ZEND_LONG_FMT ")\n", var_hash?var_hash->used_slots:-1L); @@ -242,12 +244,26 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) /* Perform delayed __wakeup calls */ if (!delayed_call_failed) { zval retval; - if (Z_ISUNDEF(wakeup_name)) { - ZVAL_STRINGL(&wakeup_name, "__wakeup", sizeof("__wakeup") - 1); - } + zend_fcall_info fci; + zend_fcall_info_cache fci_cache; + + ZEND_ASSERT(Z_TYPE_P(zv) == IS_OBJECT); + + fci.size = sizeof(fci); + fci.object = Z_OBJ_P(zv); + fci.retval = &retval; + fci.param_count = 0; + fci.params = NULL; + fci.named_params = NULL; + ZVAL_UNDEF(&fci.function_name); + + fci_cache.function_handler = zend_hash_find_ptr( + &fci.object->ce->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP)); + fci_cache.object = fci.object; + fci_cache.called_scope = fci.object->ce; BG(serialize_lock)++; - if (call_user_function(NULL, zv, &wakeup_name, &retval, 0, 0) == FAILURE || Z_ISUNDEF(retval)) { + if (zend_call_function(&fci, &fci_cache) == FAILURE || Z_ISUNDEF(retval)) { delayed_call_failed = 1; GC_ADD_FLAGS(Z_OBJ_P(zv), IS_OBJ_DESTRUCTOR_CALLED); } @@ -284,8 +300,6 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) var_dtor_hash = next; } - zval_ptr_dtor_nogc(&wakeup_name); - if ((*var_hashx)->ref_props) { zend_hash_destroy((*var_hashx)->ref_props); FREE_HASHTABLE((*var_hashx)->ref_props); @@ -338,12 +352,9 @@ static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t } static inline int unserialize_allowed_class( - zend_string *class_name, php_unserialize_data_t *var_hashx) + zend_string *lcname, php_unserialize_data_t *var_hashx) { HashTable *classes = (*var_hashx)->allowed_classes; - zend_string *lcname; - int res; - ALLOCA_FLAG(use_heap) if(classes == NULL) { return 1; @@ -352,11 +363,7 @@ static inline int unserialize_allowed_class( return 0; } - ZSTR_ALLOCA_ALLOC(lcname, ZSTR_LEN(class_name), use_heap); - zend_str_tolower_copy(ZSTR_VAL(lcname), ZSTR_VAL(class_name), ZSTR_LEN(class_name)); - res = zend_hash_exists(classes, lcname); - ZSTR_ALLOCA_FREE(lcname, use_heap); - return res; + return zend_hash_exists(classes, lcname); } #define YYFILL(n) do { } while (0) @@ -446,7 +453,7 @@ 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, zend_object *obj) +static zend_always_inline int process_nested_array_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements) { if (var_hash) { if ((*var_hash)->max_depth > 0 && (*var_hash)->cur_depth >= (*var_hash)->max_depth) { @@ -463,7 +470,6 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab while (elements-- > 0) { zval key, *data, d, *old_data; zend_ulong idx; - zend_property_info *info = NULL; ZVAL_UNDEF(&key); @@ -475,118 +481,190 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab data = NULL; ZVAL_UNDEF(&d); - if (!obj) { - if (Z_TYPE(key) == IS_LONG) { - idx = Z_LVAL(key); + if (Z_TYPE(key) == IS_LONG) { + idx = Z_LVAL(key); numeric_key: - if (UNEXPECTED((old_data = zend_hash_index_find(ht, idx)) != NULL)) { - //??? update hash - var_push_dtor(var_hash, old_data); - data = zend_hash_index_update(ht, idx, &d); - } else { - data = zend_hash_index_add_new(ht, idx, &d); - } - } else if (Z_TYPE(key) == IS_STRING) { - if (UNEXPECTED(ZEND_HANDLE_NUMERIC(Z_STR(key), idx))) { - goto numeric_key; - } - if (UNEXPECTED((old_data = zend_hash_find(ht, Z_STR(key))) != NULL)) { - //??? update hash - var_push_dtor(var_hash, old_data); - data = zend_hash_update(ht, Z_STR(key), &d); - } else { - data = zend_hash_add_new(ht, Z_STR(key), &d); - } + if (UNEXPECTED((old_data = zend_hash_index_find(ht, idx)) != NULL)) { + //??? update hash + var_push_dtor(var_hash, old_data); + data = zend_hash_index_update(ht, idx, &d); } else { - zval_ptr_dtor(&key); - goto failure; + data = zend_hash_index_add_new(ht, idx, &d); + } + } else if (Z_TYPE(key) == IS_STRING) { + if (UNEXPECTED(ZEND_HANDLE_NUMERIC(Z_STR(key), idx))) { + goto numeric_key; + } + if (UNEXPECTED((old_data = zend_hash_find(ht, Z_STR(key))) != NULL)) { + //??? update hash + var_push_dtor(var_hash, old_data); + data = zend_hash_update(ht, Z_STR(key), &d); + } else { + data = zend_hash_add_new(ht, Z_STR(key), &d); + } + } else { + zval_ptr_dtor(&key); + goto failure; + } + + if (!php_var_unserialize_internal(data, p, max, var_hash, 0)) { + zval_ptr_dtor(&key); + goto failure; + } + + if (BG(unserialize).level > 1) { + var_push_dtor(var_hash, data); + } + zval_ptr_dtor_str(&key); + + if (elements && *(*p-1) != ';' && *(*p-1) != '}') { + (*p)--; + goto failure; + } + } + + if (var_hash) { + (*var_hash)->cur_depth--; + } + return 1; + +failure: + if (var_hash) { + (*var_hash)->cur_depth--; + } + return 0; +} + +static int is_property_visibility_changed(zend_class_entry *ce, zval *key) +{ + if (zend_hash_num_elements(&ce->properties_info) > 0) { + zend_property_info *existing_propinfo; + const char *unmangled_class = NULL; + const char *unmangled_prop; + size_t unmangled_prop_len; + + if (UNEXPECTED(zend_unmangle_property_name_ex(Z_STR_P(key), &unmangled_class, &unmangled_prop, &unmangled_prop_len) == FAILURE)) { + zval_ptr_dtor_str(key); + return -1; + } + + if (unmangled_class == NULL) { + existing_propinfo = zend_hash_find_ptr(&ce->properties_info, Z_STR_P(key)); + if (existing_propinfo != NULL) { + zval_ptr_dtor_str(key); + ZVAL_STR_COPY(key, existing_propinfo->name); + return 1; } } else { - if (EXPECTED(Z_TYPE(key) == IS_STRING)) { + if (!strcmp(unmangled_class, "*") + || !strcasecmp(unmangled_class, ZSTR_VAL(ce->name))) { + existing_propinfo = zend_hash_str_find_ptr( + &ce->properties_info, unmangled_prop, unmangled_prop_len); + if (existing_propinfo != NULL) { + zval_ptr_dtor_str(key); + ZVAL_STR_COPY(key, existing_propinfo->name); + return 1; + } + } + } + } + return 0; +} + + +static zend_always_inline int process_nested_object_data(UNSERIALIZE_PARAMETER, HashTable *ht, zend_long elements, zend_object *obj) +{ + if (var_hash) { + if ((*var_hash)->max_depth > 0 && (*var_hash)->cur_depth >= (*var_hash)->max_depth) { + php_error_docref(NULL, E_WARNING, + "Maximum depth of " ZEND_LONG_FMT " exceeded. " + "The depth limit can be changed using the max_depth unserialize() option " + "or the unserialize_max_depth ini setting", + (*var_hash)->max_depth); + return 0; + } + (*var_hash)->cur_depth++; + } + + while (elements-- > 0) { + zval key, *data, d, *old_data; + zend_property_info *info = NULL; + + ZVAL_UNDEF(&key); + + if (!php_var_unserialize_internal(&key, p, max, NULL, 1)) { + zval_ptr_dtor(&key); + goto failure; + } + + data = NULL; + ZVAL_UNDEF(&d); + + if (EXPECTED(Z_TYPE(key) == IS_STRING)) { string_key: - 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; - const char *unmangled_prop; - size_t unmangled_prop_len; - zend_string *unmangled; - - if (UNEXPECTED(zend_unmangle_property_name_ex(Z_STR(key), &unmangled_class, &unmangled_prop, &unmangled_prop_len) == FAILURE)) { - zval_ptr_dtor(&key); - goto failure; - } + if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) { + if (Z_TYPE_P(old_data) == IS_INDIRECT) { +declared_property: + /* This is a property with a declaration */ + old_data = Z_INDIRECT_P(old_data); + info = zend_get_typed_property_info_for_slot(obj, old_data); + if (info) { + if (Z_ISREF_P(old_data)) { + /* If the value is overwritten, remove old type source from ref. */ + ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(old_data), info); + } - unmangled = zend_string_init(unmangled_prop, unmangled_prop_len, 0); - - 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) { - new_key = zend_mangle_property_name( - "*", 1, ZSTR_VAL(unmangled), ZSTR_LEN(unmangled), 0); - zend_string_release_ex(unmangled, 0); - } else if (existing_propinfo->flags & ZEND_ACC_PRIVATE) { - if (unmangled_class != NULL && strcmp(unmangled_class, "*") != 0) { - new_key = zend_mangle_property_name( - unmangled_class, strlen(unmangled_class), - ZSTR_VAL(unmangled), ZSTR_LEN(unmangled), - 0); - } else { - new_key = zend_mangle_property_name( - ZSTR_VAL(existing_propinfo->ce->name), ZSTR_LEN(existing_propinfo->ce->name), - ZSTR_VAL(unmangled), ZSTR_LEN(unmangled), - 0); - } - zend_string_release_ex(unmangled, 0); - } else { - ZEND_ASSERT(existing_propinfo->flags & ZEND_ACC_PUBLIC); - new_key = unmangled; + if ((*var_hash)->ref_props) { + /* Remove old entry from ref_props table, if it exists. */ + zend_hash_index_del( + (*var_hash)->ref_props, (zend_uintptr_t) old_data); } - zval_ptr_dtor_str(&key); - ZVAL_STR(&key, new_key); + } + var_push_dtor(var_hash, old_data); + Z_TRY_DELREF_P(old_data); + ZVAL_UNDEF(old_data); + data = old_data; + } else { + int ret = is_property_visibility_changed(obj->ce, &key); + + if (EXPECTED(!ret)) { + var_push_dtor(var_hash, old_data); + data = zend_hash_update(ht, Z_STR(key), &d); + } else if (ret < 0) { + goto failure; } else { - zend_string_release_ex(unmangled, 0); + goto second_try; } } + } else { + int ret = is_property_visibility_changed(obj->ce, &key); - if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) { - if (Z_TYPE_P(old_data) == IS_INDIRECT) { - /* This is a property with a declaration */ - old_data = Z_INDIRECT_P(old_data); - info = zend_get_typed_property_info_for_slot(obj, old_data); - if (info) { - if (Z_ISREF_P(old_data)) { - /* If the value is overwritten, remove old type source from ref. */ - ZEND_REF_DEL_TYPE_SOURCE(Z_REF_P(old_data), info); - } - - if ((*var_hash)->ref_props) { - /* Remove old entry from ref_props table, if it exists. */ - zend_hash_index_del( - (*var_hash)->ref_props, (zend_uintptr_t) old_data); - } + if (EXPECTED(!ret)) { + data = zend_hash_add_new(ht, Z_STR(key), &d); + } else if (ret < 0) { + goto failure; + } else { +second_try: + if ((old_data = zend_hash_find(ht, Z_STR(key))) != NULL) { + if (Z_TYPE_P(old_data) == IS_INDIRECT) { + goto declared_property; + } else { + var_push_dtor(var_hash, old_data); + data = zend_hash_update(ht, Z_STR(key), &d); } - var_push_dtor(var_hash, old_data); - Z_TRY_DELREF_P(old_data); - ZVAL_COPY_VALUE(old_data, &d); - data = old_data; } else { - var_push_dtor(var_hash, old_data); - data = zend_hash_update_ind(ht, Z_STR(key), &d); + data = zend_hash_add_new(ht, Z_STR(key), &d); } - } else { - data = zend_hash_add_new(ht, Z_STR(key), &d); } - } else if (Z_TYPE(key) == IS_LONG) { - /* object properties should include no integers */ - convert_to_string(&key); - goto string_key; - } else { - zval_ptr_dtor(&key); - goto failure; } + zval_ptr_dtor_str(&key); + } else if (Z_TYPE(key) == IS_LONG) { + /* object properties should include no integers */ + convert_to_string(&key); + goto string_key; + } else { + zval_ptr_dtor(&key); + goto failure; } if (!php_var_unserialize_internal(data, p, max, var_hash, 0)) { @@ -595,7 +673,6 @@ string_key: * The data is still stored in the property. */ ZEND_REF_ADD_TYPE_SOURCE(Z_REF_P(data), info); } - zval_ptr_dtor(&key); goto failure; } @@ -603,7 +680,6 @@ string_key: if (!zend_verify_prop_assignable_by_ref(info, data, /* strict */ 1)) { zval_ptr_dtor(data); ZVAL_UNDEF(data); - zval_ptr_dtor_nogc(&key); goto failure; } @@ -624,7 +700,6 @@ string_key: if (BG(unserialize).level > 1) { var_push_dtor(var_hash, data); } - zval_ptr_dtor_str(&key); if (elements && *(*p-1) != ';' && *(*p-1) != '}') { (*p)--; @@ -688,10 +763,10 @@ static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce) #ifdef PHP_WIN32 # pragma optimize("", off) #endif -static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, zend_bool has_unserialize) +static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, bool has_unserialize) { HashTable *ht; - zend_bool has_wakeup; + bool has_wakeup; if (has_unserialize) { zval ary, *tmp; @@ -703,7 +778,7 @@ static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, zend_ array_init_size(&ary, elements); /* Avoid reallocation due to packed -> mixed conversion. */ zend_hash_real_init_mixed(Z_ARRVAL(ary)); - if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL(ary), elements, NULL)) { + if (!process_nested_array_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL(ary), elements)) { ZVAL_DEREF(rval); GC_ADD_FLAGS(Z_OBJ_P(rval), IS_OBJ_DESTRUCTOR_CALLED); zval_ptr_dtor(&ary); @@ -723,7 +798,7 @@ static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, zend_ } has_wakeup = Z_OBJCE_P(rval) != PHP_IC_ENTRY - && zend_hash_str_exists(&Z_OBJCE_P(rval)->function_table, "__wakeup", sizeof("__wakeup")-1); + && zend_hash_exists(&Z_OBJCE_P(rval)->function_table, ZSTR_KNOWN(ZEND_STR_WAKEUP)); ht = Z_OBJPROP_P(rval); if (elements >= (zend_long)(HT_MAX_SIZE - zend_hash_num_elements(ht))) { @@ -731,7 +806,7 @@ static inline int object_common(UNSERIALIZE_PARAMETER, zend_long elements, zend_ } zend_hash_extend(ht, zend_hash_num_elements(ht) + elements, HT_FLAGS(ht) & HASH_FLAG_PACKED); - if (!process_nested_data(UNSERIALIZE_PASSTHRU, ht, elements, Z_OBJ_P(rval))) { + if (!process_nested_object_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); @@ -1001,7 +1076,7 @@ use_double: *p = YYCURSOR; if (!var_hash) return 0; - if (elements < 0 || elements >= HT_MAX_SIZE || elements > max - YYCURSOR) { + if (elements < 0 || elements >= HT_MAX_SIZE || IS_FAKE_ELEM_COUNT(elements, max - YYCURSOR)) { return 0; } @@ -1021,7 +1096,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, NULL)) { + if (!process_nested_array_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_P(rval), elements)) { return 0; } @@ -1034,9 +1109,9 @@ object ":" uiv ":" ["] { char *str; zend_string *class_name; zend_class_entry *ce; - zend_bool incomplete_class = 0; - zend_bool custom_object = 0; - zend_bool has_unserialize = 0; + bool incomplete_class = 0; + bool custom_object = 0; + bool has_unserialize = 0; zval user_func; zval retval; @@ -1067,22 +1142,55 @@ object ":" uiv ":" ["] { return 0; } - class_name = zend_string_init(str, len, 0); - if (!zend_is_valid_class_name(class_name)) { - zend_string_release_ex(class_name, 0); + if (len == 0) { + /* empty class names are not allowed */ + return 0; + } + + if (str[0] == '\000') { + /* runtime definition keys are not allowed */ + return 0; + } + + if (str[0] == '\\') { + /* class name can't start from namespace separator */ return 0; } + class_name = zend_string_init(str, len, 0); + do { - if(!unserialize_allowed_class(class_name, var_hash)) { + zend_string *lc_name = zend_string_tolower(class_name); + + if(!unserialize_allowed_class(lc_name, var_hash)) { + zend_string_release_ex(lc_name, 0); + if (!zend_is_valid_class_name(class_name)) { + zend_string_release_ex(class_name, 0); + return 0; + } incomplete_class = 1; ce = PHP_IC_ENTRY; break; } + ce = zend_hash_find_ptr(EG(class_table), lc_name); + if (ce + && (ce->ce_flags & ZEND_ACC_LINKED) + && !(ce->ce_flags & ZEND_ACC_ANON_CLASS)) { + zend_string_release_ex(lc_name, 0); + break; + } + + if (!zend_is_valid_class_name(class_name)) { + zend_string_release_ex(lc_name, 0); + zend_string_release_ex(class_name, 0); + return 0; + } + /* Try to find class directly */ BG(serialize_lock)++; - ce = zend_lookup_class(class_name); + ce = zend_lookup_class_ex(class_name, lc_name, 0); + zend_string_release_ex(lc_name, 0); if (ce) { BG(serialize_lock)--; if (EG(exception)) { @@ -1169,7 +1277,7 @@ object ":" uiv ":" ["] { } elements = parse_iv2(*p + 2, p); - if (elements < 0 || elements > max - YYCURSOR) { + if (elements < 0 || IS_FAKE_ELEM_COUNT(elements, max - YYCURSOR)) { zend_string_release_ex(class_name, 0); return 0; } @@ -1201,6 +1309,86 @@ object ":" uiv ":" ["] { return object_common(UNSERIALIZE_PASSTHRU, elements, has_unserialize); } +"E:" uiv ":" ["] { + if (!var_hash) return 0; + + size_t len = parse_uiv(start + 2); + size_t maxlen = max - YYCURSOR; + if (maxlen < len || len == 0) { + *p = start + 2; + return 0; + } + + char *str = (char *) YYCURSOR; + YYCURSOR += len; + + if (*(YYCURSOR) != '"') { + *p = YYCURSOR; + return 0; + } + if (*(YYCURSOR+1) != ';') { + *p = YYCURSOR+1; + return 0; + } + + char *colon_ptr = memchr(str, ':', len); + if (colon_ptr == NULL) { + php_error_docref(NULL, E_WARNING, "Invalid enum name '%.*s' (missing colon)", (int) len, str); + return 0; + } + size_t colon_pos = colon_ptr - str; + + zend_string *enum_name = zend_string_init(str, colon_pos, 0); + zend_string *case_name = zend_string_init(&str[colon_pos + 1], len - colon_pos - 1, 0); + + if (!zend_is_valid_class_name(enum_name)) { + goto fail; + } + + zend_class_entry *ce = zend_lookup_class(enum_name); + if (!ce) { + php_error_docref(NULL, E_WARNING, "Class '%s' not found", ZSTR_VAL(enum_name)); + goto fail; + } + if (!(ce->ce_flags & ZEND_ACC_ENUM)) { + php_error_docref(NULL, E_WARNING, "Class '%s' is not an enum", ZSTR_VAL(enum_name)); + goto fail; + } + + YYCURSOR += 2; + *p = YYCURSOR; + + zend_class_constant *c = zend_hash_find_ptr(CE_CONSTANTS_TABLE(ce), case_name); + if (!c) { + php_error_docref(NULL, E_WARNING, "Undefined constant %s::%s", ZSTR_VAL(enum_name), ZSTR_VAL(case_name)); + goto fail; + } + + if (!(Z_ACCESS_FLAGS(c->value) & ZEND_CLASS_CONST_IS_CASE)) { + php_error_docref(NULL, E_WARNING, "%s::%s is not an enum case", ZSTR_VAL(enum_name), ZSTR_VAL(case_name)); + goto fail; + } + + zend_string_release_ex(enum_name, 0); + zend_string_release_ex(case_name, 0); + + zval *value = &c->value; + if (Z_TYPE_P(value) == IS_CONSTANT_AST) { + if (zval_update_constant_ex(value, c->ce) == FAILURE) { + return 0; + } + } + ZEND_ASSERT(Z_TYPE_P(value) == IS_OBJECT); + ZVAL_COPY(rval, value); + + return 1; + +fail: + zend_string_release_ex(enum_name, 0); + zend_string_release_ex(case_name, 0); + return 0; +} + "}" { /* this is the case where we have less data than planned */ php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data"); |