From 1ac4d8f2c632f5be5a02d49c1e0d3b1fb515e4a8 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Mon, 29 Jul 2013 17:59:35 +0200 Subject: [PATCH] fix bug #65481 (shutdown segfault due to serialize) --- NEWS | 1 + ext/standard/php_var.h | 1 + ext/standard/tests/serialize/bug65481.phpt | 40 +++++++ ext/standard/var_unserializer.c | 117 +++++++++++++-------- ext/standard/var_unserializer.re | 53 +++++++--- 5 files changed, 154 insertions(+), 58 deletions(-) create mode 100644 ext/standard/tests/serialize/bug65481.phpt 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-- +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); -- 2.40.0