diff options
author | Stanislav Malyshev <stas@php.net> | 2016-09-05 19:56:36 -0700 |
---|---|---|
committer | Stanislav Malyshev <stas@php.net> | 2016-09-05 19:56:36 -0700 |
commit | 747d21cfd2a7414b8d5ace203524f61eab2b8323 (patch) | |
tree | 95f5dd2695455e6a6f4bf794c33a37bc635da5c4 /ext | |
parent | 1928cdcacb3284658682d0cd68ac1ee3cf9cc653 (diff) | |
download | php-git-747d21cfd2a7414b8d5ace203524f61eab2b8323.tar.gz |
Fix bug #72785 - allowed_classes only applies to outermost unserialize()
Diffstat (limited to 'ext')
-rw-r--r-- | ext/standard/php_var.h | 2 | ||||
-rw-r--r-- | ext/standard/tests/serialize/bug72785.phpt | 24 | ||||
-rw-r--r-- | ext/standard/tests/serialize/unserialize_error_001.phpt | 45 | ||||
-rw-r--r-- | ext/standard/var.c | 9 | ||||
-rw-r--r-- | ext/standard/var_unserializer.c | 94 | ||||
-rw-r--r-- | ext/standard/var_unserializer.re | 27 |
6 files changed, 103 insertions, 98 deletions
diff --git a/ext/standard/php_var.h b/ext/standard/php_var.h index 80208d29ee..2d1cc5a40b 100644 --- a/ext/standard/php_var.h +++ b/ext/standard/php_var.h @@ -45,12 +45,12 @@ PHPAPI void php_var_serialize(smart_str *buf, zval *struc, php_serialize_data_t PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash); PHPAPI int php_var_unserialize_ref(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash); PHPAPI int php_var_unserialize_intern(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash); -PHPAPI int php_var_unserialize_ex(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash, HashTable *classes); PHPAPI php_serialize_data_t php_var_serialize_init(void); PHPAPI void php_var_serialize_destroy(php_serialize_data_t d); PHPAPI php_unserialize_data_t php_var_unserialize_init(void); PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d); +PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes); #define PHP_VAR_SERIALIZE_INIT(d) \ (d) = php_var_serialize_init() diff --git a/ext/standard/tests/serialize/bug72785.phpt b/ext/standard/tests/serialize/bug72785.phpt new file mode 100644 index 0000000000..8bcdf635f7 --- /dev/null +++ b/ext/standard/tests/serialize/bug72785.phpt @@ -0,0 +1,24 @@ +--TEST-- +Bug #72785: allowed_classes only applies to outermost unserialize() +--FILE-- +<?php + +// Forbidden class +class A {} + +$p = 'x:i:0;a:1:{i:0;O:1:"A":0:{}};m:a:0:{}'; +$s = 'C:11:"ArrayObject":' . strlen($p) . ':{' . $p . '}'; +var_dump(unserialize($s, ['allowed_classes' => ['ArrayObject']])); + +?> +--EXPECT-- +object(ArrayObject)#1 (1) { + ["storage":"ArrayObject":private]=> + array(1) { + [0]=> + object(__PHP_Incomplete_Class)#2 (1) { + ["__PHP_Incomplete_Class_Name"]=> + string(1) "A" + } + } +} diff --git a/ext/standard/tests/serialize/unserialize_error_001.phpt b/ext/standard/tests/serialize/unserialize_error_001.phpt index 5589cbd835..6773ec748c 100644 --- a/ext/standard/tests/serialize/unserialize_error_001.phpt +++ b/ext/standard/tests/serialize/unserialize_error_001.phpt @@ -13,40 +13,11 @@ var_dump(unserialize($s, ["allowed_classes" => 0])); var_dump(unserialize($s, ["allowed_classes" => 1])); --EXPECTF-- -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(__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" -} +Warning: unserialize(): allowed_classes option should be array or boolean in %s on line %d +bool(false) + +Warning: unserialize(): allowed_classes option should be array or boolean in %s on line %d +bool(false) + +Warning: unserialize(): allowed_classes option should be array or boolean in %s on line %d +bool(false) diff --git a/ext/standard/var.c b/ext/standard/var.c index cc033aada7..18d027f5aa 100644 --- a/ext/standard/var.c +++ b/ext/standard/var.c @@ -1079,6 +1079,12 @@ PHP_FUNCTION(unserialize) PHP_VAR_UNSERIALIZE_INIT(var_hash); if(options != NULL) { classes = zend_hash_str_find(Z_ARRVAL_P(options), "allowed_classes", sizeof("allowed_classes")-1); + if (classes && Z_TYPE_P(classes) != IS_ARRAY && Z_TYPE_P(classes) != IS_TRUE && Z_TYPE_P(classes) != IS_FALSE) { + php_error_docref(NULL, E_WARNING, "allowed_classes option should be array or boolean"); + PHP_VAR_UNSERIALIZE_DESTROY(var_hash); + RETURN_FALSE; + } + if(classes && (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); @@ -1094,9 +1100,10 @@ PHP_FUNCTION(unserialize) zend_string_release(lcname); } ZEND_HASH_FOREACH_END(); } + php_var_unserialize_set_allowed_classes(var_hash, class_hash); } - if (!php_var_unserialize_ex(return_value, &p, p + buf_len, &var_hash, class_hash)) { + if (!php_var_unserialize(return_value, &p, p + buf_len, &var_hash)) { 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 d59231cf89..18f80a8c7d 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 */ +/* Generated by re2c 0.13.7.5 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -29,6 +29,7 @@ struct php_unserialize_data { void *last; void *first_dtor; void *last_dtor; + HashTable *allowed_classes; }; PHPAPI php_unserialize_data_t php_var_unserialize_init() { @@ -58,6 +59,10 @@ PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) { } } +PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) { + d->allowed_classes = classes; +} + /* {{{ reference-handling for unserializer: var_* */ #define VAR_ENTRIES_MAX 1024 @@ -246,8 +251,10 @@ static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t return str; } -static inline int unserialize_allowed_class(zend_string *class_name, HashTable *classes) +static inline int unserialize_allowed_class( + zend_string *class_name, php_unserialize_data_t *var_hashx) { + HashTable *classes = (*var_hashx)->allowed_classes; zend_string *lcname; int res; ALLOCA_FLAG(use_heap) @@ -273,7 +280,7 @@ static inline int unserialize_allowed_class(zend_string *class_name, HashTable * #define YYMARKER marker -#line 281 "ext/standard/var_unserializer.re" +#line 288 "ext/standard/var_unserializer.re" @@ -333,8 +340,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, HashTable *classes -#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes +#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash +#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER); @@ -346,7 +353,7 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab ZVAL_UNDEF(&key); - if (!php_var_unserialize_internal(&key, p, max, NULL, classes)) { + if (!php_var_unserialize_internal(&key, p, max, NULL)) { zval_dtor(&key); return 0; } @@ -402,7 +409,7 @@ string_key: } } - if (!php_var_unserialize_internal(data, p, max, var_hash, classes)) { + if (!php_var_unserialize_internal(data, p, max, var_hash)) { zval_dtor(&key); return 0; } @@ -533,13 +540,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements) # pragma optimize("", on) #endif -PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash) -{ - HashTable *classes = NULL; - return php_var_unserialize_ex(UNSERIALIZE_PASSTHRU); -} - -PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) +PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) { var_entries *orig_var_entries = (*var_hash)->last; zend_long orig_used_slots = orig_var_entries ? orig_var_entries->used_slots : 0; @@ -585,7 +586,7 @@ static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER) start = cursor; -#line 589 "ext/standard/var_unserializer.c" +#line 590 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -645,9 +646,9 @@ yy2: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: -#line 955 "ext/standard/var_unserializer.re" +#line 956 "ext/standard/var_unserializer.re" { return 0; } -#line 651 "ext/standard/var_unserializer.c" +#line 652 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@ -690,13 +691,13 @@ yy13: goto yy3; yy14: ++YYCURSOR; -#line 949 "ext/standard/var_unserializer.re" +#line 950 "ext/standard/var_unserializer.re" { /* this is the case where we have less data than planned */ php_error_docref(NULL, E_NOTICE, "Unexpected end of serialized data"); return 0; /* not sure if it should be 0 or 1 here? */ } -#line 700 "ext/standard/var_unserializer.c" +#line 701 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@ -722,11 +723,12 @@ yy20: if (yybm[0+yych] & 128) { goto yy20; } - if (yych != ':') goto yy18; + if (yych <= '/') goto yy18; + if (yych >= ';') goto yy18; yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 804 "ext/standard/var_unserializer.re" +#line 805 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; zend_long elements; @@ -776,7 +778,7 @@ yy20: class_name = zend_string_init(str, len, 0); do { - if(!unserialize_allowed_class(class_name, classes)) { + if(!unserialize_allowed_class(class_name, var_hash)) { incomplete_class = 1; ce = PHP_IC_ENTRY; break; @@ -871,7 +873,7 @@ yy20: return object_common2(UNSERIALIZE_PASSTHRU, elements); } -#line 875 "ext/standard/var_unserializer.c" +#line 877 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@ -896,14 +898,14 @@ yy27: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 797 "ext/standard/var_unserializer.re" +#line 798 "ext/standard/var_unserializer.re" { if (!var_hash) return 0; return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } -#line 907 "ext/standard/var_unserializer.c" +#line 909 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@ -924,7 +926,7 @@ yy34: yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; -#line 773 "ext/standard/var_unserializer.re" +#line 774 "ext/standard/var_unserializer.re" { zend_long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ @@ -948,7 +950,7 @@ yy34: return finish_nested_data(UNSERIALIZE_PASSTHRU); } -#line 952 "ext/standard/var_unserializer.c" +#line 954 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@ -969,7 +971,7 @@ yy41: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 739 "ext/standard/var_unserializer.re" +#line 740 "ext/standard/var_unserializer.re" { size_t len, maxlen; zend_string *str; @@ -1003,7 +1005,7 @@ yy41: ZVAL_STR(rval, str); return 1; } -#line 1007 "ext/standard/var_unserializer.c" +#line 1009 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@ -1024,7 +1026,7 @@ yy48: yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 707 "ext/standard/var_unserializer.re" +#line 708 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -1056,7 +1058,7 @@ yy48: ZVAL_STRINGL(rval, str, len); return 1; } -#line 1060 "ext/standard/var_unserializer.c" +#line 1062 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@ -1144,7 +1146,7 @@ yy61: } yy63: ++YYCURSOR; -#line 698 "ext/standard/var_unserializer.re" +#line 699 "ext/standard/var_unserializer.re" { #if SIZEOF_ZEND_LONG == 4 use_double: @@ -1153,7 +1155,7 @@ use_double: ZVAL_DOUBLE(rval, zend_strtod((const char *)start + 2, NULL)); return 1; } -#line 1157 "ext/standard/var_unserializer.c" +#line 1159 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@ -1212,7 +1214,7 @@ yy73: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 682 "ext/standard/var_unserializer.re" +#line 683 "ext/standard/var_unserializer.re" { *p = YYCURSOR; @@ -1228,7 +1230,7 @@ yy73: return 1; } -#line 1232 "ext/standard/var_unserializer.c" +#line 1234 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@ -1255,7 +1257,7 @@ yy79: if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; -#line 656 "ext/standard/var_unserializer.re" +#line 657 "ext/standard/var_unserializer.re" { #if SIZEOF_ZEND_LONG == 4 int digits = YYCURSOR - start - 3; @@ -1281,7 +1283,7 @@ yy79: ZVAL_LONG(rval, parse_iv(start + 2)); return 1; } -#line 1285 "ext/standard/var_unserializer.c" +#line 1287 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@ -1289,22 +1291,22 @@ yy83: yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 650 "ext/standard/var_unserializer.re" +#line 651 "ext/standard/var_unserializer.re" { *p = YYCURSOR; ZVAL_BOOL(rval, parse_iv(start + 2)); return 1; } -#line 1299 "ext/standard/var_unserializer.c" +#line 1301 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; -#line 644 "ext/standard/var_unserializer.re" +#line 645 "ext/standard/var_unserializer.re" { *p = YYCURSOR; ZVAL_NULL(rval); return 1; } -#line 1308 "ext/standard/var_unserializer.c" +#line 1310 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@ -1327,7 +1329,7 @@ yy91: if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; -#line 619 "ext/standard/var_unserializer.re" +#line 620 "ext/standard/var_unserializer.re" { zend_long id; @@ -1352,7 +1354,7 @@ yy91: return 1; } -#line 1356 "ext/standard/var_unserializer.c" +#line 1358 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@ -1375,7 +1377,7 @@ yy97: if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; -#line 593 "ext/standard/var_unserializer.re" +#line 594 "ext/standard/var_unserializer.re" { zend_long id; @@ -1401,9 +1403,9 @@ yy97: return 1; } -#line 1405 "ext/standard/var_unserializer.c" +#line 1407 "ext/standard/var_unserializer.c" } -#line 957 "ext/standard/var_unserializer.re" +#line 958 "ext/standard/var_unserializer.re" return 0; diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index 90762a41d0..bbf4009f71 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -27,6 +27,7 @@ struct php_unserialize_data { void *last; void *first_dtor; void *last_dtor; + HashTable *allowed_classes; }; PHPAPI php_unserialize_data_t php_var_unserialize_init() { @@ -56,6 +57,10 @@ PHPAPI void php_var_unserialize_destroy(php_unserialize_data_t d) { } } +PHPAPI void php_var_unserialize_set_allowed_classes(php_unserialize_data_t d, HashTable *classes) { + d->allowed_classes = classes; +} + /* {{{ reference-handling for unserializer: var_* */ #define VAR_ENTRIES_MAX 1024 @@ -244,8 +249,10 @@ static zend_string *unserialize_str(const unsigned char **p, size_t len, size_t return str; } -static inline int unserialize_allowed_class(zend_string *class_name, HashTable *classes) +static inline int unserialize_allowed_class( + zend_string *class_name, php_unserialize_data_t *var_hashx) { + HashTable *classes = (*var_hashx)->allowed_classes; zend_string *lcname; int res; ALLOCA_FLAG(use_heap) @@ -337,8 +344,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, HashTable *classes -#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash, classes +#define UNSERIALIZE_PARAMETER zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash +#define UNSERIALIZE_PASSTHRU rval, p, max, var_hash static int php_var_unserialize_internal(UNSERIALIZE_PARAMETER); @@ -350,7 +357,7 @@ static zend_always_inline int process_nested_data(UNSERIALIZE_PARAMETER, HashTab ZVAL_UNDEF(&key); - if (!php_var_unserialize_internal(&key, p, max, NULL, classes)) { + if (!php_var_unserialize_internal(&key, p, max, NULL)) { zval_dtor(&key); return 0; } @@ -406,7 +413,7 @@ string_key: } } - if (!php_var_unserialize_internal(data, p, max, var_hash, classes)) { + if (!php_var_unserialize_internal(data, p, max, var_hash)) { zval_dtor(&key); return 0; } @@ -537,13 +544,7 @@ static inline int object_common2(UNSERIALIZE_PARAMETER, zend_long elements) # pragma optimize("", on) #endif -PHPAPI int php_var_unserialize(zval *rval, const unsigned char **p, const unsigned char *max, php_unserialize_data_t *var_hash) -{ - HashTable *classes = NULL; - return php_var_unserialize_ex(UNSERIALIZE_PASSTHRU); -} - -PHPAPI int php_var_unserialize_ex(UNSERIALIZE_PARAMETER) +PHPAPI int php_var_unserialize(UNSERIALIZE_PARAMETER) { var_entries *orig_var_entries = (*var_hash)->last; zend_long orig_used_slots = orig_var_entries ? orig_var_entries->used_slots : 0; @@ -850,7 +851,7 @@ object ":" uiv ":" ["] { class_name = zend_string_init(str, len, 0); do { - if(!unserialize_allowed_class(class_name, classes)) { + if(!unserialize_allowed_class(class_name, var_hash)) { incomplete_class = 1; ce = PHP_IC_ENTRY; break; |