diff options
author | Stanislav Malyshev <stas@php.net> | 2015-08-23 13:27:59 -0700 |
---|---|---|
committer | Stanislav Malyshev <stas@php.net> | 2015-08-23 19:56:12 -0700 |
commit | df4bf28f9f104ca3ef78ed94b497859f15b004e5 (patch) | |
tree | ce5ddb98f2f77189bf741bb1c4fccdc3af753a08 | |
parent | 1744be2d17befc69bf00033993f4081852a747d6 (diff) | |
download | php-git-df4bf28f9f104ca3ef78ed94b497859f15b004e5.tar.gz |
Fix bug #70219 (Use after free vulnerability in session deserializer)
-rw-r--r-- | ext/session/session.c | 36 | ||||
-rw-r--r-- | ext/session/tests/session_decode_error2.phpt | 518 | ||||
-rw-r--r-- | ext/session/tests/session_decode_variation3.phpt | 2 | ||||
-rw-r--r-- | ext/standard/tests/serialize/bug70219.phpt | 38 | ||||
-rw-r--r-- | ext/standard/var_unserializer.c | 68 | ||||
-rw-r--r-- | ext/standard/var_unserializer.re | 64 |
6 files changed, 228 insertions, 498 deletions
diff --git a/ext/session/session.c b/ext/session/session.c index 306aba3a7d..0e53c62133 100644 --- a/ext/session/session.c +++ b/ext/session/session.c @@ -210,16 +210,18 @@ static char *php_session_encode(int *newlen TSRMLS_DC) /* {{{ */ } /* }}} */ -static void php_session_decode(const char *val, int vallen TSRMLS_DC) /* {{{ */ +static int php_session_decode(const char *val, int vallen TSRMLS_DC) /* {{{ */ { if (!PS(serializer)) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown session.serialize_handler. Failed to decode session object"); - return; + return FAILURE; } if (PS(serializer)->decode(val, vallen TSRMLS_CC) == FAILURE) { php_session_destroy(TSRMLS_C); php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to decode session object. Session has been destroyed"); + return FAILURE; } + return SUCCESS; } /* }}} */ @@ -413,7 +415,7 @@ PHPAPI char *php_session_create_id(PS_CREATE_SID_ARGS) /* {{{ */ php_error_docref(NULL TSRMLS_CC, E_WARNING, "The ini setting hash_bits_per_character is out of range (should be 4, 5, or 6) - using 4 for now"); } - + outid = emalloc((size_t)((digest_len + 2) * ((8.0f / PS(hash_bits_per_character)) + 0.5))); j = (int) (bin_to_readable((char *)digest, digest_len, outid, (char)PS(hash_bits_per_character)) - outid); efree(digest); @@ -855,8 +857,11 @@ PS_SERIALIZER_DECODE_FUNC(php_binary) /* {{{ */ ALLOC_INIT_ZVAL(current); if (php_var_unserialize(¤t, (const unsigned char **) &p, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) { php_set_session_var(name, namelen, current, &var_hash TSRMLS_CC); + } else { + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + return FAILURE; } - zval_ptr_dtor(¤t); + var_push_dtor_no_addref(&var_hash, ¤t); } PS_ADD_VARL(name, namelen); efree(name); @@ -947,8 +952,13 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */ ALLOC_INIT_ZVAL(current); if (php_var_unserialize(¤t, (const unsigned char **) &q, (const unsigned char *) endptr, &var_hash TSRMLS_CC)) { php_set_session_var(name, namelen, current, &var_hash TSRMLS_CC); + } else { + var_push_dtor_no_addref(&var_hash, ¤t); + efree(name); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + return FAILURE; } - zval_ptr_dtor(¤t); + var_push_dtor_no_addref(&var_hash, ¤t); } PS_ADD_VARL(name, namelen); skip: @@ -1744,7 +1754,7 @@ static PHP_FUNCTION(session_set_save_handler) } efree(name); } - + if (PS(mod) && PS(mod) != &ps_mod_user) { zend_alter_ini_entry("session.save_handler", sizeof("session.save_handler"), "user", sizeof("user")-1, PHP_INI_USER, PHP_INI_STAGE_RUNTIME); } @@ -1922,9 +1932,7 @@ static PHP_FUNCTION(session_decode) return; } - php_session_decode(str, str_len TSRMLS_CC); - - RETURN_TRUE; + RETVAL_BOOL(php_session_decode(str, str_len TSRMLS_CC) == SUCCESS); } /* }}} */ @@ -2516,12 +2524,12 @@ static int php_session_rfc1867_callback(unsigned int event, void *event_data, vo case MULTIPART_EVENT_FILE_START: { multipart_event_file_start *data = (multipart_event_file_start *) event_data; - /* Do nothing when $_POST["PHP_SESSION_UPLOAD_PROGRESS"] is not set + /* Do nothing when $_POST["PHP_SESSION_UPLOAD_PROGRESS"] is not set * or when we have no session id */ if (!Z_TYPE(progress->sid) || !progress->key.c) { break; } - + /* First FILE_START event, initializing data */ if (!progress->data) { @@ -2571,7 +2579,7 @@ static int php_session_rfc1867_callback(unsigned int event, void *event_data, vo add_assoc_zval_ex(progress->current_file, "bytes_processed", sizeof("bytes_processed"), progress->current_file_bytes_processed); add_next_index_zval(progress->files, progress->current_file); - + Z_LVAL_P(progress->post_bytes_processed) = data->post_bytes_processed; php_session_rfc1867_update(progress, 0 TSRMLS_CC); @@ -2583,7 +2591,7 @@ static int php_session_rfc1867_callback(unsigned int event, void *event_data, vo if (!Z_TYPE(progress->sid) || !progress->key.c) { break; } - + Z_LVAL_P(progress->current_file_bytes_processed) = data->offset + data->length; Z_LVAL_P(progress->post_bytes_processed) = data->post_bytes_processed; @@ -2596,7 +2604,7 @@ static int php_session_rfc1867_callback(unsigned int event, void *event_data, vo if (!Z_TYPE(progress->sid) || !progress->key.c) { break; } - + if (data->temp_filename) { add_assoc_string_ex(progress->current_file, "tmp_name", sizeof("tmp_name"), data->temp_filename, 1); } diff --git a/ext/session/tests/session_decode_error2.phpt b/ext/session/tests/session_decode_error2.phpt index 4160f87855..515047b675 100644 --- a/ext/session/tests/session_decode_error2.phpt +++ b/ext/session/tests/session_decode_error2.phpt @@ -53,563 +53,247 @@ array(0) { } -- Iteration 4 -- -bool(true) -array(1) { - ["foo"]=> - NULL + +Warning: session_decode(): Failed to decode session object. Session has been destroyed in %s/session_decode_error2.php on line %d +bool(false) +array(0) { } -- Iteration 5 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 6 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 7 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 8 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 9 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 10 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 11 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 12 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 13 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 14 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 15 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 16 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 17 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 18 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 19 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 20 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 21 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 22 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 23 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 24 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 25 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 26 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 27 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 28 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 29 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 30 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 31 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 32 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 33 -- -bool(true) -array(1) { - ["foo"]=> - NULL +bool(false) +array(0) { } -- Iteration 34 -- -bool(true) -array(1) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 35 -- -bool(true) -array(1) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 36 -- -bool(true) -array(1) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 37 -- -bool(true) -array(1) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 38 -- -bool(true) -array(1) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 39 -- -bool(true) -array(2) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - NULL +bool(false) +array(0) { } -- Iteration 40 -- -bool(true) -array(2) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - NULL +bool(false) +array(0) { } -- Iteration 41 -- -bool(true) -array(2) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - NULL +bool(false) +array(0) { } -- Iteration 42 -- -bool(true) -array(2) { - ["foo"]=> - array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - NULL +bool(false) +array(0) { } -- Iteration 43 -- -bool(true) -array(2) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 44 -- -bool(true) -array(2) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 45 -- -bool(true) -array(2) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 46 -- -bool(true) -array(2) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 47 -- -bool(true) -array(2) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } +bool(false) +array(0) { } -- Iteration 48 -- -bool(true) -array(3) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["blah"]=> - NULL +bool(false) +array(0) { } -- Iteration 49 -- -bool(true) -array(3) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["blah"]=> - NULL +bool(false) +array(0) { } -- Iteration 50 -- -bool(true) -array(3) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["blah"]=> - NULL +bool(false) +array(0) { } -- Iteration 51 -- -bool(true) -array(3) { - ["foo"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["guff"]=> - &array(3) { - [0]=> - int(1) - [1]=> - int(2) - [2]=> - int(3) - } - ["blah"]=> - NULL +bool(false) +array(0) { } -bool(true) -Done +Warning: session_destroy(): Trying to destroy uninitialized session in %s/session_decode_error2.php on line %d +bool(false) +Done diff --git a/ext/session/tests/session_decode_variation3.phpt b/ext/session/tests/session_decode_variation3.phpt index 4a6f768713..096053171d 100644 --- a/ext/session/tests/session_decode_variation3.phpt +++ b/ext/session/tests/session_decode_variation3.phpt @@ -49,7 +49,7 @@ array(3) { } Warning: session_decode(): Unknown session.serialize_handler. Failed to decode session object in %s on line %d -bool(true) +bool(false) array(3) { ["foo"]=> int(1234567890) 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_unserializer.c b/ext/standard/var_unserializer.c index ee0cac4762..ffaf680c51 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.7.5 on Tue Mar 17 13:14:30 2015 */ +/* Generated by re2c 0.13.7.5 on Sun Aug 23 19:50:03 2015 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -92,7 +92,13 @@ 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)); #endif @@ -121,7 +127,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) { @@ -139,7 +145,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; @@ -162,7 +168,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); @@ -170,7 +176,7 @@ 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++) { zval_ptr_dtor(&var_hash->data[i]); @@ -233,7 +239,7 @@ static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen #define YYMARKER marker -#line 241 "ext/standard/var_unserializer.re" +#line 247 "ext/standard/var_unserializer.re" @@ -251,7 +257,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') { @@ -280,7 +286,7 @@ static inline size_t parse_uiv(const unsigned char *p) if (*p == '+') { p++; } - + while (1) { cursor = *p; if (cursor >= '0' && cursor <= '9') { @@ -304,24 +310,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_dtor(data); - FREE_ZVAL(data); + var_push_dtor_no_addref(var_hash, &key); + var_push_dtor_no_addref(var_hash, &data); return 0; } @@ -350,9 +352,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)--; @@ -402,11 +402,11 @@ 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; - + /* The internal class check here is a BC fix only, userspace classes implementing the Serializable interface have eventually an inconsistent behavior at this place when unserialized from a manipulated string. Additionaly the interal classes can possibly @@ -470,19 +470,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; - - + + #line 488 "ext/standard/var_unserializer.c" { @@ -645,7 +645,7 @@ yy20: if (*start == 'C') { custom_object = 1; } - + INIT_PZVAL(*rval); len2 = len = parse_uiv(start + 2); maxlen = max - YYCURSOR; @@ -694,14 +694,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); @@ -734,7 +734,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; @@ -762,7 +762,7 @@ yy20: efree(class_name); return ret; } - + elements = object_common1(UNSERIALIZE_PASSTHRU, ce); if (incomplete_class) { @@ -801,7 +801,7 @@ yy27: { INIT_PZVAL(*rval); - + return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } @@ -1239,7 +1239,7 @@ yy91: *rval = *rval_ref; Z_ADDREF_PP(rval); Z_UNSET_ISREF_PP(rval); - + return 1; } #line 1246 "ext/standard/var_unserializer.c" @@ -1283,7 +1283,7 @@ yy97: *rval = *rval_ref; Z_ADDREF_PP(rval); Z_SET_ISREF_PP(rval); - + return 1; } #line 1290 "ext/standard/var_unserializer.c" diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index abac77ccea..f02602cd7e 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -90,7 +90,13 @@ 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)); #endif @@ -119,7 +125,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) { @@ -137,7 +143,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; @@ -160,7 +166,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); @@ -168,7 +174,7 @@ 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++) { zval_ptr_dtor(&var_hash->data[i]); @@ -255,7 +261,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') { @@ -284,7 +290,7 @@ static inline size_t parse_uiv(const unsigned char *p) if (*p == '+') { p++; } - + while (1) { cursor = *p; if (cursor >= '0' && cursor <= '9') { @@ -308,24 +314,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_dtor(data); - FREE_ZVAL(data); + var_push_dtor_no_addref(var_hash, &key); + var_push_dtor_no_addref(var_hash, &data); return 0; } @@ -354,9 +356,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)--; @@ -406,11 +406,11 @@ 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; - + /* The internal class check here is a BC fix only, userspace classes implementing the Serializable interface have eventually an inconsistent behavior at this place when unserialized from a manipulated string. Additionaly the interal classes can possibly @@ -474,19 +474,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 ";" { @@ -506,7 +506,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) *rval = *rval_ref; Z_ADDREF_PP(rval); Z_SET_ISREF_PP(rval); - + return 1; } @@ -529,7 +529,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) *rval = *rval_ref; Z_ADDREF_PP(rval); Z_UNSET_ISREF_PP(rval); - + return 1; } @@ -679,7 +679,7 @@ use_double: "o:" iv ":" ["] { INIT_PZVAL(*rval); - + return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } @@ -702,7 +702,7 @@ object ":" uiv ":" ["] { if (*start == 'C') { custom_object = 1; } - + INIT_PZVAL(*rval); len2 = len = parse_uiv(start + 2); maxlen = max - YYCURSOR; @@ -751,14 +751,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); @@ -791,7 +791,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; @@ -819,7 +819,7 @@ object ":" uiv ":" ["] { efree(class_name); return ret; } - + elements = object_common1(UNSERIALIZE_PASSTHRU, ce); if (incomplete_class) { |