summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStanislav Malyshev <stas@php.net>2015-08-23 13:27:59 -0700
committerStanislav Malyshev <stas@php.net>2015-08-23 19:56:12 -0700
commitdf4bf28f9f104ca3ef78ed94b497859f15b004e5 (patch)
treece5ddb98f2f77189bf741bb1c4fccdc3af753a08
parent1744be2d17befc69bf00033993f4081852a747d6 (diff)
downloadphp-git-df4bf28f9f104ca3ef78ed94b497859f15b004e5.tar.gz
Fix bug #70219 (Use after free vulnerability in session deserializer)
-rw-r--r--ext/session/session.c36
-rw-r--r--ext/session/tests/session_decode_error2.phpt518
-rw-r--r--ext/session/tests/session_decode_variation3.phpt2
-rw-r--r--ext/standard/tests/serialize/bug70219.phpt38
-rw-r--r--ext/standard/var_unserializer.c68
-rw-r--r--ext/standard/var_unserializer.re64
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(&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);
}
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) {