diff options
Diffstat (limited to 'ext/standard')
-rw-r--r-- | ext/standard/tests/serialize/bug70172.phpt | 54 | ||||
-rw-r--r-- | ext/standard/tests/serialize/bug70172_2.phpt | 68 | ||||
-rw-r--r-- | ext/standard/tests/serialize/bug70219.phpt | 38 | ||||
-rw-r--r-- | ext/standard/var.c | 24 | ||||
-rw-r--r-- | ext/standard/var_unserializer.c | 135 | ||||
-rw-r--r-- | ext/standard/var_unserializer.re | 73 |
6 files changed, 291 insertions, 101 deletions
diff --git a/ext/standard/tests/serialize/bug70172.phpt b/ext/standard/tests/serialize/bug70172.phpt new file mode 100644 index 0000000000..0a4aa4be16 --- /dev/null +++ b/ext/standard/tests/serialize/bug70172.phpt @@ -0,0 +1,54 @@ +--TEST-- +Bug #70172 - Use After Free Vulnerability in unserialize() +--XFAIL-- +Memory leak on debug build, needs fix. +--FILE-- +<?php +class obj implements Serializable { + var $data; + function serialize() { + return serialize($this->data); + } + function unserialize($data) { + $this->data = unserialize($data); + } +} + +$fakezval = ptr2str(1122334455); +$fakezval .= ptr2str(0); +$fakezval .= "\x00\x00\x00\x00"; +$fakezval .= "\x01"; +$fakezval .= "\x00"; +$fakezval .= "\x00\x00"; + +$inner = 'r:2;'; +$exploit = 'a:2:{i:0;i:1;i:1;C:3:"obj":'.strlen($inner).':{'.$inner.'}}'; + +$data = unserialize($exploit); + +for ($i = 0; $i < 5; $i++) { + $v[$i] = $fakezval.$i; +} + +var_dump($data); + +function ptr2str($ptr) +{ + $out = ''; + for ($i = 0; $i < 8; $i++) { + $out .= chr($ptr & 0xff); + $ptr >>= 8; + } + return $out; +} +?> +--EXPECTF-- +array(2) { + [0]=> + int(1) + [1]=> + object(obj)#%d (1) { + ["data"]=> + int(1) + } +}
\ No newline at end of file diff --git a/ext/standard/tests/serialize/bug70172_2.phpt b/ext/standard/tests/serialize/bug70172_2.phpt new file mode 100644 index 0000000000..ae48341a46 --- /dev/null +++ b/ext/standard/tests/serialize/bug70172_2.phpt @@ -0,0 +1,68 @@ +--TEST-- +Bug #70172 - Use After Free Vulnerability in unserialize() +--FILE-- +<?php +class obj implements Serializable { + var $data; + function serialize() { + return serialize($this->data); + } + function unserialize($data) { + $this->data = unserialize($data); + } +} + +class obj2 { + var $ryat; + function __wakeup() { + $this->ryat = 1; + } +} + +$fakezval = ptr2str(1122334455); +$fakezval .= ptr2str(0); +$fakezval .= "\x00\x00\x00\x00"; +$fakezval .= "\x01"; +$fakezval .= "\x00"; +$fakezval .= "\x00\x00"; + +$inner = 'r:2;'; +$exploit = 'a:2:{i:0;O:4:"obj2":1:{s:4:"ryat";C:3:"obj":'.strlen($inner).':{'.$inner.'}}i:1;a:1:{i:0;a:1:{i:0;R:4;}}}'; + +$data = unserialize($exploit); + +for ($i = 0; $i < 5; $i++) { + $v[$i] = $fakezval.$i; +} + +var_dump($data); + +function ptr2str($ptr) +{ + $out = ''; + for ($i = 0; $i < 8; $i++) { + $out .= chr($ptr & 0xff); + $ptr >>= 8; + } + return $out; +} +?> +--EXPECTF-- +array(2) { + [0]=> + object(obj2)#%d (1) { + ["ryat"]=> + int(1) + } + [1]=> + array(1) { + [0]=> + array(1) { + [0]=> + object(obj2)#%d (1) { + ["ryat"]=> + int(1) + } + } + } +}
\ No newline at end of file diff --git a/ext/standard/tests/serialize/bug70219.phpt b/ext/standard/tests/serialize/bug70219.phpt new file mode 100644 index 0000000000..84a059f365 --- /dev/null +++ b/ext/standard/tests/serialize/bug70219.phpt @@ -0,0 +1,38 @@ +--TEST-- +Bug #70219 Use after free vulnerability in session deserializer +--FILE-- +<?php +class obj implements Serializable { + var $data; + function serialize() { + return serialize($this->data); + } + function unserialize($data) { + session_start(); + session_decode($data); + } +} + +$inner = 'ryat|a:1:{i:0;a:1:{i:1;'; +$exploit = 'a:2:{i:0;C:3:"obj":'.strlen($inner).':{'.$inner.'}i:1;R:4;}'; + +$data = unserialize($exploit); + +for ($i = 0; $i < 5; $i++) { + $v[$i] = 'hi'.$i; +} + +var_dump($data); +?> +--EXPECTF-- +Warning: session_decode(): Failed to decode session object. Session has been destroyed in %s on line %d +array(2) { + [0]=> + object(obj)#%d (1) { + ["data"]=> + NULL + } + [1]=> + array(0) { + } +} diff --git a/ext/standard/var.c b/ext/standard/var.c index 3f2c0d7887..8d8f68aa68 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -373,7 +373,7 @@ static int php_array_element_export(zval **zv TSRMLS_DC, int num_args, va_list a smart_str_appendc(buf, ','); smart_str_appendc(buf, '\n'); - + return 0; } /* }}} */ @@ -392,7 +392,7 @@ static int php_object_element_export(zval **zv TSRMLS_DC, int num_args, va_list const char *pname; char *pname_esc; int pname_esc_len; - + zend_unmangle_property_name(hash_key->arKey, hash_key->nKeyLength - 1, &class_name, &pname); pname_esc = php_addcslashes(pname, strlen(pname), &pname_esc_len, 0, @@ -469,7 +469,7 @@ PHPAPI void php_var_export_ex(zval **struc, int level, smart_str *buf TSRMLS_DC) buffer_append_spaces(buf, level - 1); } smart_str_appendc(buf, ')'); - + break; case IS_OBJECT: @@ -799,7 +799,7 @@ static void php_var_serialize_intern(smart_str *buf, zval *struc, HashTable *var BG(serialize_lock)++; res = call_user_function_ex(CG(function_table), &struc, &fname, &retval_ptr, 0, 0, 1, NULL TSRMLS_CC); BG(serialize_lock)--; - + if (EG(exception)) { if (retval_ptr) { zval_ptr_dtor(&retval_ptr); @@ -948,6 +948,8 @@ PHP_FUNCTION(unserialize) int buf_len; const unsigned char *p; php_unserialize_data_t var_hash; + int oldlevel; + zval *old_rval = return_value; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) { RETURN_FALSE; @@ -967,6 +969,20 @@ PHP_FUNCTION(unserialize) } RETURN_FALSE; } + if (return_value != old_rval) { + /* + * Terrible hack due to the fact that executor passes us zval *, + * but unserialize with r/R wants to replace it with another zval * + */ + zval_dtor(old_rval); + *old_rval = *return_value; + zval_copy_ctor(old_rval); + var_push_dtor_no_addref(&var_hash, &return_value); + /* FIXME: old_rval is not freed in some scenarios, see bug #70172 + var_push_dtor_no_addref(&var_hash, &old_rval); */ + } else { + var_push_dtor(&var_hash, &return_value); + } PHP_VAR_UNSERIALIZE_DESTROY(var_hash); } /* }}} */ diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index ac17529c20..575070b5b7 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -68,7 +68,7 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) var_hash = (*var_hashx)->last_dtor; #if VAR_ENTRIES_DBG - fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval)); + fprintf(stderr, "var_push_dtor(%p, %ld): %d\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval)); #endif if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) { @@ -91,9 +91,15 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rval) { - var_entries *var_hash = (*var_hashx)->last_dtor; + var_entries *var_hash; + + if (!var_hashx || !*var_hashx) { + return; + } + + var_hash = (*var_hashx)->last_dtor; #if VAR_ENTRIES_DBG - fprintf(stderr, "var_push_dtor_no_addref(%ld): %d (%d)\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval)); + fprintf(stderr, "var_push_dtor_no_addref(%p, %ld): %d (%d)\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval)); #endif if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) { @@ -120,7 +126,7 @@ PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval **n #if VAR_ENTRIES_DBG fprintf(stderr, "var_replace(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(nzval)); #endif - + while (var_hash) { for (i = 0; i < var_hash->used_slots; i++) { if (var_hash->data[i] == ozval) { @@ -138,7 +144,7 @@ static int var_access(php_unserialize_data_t *var_hashx, long id, zval ***store) #if VAR_ENTRIES_DBG fprintf(stderr, "var_access(%ld): %ld\n", var_hash?var_hash->used_slots:-1L, id); #endif - + while (id >= VAR_ENTRIES_MAX && var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) { var_hash = var_hash->next; id -= VAR_ENTRIES_MAX; @@ -161,7 +167,7 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) #if VAR_ENTRIES_DBG fprintf(stderr, "var_destroy(%ld)\n", var_hash?var_hash->used_slots:-1L); #endif - + while (var_hash) { next = var_hash->next; efree(var_hash); @@ -169,9 +175,12 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) } var_hash = (*var_hashx)->first_dtor; - + while (var_hash) { for (i = 0; i < var_hash->used_slots; i++) { +#if VAR_ENTRIES_DBG + fprintf(stderr, "var_destroy dtor(%p, %ld)\n", var_hash->data[i], Z_REFCOUNT_P(var_hash->data[i])); +#endif zval_ptr_dtor(&var_hash->data[i]); } next = var_hash->next; @@ -232,7 +241,7 @@ static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen #define YYMARKER marker -#line 240 "ext/standard/var_unserializer.re" +#line 249 "ext/standard/var_unserializer.re" @@ -250,7 +259,7 @@ static inline long parse_iv2(const unsigned char *p, const unsigned char **q) case '+': p++; } - + while (1) { cursor = (char)*p; if (cursor >= '0' && cursor <= '9') { @@ -279,7 +288,7 @@ static inline size_t parse_uiv(const unsigned char *p) if (*p == '+') { p++; } - + while (1) { cursor = *p; if (cursor >= '0' && cursor <= '9') { @@ -303,23 +312,20 @@ static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long ALLOC_INIT_ZVAL(key); if (!php_var_unserialize(&key, p, max, NULL TSRMLS_CC)) { - zval_dtor(key); - FREE_ZVAL(key); + var_push_dtor_no_addref(var_hash, &key); return 0; } if (Z_TYPE_P(key) != IS_LONG && Z_TYPE_P(key) != IS_STRING) { - zval_dtor(key); - FREE_ZVAL(key); + var_push_dtor_no_addref(var_hash, &key); return 0; } ALLOC_INIT_ZVAL(data); if (!php_var_unserialize(&data, p, max, var_hash TSRMLS_CC)) { - zval_dtor(key); - FREE_ZVAL(key); - zval_ptr_dtor(&data); + var_push_dtor_no_addref(var_hash, &key); + var_push_dtor_no_addref(var_hash, &data); return 0; } @@ -348,9 +354,7 @@ static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long sizeof data, NULL); } var_push_dtor(var_hash, &data); - - zval_dtor(key); - FREE_ZVAL(key); + var_push_dtor_no_addref(var_hash, &key); if (elements && *(*p-1) != ';' && *(*p-1) != '}') { (*p)--; @@ -400,7 +404,7 @@ static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce) static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) { long elements; - + elements = parse_iv2((*p) + 2, p); (*p) += 2; @@ -464,21 +468,21 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) limit = max; cursor = *p; - + if (YYCURSOR >= YYLIMIT) { return 0; } - + if (var_hash && cursor[0] != 'R') { var_push(var_hash, rval); } start = cursor; - - -#line 482 "ext/standard/var_unserializer.c" + + +#line 486 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -538,9 +542,9 @@ yy2: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: -#line 833 "ext/standard/var_unserializer.re" +#line 840 "ext/standard/var_unserializer.re" { return 0; } -#line 544 "ext/standard/var_unserializer.c" +#line 548 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@ -583,13 +587,13 @@ yy13: goto yy3; yy14: ++YYCURSOR; -#line 827 "ext/standard/var_unserializer.re" +#line 834 "ext/standard/var_unserializer.re" { /* this is the case where we have less data than planned */ php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Unexpected end of serialized data"); return 0; /* not sure if it should be 0 or 1 here? */ } -#line 593 "ext/standard/var_unserializer.c" +#line 597 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@ -620,7 +624,7 @@ yy20: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 681 "ext/standard/var_unserializer.re" +#line 687 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; long elements; @@ -636,10 +640,11 @@ yy20: zval **args[1]; zval *arg_func_name; + if (!var_hash) return 0; if (*start == 'C') { custom_object = 1; } - + INIT_PZVAL(*rval); len2 = len = parse_uiv(start + 2); maxlen = max - YYCURSOR; @@ -688,14 +693,14 @@ yy20: efree(class_name); return 0; } - + /* Check for unserialize callback */ if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) { incomplete_class = 1; ce = PHP_IC_ENTRY; break; } - + /* Call unserialize callback */ MAKE_STD_ZVAL(user_func); ZVAL_STRING(user_func, PG(unserialize_callback_func), 1); @@ -728,7 +733,7 @@ yy20: zval_ptr_dtor(&arg_func_name); return 0; } - + /* The callback function may have defined the class */ if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) { ce = *pce; @@ -756,7 +761,7 @@ yy20: efree(class_name); return ret; } - + elements = object_common1(UNSERIALIZE_PASSTHRU, ce); if (incomplete_class) { @@ -766,7 +771,7 @@ yy20: return object_common2(UNSERIALIZE_PASSTHRU, elements); } -#line 770 "ext/standard/var_unserializer.c" +#line 775 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@ -791,15 +796,16 @@ yy27: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 673 "ext/standard/var_unserializer.re" +#line 678 "ext/standard/var_unserializer.re" { + if (!var_hash) return 0; INIT_PZVAL(*rval); - + return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } -#line 803 "ext/standard/var_unserializer.c" +#line 809 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@ -820,11 +826,12 @@ yy34: yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; -#line 653 "ext/standard/var_unserializer.re" +#line 657 "ext/standard/var_unserializer.re" { long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ *p = YYCURSOR; + if (!var_hash) return 0; if (elements < 0) { return 0; @@ -840,7 +847,7 @@ yy34: return finish_nested_data(UNSERIALIZE_PASSTHRU); } -#line 844 "ext/standard/var_unserializer.c" +#line 851 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@ -861,7 +868,7 @@ yy41: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 624 "ext/standard/var_unserializer.re" +#line 628 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -890,7 +897,7 @@ yy41: ZVAL_STRINGL(*rval, str, len, 0); return 1; } -#line 894 "ext/standard/var_unserializer.c" +#line 901 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@ -911,7 +918,7 @@ yy48: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 596 "ext/standard/var_unserializer.re" +#line 600 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -939,7 +946,7 @@ yy48: ZVAL_STRINGL(*rval, str, len, 1); return 1; } -#line 943 "ext/standard/var_unserializer.c" +#line 950 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@ -1027,7 +1034,7 @@ yy61: } yy63: ++YYCURSOR; -#line 586 "ext/standard/var_unserializer.re" +#line 590 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 use_double: @@ -1037,7 +1044,7 @@ use_double: ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); return 1; } -#line 1041 "ext/standard/var_unserializer.c" +#line 1048 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@ -1096,7 +1103,7 @@ yy73: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 571 "ext/standard/var_unserializer.re" +#line 575 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); @@ -1111,7 +1118,7 @@ yy73: return 1; } -#line 1115 "ext/standard/var_unserializer.c" +#line 1122 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@ -1138,7 +1145,7 @@ yy79: if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; -#line 544 "ext/standard/var_unserializer.re" +#line 548 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 int digits = YYCURSOR - start - 3; @@ -1165,7 +1172,7 @@ yy79: ZVAL_LONG(*rval, parse_iv(start + 2)); return 1; } -#line 1169 "ext/standard/var_unserializer.c" +#line 1176 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@ -1173,24 +1180,24 @@ yy83: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 537 "ext/standard/var_unserializer.re" +#line 541 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_BOOL(*rval, parse_iv(start + 2)); return 1; } -#line 1184 "ext/standard/var_unserializer.c" +#line 1191 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; -#line 530 "ext/standard/var_unserializer.re" +#line 534 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); return 1; } -#line 1194 "ext/standard/var_unserializer.c" +#line 1201 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@ -1213,7 +1220,7 @@ yy91: if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; -#line 507 "ext/standard/var_unserializer.re" +#line 511 "ext/standard/var_unserializer.re" { long id; @@ -1233,10 +1240,10 @@ yy91: *rval = *rval_ref; Z_ADDREF_PP(rval); Z_UNSET_ISREF_PP(rval); - + return 1; } -#line 1240 "ext/standard/var_unserializer.c" +#line 1247 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@ -1259,7 +1266,7 @@ yy97: if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; -#line 486 "ext/standard/var_unserializer.re" +#line 490 "ext/standard/var_unserializer.re" { long id; @@ -1272,17 +1279,17 @@ yy97: } if (*rval != NULL) { - zval_ptr_dtor(rval); + var_push_dtor_no_addref(var_hash, rval); } *rval = *rval_ref; Z_ADDREF_PP(rval); Z_SET_ISREF_PP(rval); - + return 1; } -#line 1284 "ext/standard/var_unserializer.c" +#line 1291 "ext/standard/var_unserializer.c" } -#line 835 "ext/standard/var_unserializer.re" +#line 842 "ext/standard/var_unserializer.re" return 0; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 295acc55c4..8365b6cc2c 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -66,7 +66,7 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) var_hash = (*var_hashx)->last_dtor; #if VAR_ENTRIES_DBG - fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval)); + fprintf(stderr, "var_push_dtor(%p, %ld): %d\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval)); #endif if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) { @@ -89,9 +89,15 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval) PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rval) { - var_entries *var_hash = (*var_hashx)->last_dtor; + var_entries *var_hash; + + if (!var_hashx || !*var_hashx) { + return; + } + + var_hash = (*var_hashx)->last_dtor; #if VAR_ENTRIES_DBG - fprintf(stderr, "var_push_dtor_no_addref(%ld): %d (%d)\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval)); + fprintf(stderr, "var_push_dtor_no_addref(%p, %ld): %d (%d)\n", *rval, var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval), Z_REFCOUNT_PP(rval)); #endif if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) { @@ -118,7 +124,7 @@ PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval **n #if VAR_ENTRIES_DBG fprintf(stderr, "var_replace(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(nzval)); #endif - + while (var_hash) { for (i = 0; i < var_hash->used_slots; i++) { if (var_hash->data[i] == ozval) { @@ -136,7 +142,7 @@ static int var_access(php_unserialize_data_t *var_hashx, long id, zval ***store) #if VAR_ENTRIES_DBG fprintf(stderr, "var_access(%ld): %ld\n", var_hash?var_hash->used_slots:-1L, id); #endif - + while (id >= VAR_ENTRIES_MAX && var_hash && var_hash->used_slots == VAR_ENTRIES_MAX) { var_hash = var_hash->next; id -= VAR_ENTRIES_MAX; @@ -159,7 +165,7 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) #if VAR_ENTRIES_DBG fprintf(stderr, "var_destroy(%ld)\n", var_hash?var_hash->used_slots:-1L); #endif - + while (var_hash) { next = var_hash->next; efree(var_hash); @@ -167,9 +173,12 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx) } var_hash = (*var_hashx)->first_dtor; - + while (var_hash) { for (i = 0; i < var_hash->used_slots; i++) { +#if VAR_ENTRIES_DBG + fprintf(stderr, "var_destroy dtor(%p, %ld)\n", var_hash->data[i], Z_REFCOUNT_P(var_hash->data[i])); +#endif zval_ptr_dtor(&var_hash->data[i]); } next = var_hash->next; @@ -254,7 +263,7 @@ static inline long parse_iv2(const unsigned char *p, const unsigned char **q) case '+': p++; } - + while (1) { cursor = (char)*p; if (cursor >= '0' && cursor <= '9') { @@ -283,7 +292,7 @@ static inline size_t parse_uiv(const unsigned char *p) if (*p == '+') { p++; } - + while (1) { cursor = *p; if (cursor >= '0' && cursor <= '9') { @@ -307,23 +316,20 @@ static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long ALLOC_INIT_ZVAL(key); if (!php_var_unserialize(&key, p, max, NULL TSRMLS_CC)) { - zval_dtor(key); - FREE_ZVAL(key); + var_push_dtor_no_addref(var_hash, &key); return 0; } if (Z_TYPE_P(key) != IS_LONG && Z_TYPE_P(key) != IS_STRING) { - zval_dtor(key); - FREE_ZVAL(key); + var_push_dtor_no_addref(var_hash, &key); return 0; } ALLOC_INIT_ZVAL(data); if (!php_var_unserialize(&data, p, max, var_hash TSRMLS_CC)) { - zval_dtor(key); - FREE_ZVAL(key); - zval_ptr_dtor(&data); + var_push_dtor_no_addref(var_hash, &key); + var_push_dtor_no_addref(var_hash, &data); return 0; } @@ -352,9 +358,7 @@ static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long sizeof data, NULL); } var_push_dtor(var_hash, &data); - - zval_dtor(key); - FREE_ZVAL(key); + var_push_dtor_no_addref(var_hash, &key); if (elements && *(*p-1) != ';' && *(*p-1) != '}') { (*p)--; @@ -404,7 +408,7 @@ static inline int object_custom(UNSERIALIZE_PARAMETER, zend_class_entry *ce) static inline long object_common1(UNSERIALIZE_PARAMETER, zend_class_entry *ce) { long elements; - + elements = parse_iv2((*p) + 2, p); (*p) += 2; @@ -468,19 +472,19 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) limit = max; cursor = *p; - + if (YYCURSOR >= YYLIMIT) { return 0; } - + if (var_hash && cursor[0] != 'R') { var_push(var_hash, rval); } start = cursor; - - + + /*!re2c "R:" iv ";" { @@ -495,12 +499,12 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) } if (*rval != NULL) { - zval_ptr_dtor(rval); + var_push_dtor_no_addref(var_hash, rval); } *rval = *rval_ref; Z_ADDREF_PP(rval); Z_SET_ISREF_PP(rval); - + return 1; } @@ -523,7 +527,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) *rval = *rval_ref; Z_ADDREF_PP(rval); Z_UNSET_ISREF_PP(rval); - + return 1; } @@ -654,6 +658,7 @@ use_double: long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ *p = YYCURSOR; + if (!var_hash) return 0; if (elements < 0) { return 0; @@ -671,9 +676,10 @@ use_double: } "o:" iv ":" ["] { + if (!var_hash) return 0; INIT_PZVAL(*rval); - + return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } @@ -693,10 +699,11 @@ object ":" uiv ":" ["] { zval **args[1]; zval *arg_func_name; + if (!var_hash) return 0; if (*start == 'C') { custom_object = 1; } - + INIT_PZVAL(*rval); len2 = len = parse_uiv(start + 2); maxlen = max - YYCURSOR; @@ -745,14 +752,14 @@ object ":" uiv ":" ["] { efree(class_name); return 0; } - + /* Check for unserialize callback */ if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) { incomplete_class = 1; ce = PHP_IC_ENTRY; break; } - + /* Call unserialize callback */ MAKE_STD_ZVAL(user_func); ZVAL_STRING(user_func, PG(unserialize_callback_func), 1); @@ -785,7 +792,7 @@ object ":" uiv ":" ["] { zval_ptr_dtor(&arg_func_name); return 0; } - + /* The callback function may have defined the class */ if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) { ce = *pce; @@ -813,7 +820,7 @@ object ":" uiv ":" ["] { efree(class_name); return ret; } - + elements = object_common1(UNSERIALIZE_PASSTHRU, ce); if (incomplete_class) { |