]> granicus.if.org Git - php/commitdiff
Fix bug #70219 (Use after free vulnerability in session deserializer)
authorStanislav Malyshev <stas@php.net>
Sun, 23 Aug 2015 20:27:59 +0000 (13:27 -0700)
committerStanislav Malyshev <stas@php.net>
Mon, 24 Aug 2015 02:56:12 +0000 (19:56 -0700)
ext/session/session.c
ext/session/tests/session_decode_error2.phpt
ext/session/tests/session_decode_variation3.phpt
ext/standard/tests/serialize/bug70219.phpt [new file with mode: 0644]
ext/standard/var_unserializer.c
ext/standard/var_unserializer.re

index 306aba3a7de037e3a5d092f81e8a5d7a39294348..0e53c621338a34604b93444a315c731ae1d17252 100644 (file)
@@ -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(&current, (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(&current);
+                       var_push_dtor_no_addref(&var_hash, &current);
                }
                PS_ADD_VARL(name, namelen);
                efree(name);
@@ -947,8 +952,13 @@ PS_SERIALIZER_DECODE_FUNC(php) /* {{{ */
                        ALLOC_INIT_ZVAL(current);
                        if (php_var_unserialize(&current, (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, &current);
+                               efree(name);
+                               PHP_VAR_UNSERIALIZE_DESTROY(var_hash);
+                               return FAILURE;
                        }
-                       zval_ptr_dtor(&current);
+                       var_push_dtor_no_addref(&var_hash, &current);
                }
                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);
                        }
index 4160f87855afd976c0fe61209708536c9daba4a1..515047b67527ca5cac80210125b2f4807084b196 100644 (file)
@@ -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
index 4a6f7687139508e27f41b706c0a61aca459ce61d..096053171da16cc83cd7d9b238ed0ff062fbf2d2 100644 (file)
@@ -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 (file)
index 0000000..84a059f
--- /dev/null
@@ -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) {
+  }
+}
index ee0cac4762948b92f81b6f86d5e9658014b47a14..ffaf680c516b1e4503e6f81b6de1b0a49169e30d 100644 (file)
@@ -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"
index abac77ccea17e82dedcf9c170f555fbb4579953c..f02602cd7e76c9c9ac3c39e80a2da9951c9e1489 100644 (file)
@@ -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) {