diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h index 35343b3d5d068..4deabcfe6a768 100644 --- a/ext/standard/php_var.h +++ b/ext/standard/php_var.h @@ -51,6 +51,7 @@ typedef struct php_unserialize_data* php_unserialize_data_t; PHPAPI void php_var_serialize(smart_str *buf, zval **struc, php_serialize_data_t *var_hash TSRMLS_DC); PHPAPI int php_var_unserialize(zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC); +PHPAPI int php_var_unserialize_ex(zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes TSRMLS_DC); #define PHP_VAR_SERIALIZE_INIT(var_hash_ptr) \ do { \ diff --git a/ext/standard/tests/serialize/serialization_error_001.phpt b/ext/standard/tests/serialize/serialization_error_001.phpt index da6f50cc02b29..014128b19f51a 100644 --- a/ext/standard/tests/serialize/serialization_error_001.phpt +++ b/ext/standard/tests/serialize/serialization_error_001.phpt @@ -21,7 +21,7 @@ var_dump( unserialize() ); //Test serialize with one more than the expected number of arguments var_dump( serialize(1,2) ); -var_dump( unserialize(1,2) ); +var_dump( unserialize(1,2,3) ); echo "Done"; ?> @@ -31,12 +31,12 @@ echo "Done"; Warning: serialize() expects exactly 1 parameter, 0 given in %s on line 16 NULL -Warning: unserialize() expects exactly 1 parameter, 0 given in %s on line 17 +Warning: unserialize() expects at least 1 parameter, 0 given in %s on line 17 bool(false) Warning: serialize() expects exactly 1 parameter, 2 given in %s on line 20 NULL -Warning: unserialize() expects exactly 1 parameter, 2 given in %s on line 21 +Warning: unserialize() expects at most 2 parameters, 3 given in %s on line 21 bool(false) Done diff --git a/ext/standard/tests/serialize/unserialize_classes.phpt b/ext/standard/tests/serialize/unserialize_classes.phpt new file mode 100644 index 0000000000000..754e5cfb19fe2 --- /dev/null +++ b/ext/standard/tests/serialize/unserialize_classes.phpt @@ -0,0 +1,88 @@ +--TEST-- +Test unserialize() with second parameter +--FILE-- + + object(foo)#%d (1) { + ["x"]=> + string(3) "bar" + } + [1]=> + int(2) + [2]=> + string(1) "3" +} +array(3) { + [0]=> + object(__PHP_Incomplete_Class)#%d (2) { + ["__PHP_Incomplete_Class_Name"]=> + string(3) "foo" + ["x"]=> + string(3) "bar" + } + [1]=> + int(2) + [2]=> + string(1) "3" +} +array(3) { + [0]=> + object(foo)#%d (1) { + ["x"]=> + string(3) "bar" + } + [1]=> + int(2) + [2]=> + string(1) "3" +} +array(3) { + [0]=> + object(__PHP_Incomplete_Class)#%d (2) { + ["__PHP_Incomplete_Class_Name"]=> + string(3) "foo" + ["x"]=> + string(3) "bar" + } + [1]=> + int(2) + [2]=> + string(1) "3" +} +array(3) { + [0]=> + object(foo)#%d (1) { + ["x"]=> + string(3) "bar" + } + [1]=> + int(2) + [2]=> + string(1) "3" +} +array(3) { + [0]=> + object(foo)#%d (1) { + ["x"]=> + string(3) "bar" + } + [1]=> + int(2) + [2]=> + string(1) "3" +} diff --git a/ext/standard/var.c b/ext/standard/var.c index f76a14cfa6641..4d18cf61198db 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -945,7 +945,7 @@ PHP_FUNCTION(serialize) } /* }}} */ -/* {{{ proto mixed unserialize(string variable_representation) +/* {{{ proto mixed unserialize(string variable_representation[, bool|array allowed_classes]) Takes a string representation of variable and recreates it */ PHP_FUNCTION(unserialize) { @@ -953,8 +953,10 @@ PHP_FUNCTION(unserialize) int buf_len; const unsigned char *p; php_unserialize_data_t var_hash; + zval *classes = NULL; + HashTable *class_hash = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &buf, &buf_len, &classes) == FAILURE) { RETURN_FALSE; } @@ -964,8 +966,35 @@ PHP_FUNCTION(unserialize) p = (const unsigned char*) buf; PHP_VAR_UNSERIALIZE_INIT(var_hash); - if (!php_var_unserialize(&return_value, &p, p + buf_len, &var_hash TSRMLS_CC)) { + if(classes != NULL) { + if(Z_TYPE_P(classes) == IS_ARRAY || !zend_is_true(classes)) { + ALLOC_HASHTABLE(class_hash); + zend_hash_init(class_hash, (Z_TYPE_P(classes) == IS_ARRAY)?zend_hash_num_elements(Z_ARRVAL_P(classes)):0, NULL, NULL, 0); + } + if(class_hash && Z_TYPE_P(classes) == IS_ARRAY) { + HashPosition pos; + HashTable *ht = Z_ARRVAL_P(classes); + zval **data; + ALLOCA_FLAG(use_heap) + + zend_hash_internal_pointer_reset_ex(ht, &pos); + while(zend_hash_get_current_data_ex(ht, (void **)&data, &pos) == SUCCESS) { + char *lc_name; + convert_to_string_ex(data); + lc_name = do_alloca(Z_STRLEN_PP(data) + 1, use_heap); + zend_str_tolower_copy(lc_name, Z_STRVAL_PP(data), Z_STRLEN_PP(data)); + zend_hash_add_empty_element(class_hash, lc_name, Z_STRLEN_PP(data)); + free_alloca(lc_name, use_heap); + zend_hash_move_forward_ex(ht, &pos); + } + } + } + + if (!php_var_unserialize_ex(&return_value, &p, p + buf_len, &var_hash, class_hash TSRMLS_CC)) { PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + if(class_hash) { + zend_hash_destroy(class_hash); + } zval_dtor(return_value); if (!EG(exception)) { php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len); @@ -973,6 +1002,9 @@ PHP_FUNCTION(unserialize) RETURN_FALSE; } PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + if(class_hash) { + zend_hash_destroy(class_hash); + } } /* }}} */ diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index d8bae08d2a599..cfb182097d69a 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 Sat Mar 30 12:24:34 2013 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -194,6 +194,25 @@ static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen return str; } +static inline int unserialize_allowed_class(const char *class_name, int len, HashTable *classes) +{ + char *lc_name; + int res; + ALLOCA_FLAG(use_heap) + + if(classes == NULL) { + return 1; + } + if(!zend_hash_num_elements(classes)) { + return 0; + } + lc_name = do_alloca(len + 1, use_heap); + zend_str_tolower_copy(lc_name, class_name, len); + res = zend_hash_exists(classes, lc_name, len); + free_alloca(lc_name, use_heap); + return res; +} + #define YYFILL(n) do { } while (0) #define YYCTYPE unsigned char #define YYCURSOR cursor @@ -201,7 +220,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 228 "ext/standard/var_unserializer.re" @@ -261,8 +280,8 @@ static inline size_t parse_uiv(const unsigned char *p) return result; } -#define UNSERIALIZE_PARAMETER zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC -#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash TSRMLS_CC +#define UNSERIALIZE_PARAMETER zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes TSRMLS_DC +#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes TSRMLS_CC static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long elements, int objprops) { @@ -271,7 +290,7 @@ 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)) { + if (!php_var_unserialize_ex(&key, p, max, NULL, classes TSRMLS_CC)) { zval_dtor(key); FREE_ZVAL(key); return 0; @@ -285,7 +304,7 @@ static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long ALLOC_INIT_ZVAL(data); - if (!php_var_unserialize(&data, p, max, var_hash TSRMLS_CC)) { + if (!php_var_unserialize_ex(&data, p, max, var_hash, classes TSRMLS_CC)) { zval_dtor(key); FREE_ZVAL(key); zval_dtor(data); @@ -411,7 +430,14 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) # pragma optimize("", on) #endif -PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) +PHPAPI int php_var_unserialize(zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC) +{ + HashTable *classes = NULL; + return php_var_unserialize_ex(UNSERIALIZE_PASSTHRU); +} + + +PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) { const unsigned char *cursor, *limit, *marker, *start; zval **rval_ref; @@ -432,7 +458,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) -#line 436 "ext/standard/var_unserializer.c" +#line 462 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -492,9 +518,9 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: -#line 785 "ext/standard/var_unserializer.re" +#line 817 "ext/standard/var_unserializer.re" { return 0; } -#line 498 "ext/standard/var_unserializer.c" +#line 524 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@ -537,13 +563,13 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) goto yy3; yy14: ++YYCURSOR; -#line 779 "ext/standard/var_unserializer.re" +#line 811 "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 573 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@ -573,7 +599,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 635 "ext/standard/var_unserializer.re" +#line 661 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; long elements; @@ -624,6 +650,12 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) class_name = estrndup(class_name, len); do { + if(!unserialize_allowed_class(class_name, len, classes)) { + incomplete_class = 1; + ce = PHP_IC_ENTRY; + break; + } + /* Try to find class directly */ BG(serialize_lock) = 1; if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) { @@ -717,7 +749,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) return object_common2(UNSERIALIZE_PASSTHRU, elements); } -#line 721 "ext/standard/var_unserializer.c" +#line 753 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@ -742,7 +774,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 627 "ext/standard/var_unserializer.re" +#line 653 "ext/standard/var_unserializer.re" { INIT_PZVAL(*rval); @@ -750,7 +782,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } -#line 754 "ext/standard/var_unserializer.c" +#line 786 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@ -771,7 +803,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; -#line 607 "ext/standard/var_unserializer.re" +#line 633 "ext/standard/var_unserializer.re" { long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ @@ -791,7 +823,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) return finish_nested_data(UNSERIALIZE_PASSTHRU); } -#line 795 "ext/standard/var_unserializer.c" +#line 827 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@ -812,7 +844,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 578 "ext/standard/var_unserializer.re" +#line 604 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -841,7 +873,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) ZVAL_STRINGL(*rval, str, len, 0); return 1; } -#line 845 "ext/standard/var_unserializer.c" +#line 877 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@ -862,7 +894,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 550 "ext/standard/var_unserializer.re" +#line 576 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -890,7 +922,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) ZVAL_STRINGL(*rval, str, len, 1); return 1; } -#line 894 "ext/standard/var_unserializer.c" +#line 926 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@ -978,7 +1010,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) } yy63: ++YYCURSOR; -#line 540 "ext/standard/var_unserializer.re" +#line 566 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 use_double: @@ -988,7 +1020,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); return 1; } -#line 992 "ext/standard/var_unserializer.c" +#line 1024 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@ -1047,7 +1079,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 525 "ext/standard/var_unserializer.re" +#line 551 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); @@ -1062,7 +1094,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) return 1; } -#line 1066 "ext/standard/var_unserializer.c" +#line 1098 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@ -1089,7 +1121,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; -#line 498 "ext/standard/var_unserializer.re" +#line 524 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 int digits = YYCURSOR - start - 3; @@ -1116,7 +1148,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) ZVAL_LONG(*rval, parse_iv(start + 2)); return 1; } -#line 1120 "ext/standard/var_unserializer.c" +#line 1152 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@ -1124,24 +1156,24 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 491 "ext/standard/var_unserializer.re" +#line 517 "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 1167 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; -#line 484 "ext/standard/var_unserializer.re" +#line 510 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); return 1; } -#line 1145 "ext/standard/var_unserializer.c" +#line 1177 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@ -1164,7 +1196,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; -#line 461 "ext/standard/var_unserializer.re" +#line 487 "ext/standard/var_unserializer.re" { long id; @@ -1187,7 +1219,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) return 1; } -#line 1191 "ext/standard/var_unserializer.c" +#line 1223 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@ -1210,7 +1242,7 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; -#line 440 "ext/standard/var_unserializer.re" +#line 466 "ext/standard/var_unserializer.re" { long id; @@ -1231,9 +1263,9 @@ PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) return 1; } -#line 1235 "ext/standard/var_unserializer.c" +#line 1267 "ext/standard/var_unserializer.c" } -#line 787 "ext/standard/var_unserializer.re" +#line 819 "ext/standard/var_unserializer.re" return 0; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 4d99cbfd78944..5b34a709009b7 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -192,6 +192,25 @@ static char *unserialize_str(const unsigned char **p, size_t *len, size_t maxlen return str; } +static inline int unserialize_allowed_class(const char *class_name, int len, HashTable *classes) +{ + char *lc_name; + int res; + ALLOCA_FLAG(use_heap) + + if(classes == NULL) { + return 1; + } + if(!zend_hash_num_elements(classes)) { + return 0; + } + lc_name = do_alloca(len + 1, use_heap); + zend_str_tolower_copy(lc_name, class_name, len); + res = zend_hash_exists(classes, lc_name, len); + free_alloca(lc_name, use_heap); + return res; +} + #define YYFILL(n) do { } while (0) #define YYCTYPE unsigned char #define YYCURSOR cursor @@ -265,8 +284,8 @@ static inline size_t parse_uiv(const unsigned char *p) return result; } -#define UNSERIALIZE_PARAMETER zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC -#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash TSRMLS_CC +#define UNSERIALIZE_PARAMETER zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes TSRMLS_DC +#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes TSRMLS_CC static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long elements, int objprops) { @@ -275,7 +294,7 @@ 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)) { + if (!php_var_unserialize_ex(&key, p, max, NULL, classes TSRMLS_CC)) { zval_dtor(key); FREE_ZVAL(key); return 0; @@ -289,7 +308,7 @@ static inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTable *ht, long ALLOC_INIT_ZVAL(data); - if (!php_var_unserialize(&data, p, max, var_hash TSRMLS_CC)) { + if (!php_var_unserialize_ex(&data, p, max, var_hash, classes TSRMLS_CC)) { zval_dtor(key); FREE_ZVAL(key); zval_dtor(data); @@ -415,7 +434,14 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, long elements) # pragma optimize("", on) #endif -PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) +PHPAPI int php_var_unserialize(zval **rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash TSRMLS_DC) +{ + HashTable *classes = NULL; + return php_var_unserialize_ex(UNSERIALIZE_PASSTHRU); +} + + +PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) { const unsigned char *cursor, *limit, *marker, *start; zval **rval_ref; @@ -682,6 +708,12 @@ object ":" uiv ":" ["] { class_name = estrndup(class_name, len); do { + if(!unserialize_allowed_class(class_name, len, classes)) { + incomplete_class = 1; + ce = PHP_IC_ENTRY; + break; + } + /* Try to find class directly */ BG(serialize_lock) = 1; if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) {