summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Wallner <mike@php.net>2013-07-29 17:59:35 +0200
committerMichael Wallner <mike@php.net>2013-08-20 00:05:11 +0200
commit1ac4d8f2c632f5be5a02d49c1e0d3b1fb515e4a8 (patch)
treebe7aff48fc4443b9df4514b1badd2b9e85d029a2
parentaa7d3d8e6d8de73ebe8dd015fb5392a4bde5bfc6 (diff)
downloadphp-git-1ac4d8f2c632f5be5a02d49c1e0d3b1fb515e4a8.tar.gz
fix bug #65481 (shutdown segfault due to serialize)
-rw-r--r--NEWS1
-rw-r--r--ext/standard/php_var.h1
-rw-r--r--ext/standard/tests/serialize/bug65481.phpt40
-rw-r--r--ext/standard/var_unserializer.c117
-rw-r--r--ext/standard/var_unserializer.re53
5 files changed, 154 insertions, 58 deletions
diff --git a/NEWS b/NEWS
index 64e049d70d..e56ff287f9 100644
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,7 @@ PHP NEWS
?? ??? 2013, PHP 5.4.19
- Core:
+ . Fixed bug #65481 (shutdown segfault due to serialize) (Mike)
. Fixed bug #65470 (Segmentation fault in zend_error() with
--enable-dtrace). (Chris Jones)
. Fixed bug #65372 (Segfault in gc_zval_possible_root when return reference
diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h
index 35343b3d5d..afc5f178e4 100644
--- a/ext/standard/php_var.h
+++ b/ext/standard/php_var.h
@@ -115,6 +115,7 @@ do { \
PHPAPI void var_replace(php_unserialize_data_t *var_hash, zval *ozval, zval **nzval);
PHPAPI void var_push_dtor(php_unserialize_data_t *var_hash, zval **val);
+PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rval);
PHPAPI void var_destroy(php_unserialize_data_t *var_hash);
#define PHP_VAR_UNSERIALIZE_ZVAL_CHANGED(var_hash, ozval, nzval) \
diff --git a/ext/standard/tests/serialize/bug65481.phpt b/ext/standard/tests/serialize/bug65481.phpt
new file mode 100644
index 0000000000..65634f63ba
--- /dev/null
+++ b/ext/standard/tests/serialize/bug65481.phpt
@@ -0,0 +1,40 @@
+--TEST--
+Bug #65481 (shutdown segfault due to serialize)
+--FILE--
+<?php
+echo "Test\n";
+
+class A {
+ public $e = array();
+}
+
+class Token implements \Serializable {
+ public function serialize()
+ {
+ $c = new A;
+
+ for ($i = 0; $i < 4; $i++)
+ {
+ $e = new A;
+ $c->e[] = $e;
+ $e->e = $c->e;
+ }
+
+ return serialize(array(serialize($c)));
+ }
+
+ public function unserialize($str)
+ {
+ $r = unserialize($str);
+ $r = unserialize($r[0]);
+ }
+}
+
+$token = new Token;
+$token = serialize($token);
+
+?>
+Done
+--EXPECT--
+Test
+Done
diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c
index d8bae08d2a..8a35e0a5af 100644
--- a/ext/standard/var_unserializer.c
+++ b/ext/standard/var_unserializer.c
@@ -1,4 +1,4 @@
-/* Generated by re2c 0.13.5 on Sat Mar 9 22:33:09 2013 */
+/* Generated by re2c 0.13.5 on Mon Jul 29 17:57:26 2013 */
#line 1 "ext/standard/var_unserializer.re"
/*
+----------------------------------------------------------------------+
@@ -26,6 +26,7 @@
/* {{{ reference-handling for unserializer: var_* */
#define VAR_ENTRIES_MAX 1024
+#define VAR_ENTRIES_DBG 0
typedef struct {
zval *data[VAR_ENTRIES_MAX];
@@ -36,7 +37,7 @@ typedef struct {
static inline void var_push(php_unserialize_data_t *var_hashx, zval **rval)
{
var_entries *var_hash = (*var_hashx)->last;
-#if 0
+#if VAR_ENTRIES_DBG
fprintf(stderr, "var_push(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
#endif
@@ -60,7 +61,7 @@ static inline void var_push(php_unserialize_data_t *var_hashx, zval **rval)
PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval)
{
var_entries *var_hash = (*var_hashx)->last_dtor;
-#if 0
+#if VAR_ENTRIES_DBG
fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
#endif
@@ -82,11 +83,35 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval)
var_hash->data[var_hash->used_slots++] = *rval;
}
+PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rval)
+{
+ var_entries *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
+
+ if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
+ var_hash = emalloc(sizeof(var_entries));
+ var_hash->used_slots = 0;
+ var_hash->next = 0;
+
+ if (!(*var_hashx)->first_dtor) {
+ (*var_hashx)->first_dtor = var_hash;
+ } else {
+ ((var_entries *) (*var_hashx)->last_dtor)->next = var_hash;
+ }
+
+ (*var_hashx)->last_dtor = var_hash;
+ }
+
+ var_hash->data[var_hash->used_slots++] = *rval;
+}
+
PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval **nzval)
{
long i;
var_entries *var_hash = (*var_hashx)->first;
-#if 0
+#if VAR_ENTRIES_DBG
fprintf(stderr, "var_replace(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(nzval));
#endif
@@ -104,7 +129,7 @@ PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval **n
static int var_access(php_unserialize_data_t *var_hashx, long id, zval ***store)
{
var_entries *var_hash = (*var_hashx)->first;
-#if 0
+#if VAR_ENTRIES_DBG
fprintf(stderr, "var_access(%ld): %ld\n", var_hash?var_hash->used_slots:-1L, id);
#endif
@@ -127,7 +152,7 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
void *next;
long i;
var_entries *var_hash = (*var_hashx)->first;
-#if 0
+#if VAR_ENTRIES_DBG
fprintf(stderr, "var_destroy(%ld)\n", var_hash?var_hash->used_slots:-1L);
#endif
@@ -201,7 +226,7 @@ static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen
#define YYMARKER marker
-#line 209 "ext/standard/var_unserializer.re"
+#line 234 "ext/standard/var_unserializer.re"
@@ -432,7 +457,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
-#line 436 "ext/standard/var_unserializer.c"
+#line 461 "ext/standard/var_unserializer.c"
{
YYCTYPE yych;
static const unsigned char yybm[] = {
@@ -492,9 +517,9 @@ yy2:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy95;
yy3:
-#line 785 "ext/standard/var_unserializer.re"
+#line 812 "ext/standard/var_unserializer.re"
{ return 0; }
-#line 498 "ext/standard/var_unserializer.c"
+#line 523 "ext/standard/var_unserializer.c"
yy4:
yych = *(YYMARKER = ++YYCURSOR);
if (yych == ':') goto yy89;
@@ -537,13 +562,13 @@ yy13:
goto yy3;
yy14:
++YYCURSOR;
-#line 779 "ext/standard/var_unserializer.re"
+#line 806 "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 547 "ext/standard/var_unserializer.c"
+#line 572 "ext/standard/var_unserializer.c"
yy16:
yych = *++YYCURSOR;
goto yy3;
@@ -573,7 +598,7 @@ yy20:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 635 "ext/standard/var_unserializer.re"
+#line 660 "ext/standard/var_unserializer.re"
{
size_t len, len2, len3, maxlen;
long elements;
@@ -625,9 +650,9 @@ yy20:
do {
/* Try to find class directly */
- BG(serialize_lock) = 1;
+ BG(serialize_lock)++;
if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) {
- BG(serialize_lock) = 0;
+ BG(serialize_lock)--;
if (EG(exception)) {
efree(class_name);
return 0;
@@ -635,7 +660,7 @@ yy20:
ce = *pce;
break;
}
- BG(serialize_lock) = 0;
+ BG(serialize_lock)--;
if (EG(exception)) {
efree(class_name);
@@ -655,9 +680,9 @@ yy20:
args[0] = &arg_func_name;
MAKE_STD_ZVAL(arg_func_name);
ZVAL_STRING(arg_func_name, class_name, 1);
- BG(serialize_lock) = 1;
+ BG(serialize_lock)++;
if (call_user_function_ex(CG(function_table), NULL, user_func, &retval_ptr, 1, args, 0, NULL TSRMLS_CC) != SUCCESS) {
- BG(serialize_lock) = 0;
+ BG(serialize_lock)--;
if (EG(exception)) {
efree(class_name);
zval_ptr_dtor(&user_func);
@@ -671,7 +696,7 @@ yy20:
zval_ptr_dtor(&arg_func_name);
break;
}
- BG(serialize_lock) = 0;
+ BG(serialize_lock)--;
if (retval_ptr) {
zval_ptr_dtor(&retval_ptr);
}
@@ -699,7 +724,9 @@ yy20:
*p = YYCURSOR;
if (custom_object) {
- int ret = object_custom(UNSERIALIZE_PASSTHRU, ce);
+ int ret;
+
+ ret = object_custom(UNSERIALIZE_PASSTHRU, ce);
if (ret && incomplete_class) {
php_store_class_name(*rval, class_name, len2);
@@ -717,7 +744,7 @@ yy20:
return object_common2(UNSERIALIZE_PASSTHRU, elements);
}
-#line 721 "ext/standard/var_unserializer.c"
+#line 748 "ext/standard/var_unserializer.c"
yy25:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -742,7 +769,7 @@ yy27:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 627 "ext/standard/var_unserializer.re"
+#line 652 "ext/standard/var_unserializer.re"
{
INIT_PZVAL(*rval);
@@ -750,7 +777,7 @@ yy27:
return object_common2(UNSERIALIZE_PASSTHRU,
object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR));
}
-#line 754 "ext/standard/var_unserializer.c"
+#line 781 "ext/standard/var_unserializer.c"
yy32:
yych = *++YYCURSOR;
if (yych == '+') goto yy33;
@@ -771,7 +798,7 @@ yy34:
yych = *++YYCURSOR;
if (yych != '{') goto yy18;
++YYCURSOR;
-#line 607 "ext/standard/var_unserializer.re"
+#line 632 "ext/standard/var_unserializer.re"
{
long elements = parse_iv(start + 2);
/* use iv() not uiv() in order to check data range */
@@ -791,7 +818,7 @@ yy34:
return finish_nested_data(UNSERIALIZE_PASSTHRU);
}
-#line 795 "ext/standard/var_unserializer.c"
+#line 822 "ext/standard/var_unserializer.c"
yy39:
yych = *++YYCURSOR;
if (yych == '+') goto yy40;
@@ -812,7 +839,7 @@ yy41:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 578 "ext/standard/var_unserializer.re"
+#line 603 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
char *str;
@@ -841,7 +868,7 @@ yy41:
ZVAL_STRINGL(*rval, str, len, 0);
return 1;
}
-#line 845 "ext/standard/var_unserializer.c"
+#line 872 "ext/standard/var_unserializer.c"
yy46:
yych = *++YYCURSOR;
if (yych == '+') goto yy47;
@@ -862,7 +889,7 @@ yy48:
yych = *++YYCURSOR;
if (yych != '"') goto yy18;
++YYCURSOR;
-#line 550 "ext/standard/var_unserializer.re"
+#line 575 "ext/standard/var_unserializer.re"
{
size_t len, maxlen;
char *str;
@@ -890,7 +917,7 @@ yy48:
ZVAL_STRINGL(*rval, str, len, 1);
return 1;
}
-#line 894 "ext/standard/var_unserializer.c"
+#line 921 "ext/standard/var_unserializer.c"
yy53:
yych = *++YYCURSOR;
if (yych <= '/') {
@@ -978,7 +1005,7 @@ yy61:
}
yy63:
++YYCURSOR;
-#line 540 "ext/standard/var_unserializer.re"
+#line 565 "ext/standard/var_unserializer.re"
{
#if SIZEOF_LONG == 4
use_double:
@@ -988,7 +1015,7 @@ use_double:
ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL));
return 1;
}
-#line 992 "ext/standard/var_unserializer.c"
+#line 1019 "ext/standard/var_unserializer.c"
yy65:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1047,7 +1074,7 @@ yy73:
yych = *++YYCURSOR;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 525 "ext/standard/var_unserializer.re"
+#line 550 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
@@ -1062,7 +1089,7 @@ yy73:
return 1;
}
-#line 1066 "ext/standard/var_unserializer.c"
+#line 1093 "ext/standard/var_unserializer.c"
yy76:
yych = *++YYCURSOR;
if (yych == 'N') goto yy73;
@@ -1089,7 +1116,7 @@ yy79:
if (yych <= '9') goto yy79;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 498 "ext/standard/var_unserializer.re"
+#line 523 "ext/standard/var_unserializer.re"
{
#if SIZEOF_LONG == 4
int digits = YYCURSOR - start - 3;
@@ -1116,7 +1143,7 @@ yy79:
ZVAL_LONG(*rval, parse_iv(start + 2));
return 1;
}
-#line 1120 "ext/standard/var_unserializer.c"
+#line 1147 "ext/standard/var_unserializer.c"
yy83:
yych = *++YYCURSOR;
if (yych <= '/') goto yy18;
@@ -1124,24 +1151,24 @@ yy83:
yych = *++YYCURSOR;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 491 "ext/standard/var_unserializer.re"
+#line 516 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
ZVAL_BOOL(*rval, parse_iv(start + 2));
return 1;
}
-#line 1135 "ext/standard/var_unserializer.c"
+#line 1162 "ext/standard/var_unserializer.c"
yy87:
++YYCURSOR;
-#line 484 "ext/standard/var_unserializer.re"
+#line 509 "ext/standard/var_unserializer.re"
{
*p = YYCURSOR;
INIT_PZVAL(*rval);
ZVAL_NULL(*rval);
return 1;
}
-#line 1145 "ext/standard/var_unserializer.c"
+#line 1172 "ext/standard/var_unserializer.c"
yy89:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1164,7 +1191,7 @@ yy91:
if (yych <= '9') goto yy91;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 461 "ext/standard/var_unserializer.re"
+#line 486 "ext/standard/var_unserializer.re"
{
long id;
@@ -1179,7 +1206,7 @@ yy91:
if (*rval == *rval_ref) return 0;
if (*rval != NULL) {
- zval_ptr_dtor(rval);
+ var_push_dtor_no_addref(var_hash, rval);
}
*rval = *rval_ref;
Z_ADDREF_PP(rval);
@@ -1187,7 +1214,7 @@ yy91:
return 1;
}
-#line 1191 "ext/standard/var_unserializer.c"
+#line 1218 "ext/standard/var_unserializer.c"
yy95:
yych = *++YYCURSOR;
if (yych <= ',') {
@@ -1210,7 +1237,7 @@ yy97:
if (yych <= '9') goto yy97;
if (yych != ';') goto yy18;
++YYCURSOR;
-#line 440 "ext/standard/var_unserializer.re"
+#line 465 "ext/standard/var_unserializer.re"
{
long id;
@@ -1231,9 +1258,9 @@ yy97:
return 1;
}
-#line 1235 "ext/standard/var_unserializer.c"
+#line 1262 "ext/standard/var_unserializer.c"
}
-#line 787 "ext/standard/var_unserializer.re"
+#line 814 "ext/standard/var_unserializer.re"
return 0;
diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re
index 4d99cbfd78..76c501e1b5 100644
--- a/ext/standard/var_unserializer.re
+++ b/ext/standard/var_unserializer.re
@@ -24,6 +24,7 @@
/* {{{ reference-handling for unserializer: var_* */
#define VAR_ENTRIES_MAX 1024
+#define VAR_ENTRIES_DBG 0
typedef struct {
zval *data[VAR_ENTRIES_MAX];
@@ -34,7 +35,7 @@ typedef struct {
static inline void var_push(php_unserialize_data_t *var_hashx, zval **rval)
{
var_entries *var_hash = (*var_hashx)->last;
-#if 0
+#if VAR_ENTRIES_DBG
fprintf(stderr, "var_push(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
#endif
@@ -58,7 +59,7 @@ static inline void var_push(php_unserialize_data_t *var_hashx, zval **rval)
PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval)
{
var_entries *var_hash = (*var_hashx)->last_dtor;
-#if 0
+#if VAR_ENTRIES_DBG
fprintf(stderr, "var_push_dtor(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(rval));
#endif
@@ -80,11 +81,35 @@ PHPAPI void var_push_dtor(php_unserialize_data_t *var_hashx, zval **rval)
var_hash->data[var_hash->used_slots++] = *rval;
}
+PHPAPI void var_push_dtor_no_addref(php_unserialize_data_t *var_hashx, zval **rval)
+{
+ var_entries *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
+
+ if (!var_hash || var_hash->used_slots == VAR_ENTRIES_MAX) {
+ var_hash = emalloc(sizeof(var_entries));
+ var_hash->used_slots = 0;
+ var_hash->next = 0;
+
+ if (!(*var_hashx)->first_dtor) {
+ (*var_hashx)->first_dtor = var_hash;
+ } else {
+ ((var_entries *) (*var_hashx)->last_dtor)->next = var_hash;
+ }
+
+ (*var_hashx)->last_dtor = var_hash;
+ }
+
+ var_hash->data[var_hash->used_slots++] = *rval;
+}
+
PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval **nzval)
{
long i;
var_entries *var_hash = (*var_hashx)->first;
-#if 0
+#if VAR_ENTRIES_DBG
fprintf(stderr, "var_replace(%ld): %d\n", var_hash?var_hash->used_slots:-1L, Z_TYPE_PP(nzval));
#endif
@@ -102,7 +127,7 @@ PHPAPI void var_replace(php_unserialize_data_t *var_hashx, zval *ozval, zval **n
static int var_access(php_unserialize_data_t *var_hashx, long id, zval ***store)
{
var_entries *var_hash = (*var_hashx)->first;
-#if 0
+#if VAR_ENTRIES_DBG
fprintf(stderr, "var_access(%ld): %ld\n", var_hash?var_hash->used_slots:-1L, id);
#endif
@@ -125,7 +150,7 @@ PHPAPI void var_destroy(php_unserialize_data_t *var_hashx)
void *next;
long i;
var_entries *var_hash = (*var_hashx)->first;
-#if 0
+#if VAR_ENTRIES_DBG
fprintf(stderr, "var_destroy(%ld)\n", var_hash?var_hash->used_slots:-1L);
#endif
@@ -472,7 +497,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER)
if (*rval == *rval_ref) return 0;
if (*rval != NULL) {
- zval_ptr_dtor(rval);
+ var_push_dtor_no_addref(var_hash, rval);
}
*rval = *rval_ref;
Z_ADDREF_PP(rval);
@@ -683,9 +708,9 @@ object ":" uiv ":" ["] {
do {
/* Try to find class directly */
- BG(serialize_lock) = 1;
+ BG(serialize_lock)++;
if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) {
- BG(serialize_lock) = 0;
+ BG(serialize_lock)--;
if (EG(exception)) {
efree(class_name);
return 0;
@@ -693,7 +718,7 @@ object ":" uiv ":" ["] {
ce = *pce;
break;
}
- BG(serialize_lock) = 0;
+ BG(serialize_lock)--;
if (EG(exception)) {
efree(class_name);
@@ -713,9 +738,9 @@ object ":" uiv ":" ["] {
args[0] = &arg_func_name;
MAKE_STD_ZVAL(arg_func_name);
ZVAL_STRING(arg_func_name, class_name, 1);
- BG(serialize_lock) = 1;
+ BG(serialize_lock)++;
if (call_user_function_ex(CG(function_table), NULL, user_func, &retval_ptr, 1, args, 0, NULL TSRMLS_CC) != SUCCESS) {
- BG(serialize_lock) = 0;
+ BG(serialize_lock)--;
if (EG(exception)) {
efree(class_name);
zval_ptr_dtor(&user_func);
@@ -729,7 +754,7 @@ object ":" uiv ":" ["] {
zval_ptr_dtor(&arg_func_name);
break;
}
- BG(serialize_lock) = 0;
+ BG(serialize_lock)--;
if (retval_ptr) {
zval_ptr_dtor(&retval_ptr);
}
@@ -757,7 +782,9 @@ object ":" uiv ":" ["] {
*p = YYCURSOR;
if (custom_object) {
- int ret = object_custom(UNSERIALIZE_PASSTHRU, ce);
+ int ret;
+
+ ret = object_custom(UNSERIALIZE_PASSTHRU, ce);
if (ret && incomplete_class) {
php_store_class_name(*rval, class_name, len2);