summaryrefslogtreecommitdiff
path: root/ext/standard
diff options
context:
space:
mode:
Diffstat (limited to 'ext/standard')
-rw-r--r--ext/standard/tests/serialize/bug70172.phpt54
-rw-r--r--ext/standard/tests/serialize/bug70172_2.phpt68
-rw-r--r--ext/standard/tests/serialize/bug70219.phpt38
-rw-r--r--ext/standard/var.c24
-rw-r--r--ext/standard/var_unserializer.c135
-rw-r--r--ext/standard/var_unserializer.re73
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) {