php.net | support | documentation | report a bug | advanced search | search howto | statistics | random bug | login | |
Patch max_input_vars.patch for *General Issues Bug #60655Patch version 2012-01-05 05:03 UTC Return to Bug #60655 | Download this patchThis patch renders other patches obsolete Obsolete patches:
Developer: laruence@php.netIndex: trunk/ext/standard/basic_functions.h =================================================================== --- trunk/ext/standard/basic_functions.h (revision 321767) +++ trunk/ext/standard/basic_functions.h (working copy) @@ -212,6 +212,8 @@ struct { void *var_hash; unsigned level; + long max_vars; + long num_vars; } unserialize; /* url_scanner_ex.re */ Index: trunk/ext/standard/tests/serialize/serialization_error_001.phpt =================================================================== --- trunk/ext/standard/tests/serialize/serialization_error_001.phpt (revision 321767) +++ trunk/ext/standard/tests/serialize/serialization_error_001.phpt (working copy) @@ -28,15 +28,15 @@ --EXPECTF-- *** Testing serialize()/unserialize() : error conditions *** -Warning: serialize() expects exactly 1 parameter, 0 given in %s on line 16 +Warning: serialize() expects exactly 1 parameter, 0 given in %sserialization_error_001.php on line %d NULL -Warning: unserialize() expects exactly 1 parameter, 0 given in %s on line 17 +Warning: unserialize() expects at least 1 parameter, 0 given in %sserialization_error_001.php on line %d bool(false) -Warning: serialize() expects exactly 1 parameter, 2 given in %s on line 20 +Warning: serialize() expects exactly 1 parameter, 2 given in %sserialization_error_001.php on line %d NULL -Warning: unserialize() expects exactly 1 parameter, 2 given in %s on line 21 +Notice: unserialize(): Error at offset 0 of 1 bytes in %sserialization_error_001.php on line %d bool(false) Done Index: trunk/ext/standard/tests/serialize/unserialize_max_vars.phpt =================================================================== --- trunk/ext/standard/tests/serialize/unserialize_max_vars.phpt (revision 0) +++ trunk/ext/standard/tests/serialize/unserialize_max_vars.phpt (revision 0) @@ -0,0 +1,57 @@ +--TEST-- +Test unserialize() functions with max_input_vars +--FILE-- +<?php +$str = serialize(array(1,2,3)); // there will be 4 items, array, and 1, 2, 3 + +print_r(unserialize($str, 3)); +print_r(unserialize($str, 4)); + +$str = serialize(array(array(),array(2,3))); // there will be 5 items, array, and array, array, 2, 3 +print_r(unserialize($str, 3)); +print_r(unserialize($str, 5)); + +$obj = (object)(array(array(),array(2,3))); +$str = serialize($obj); + +print_r(unserialize($str, 3)); +print_r(unserialize($str, 5)); +?> +--EXPECTF-- +Warning: unserialize(): Unserialized variables exceeded 3 in %sunserialize_max_vars.php on line %d +Array +( + [0] => 1 + [1] => 2 + [2] => 3 +) + +Warning: unserialize(): Unserialized variables exceeded 3 in %sunserialize_max_vars.php on line %d +Array +( + [0] => Array + ( + ) + + [1] => Array + ( + [0] => 2 + [1] => 3 + ) + +) + +Warning: unserialize(): Unserialized variables exceeded 3 in %sunserialize_max_vars.php on line %d +stdClass Object +( + [0] => Array + ( + ) + + [1] => Array + ( + [0] => 2 + [1] => 3 + ) + +) Index: trunk/ext/standard/var_unserializer.c =================================================================== --- trunk/ext/standard/var_unserializer.c (revision 321767) +++ trunk/ext/standard/var_unserializer.c (working copy) @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Sun Jan 1 10:36:31 2012 */ +/* Generated by re2c 0.13.5 on Wed Jan 4 23:12:56 2012 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -276,6 +276,7 @@ FREE_ZVAL(key); return 0; } + --(BG(unserialize).num_vars); if (Z_TYPE_P(key) != IS_LONG && Z_TYPE_P(key) != IS_STRING) { zval_dtor(key); @@ -297,12 +298,14 @@ switch (Z_TYPE_P(key)) { case IS_LONG: if (zend_hash_index_find(ht, Z_LVAL_P(key), (void **)&old_data)==SUCCESS) { + --(BG(unserialize).num_vars); var_push_dtor(var_hash, old_data); } zend_hash_index_update(ht, Z_LVAL_P(key), &data, sizeof(data), NULL); break; case IS_STRING: if (zend_symtable_find(ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, (void **)&old_data)==SUCCESS) { + --(BG(unserialize).num_vars); var_push_dtor(var_hash, old_data); } zend_symtable_update(ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &data, sizeof(data), NULL); @@ -411,17 +414,20 @@ if (YYCURSOR >= YYLIMIT) { return 0; } + + if (BG(unserialize).max_vars && BG(unserialize).num_vars >= BG(unserialize).max_vars) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unserialized variables exceeded %ld", BG(unserialize).max_vars); + return 0; + } if (var_hash && cursor[0] != 'R') { var_push(var_hash, rval); } start = cursor; - - -#line 425 "ext/standard/var_unserializer.c" +#line 431 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -481,9 +487,9 @@ yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: -#line 747 "ext/standard/var_unserializer.re" +#line 766 "ext/standard/var_unserializer.re" { return 0; } -#line 487 "ext/standard/var_unserializer.c" +#line 493 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@ -526,13 +532,13 @@ goto yy3; yy14: ++YYCURSOR; -#line 741 "ext/standard/var_unserializer.re" +#line 760 "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 536 "ext/standard/var_unserializer.c" +#line 542 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@ -562,7 +568,7 @@ yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 624 "ext/standard/var_unserializer.re" +#line 641 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; long elements; @@ -661,6 +667,7 @@ *p = YYCURSOR; if (custom_object) { + ++(BG(unserialize).num_vars); int ret = object_custom(UNSERIALIZE_PASSTHRU, ce); if (ret && incomplete_class) { @@ -677,9 +684,10 @@ } efree(class_name); + ++(BG(unserialize).num_vars); return object_common2(UNSERIALIZE_PASSTHRU, elements); } -#line 683 "ext/standard/var_unserializer.c" +#line 691 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@ -704,15 +712,16 @@ yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 616 "ext/standard/var_unserializer.re" +#line 632 "ext/standard/var_unserializer.re" { INIT_PZVAL(*rval); + ++(BG(unserialize).num_vars); return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } -#line 716 "ext/standard/var_unserializer.c" +#line 725 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@ -733,7 +742,7 @@ yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; -#line 596 "ext/standard/var_unserializer.re" +#line 611 "ext/standard/var_unserializer.re" { long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ @@ -747,13 +756,14 @@ array_init_size(*rval, elements); + ++(BG(unserialize).num_vars); if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_PP(rval), elements, 0)) { return 0; } return finish_nested_data(UNSERIALIZE_PASSTHRU); } -#line 757 "ext/standard/var_unserializer.c" +#line 767 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@ -774,7 +784,7 @@ yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 567 "ext/standard/var_unserializer.re" +#line 581 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -801,9 +811,10 @@ INIT_PZVAL(*rval); ZVAL_STRINGL(*rval, str, len, 0); + ++(BG(unserialize).num_vars); return 1; } -#line 807 "ext/standard/var_unserializer.c" +#line 818 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@ -824,7 +835,7 @@ yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 539 "ext/standard/var_unserializer.re" +#line 552 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -850,9 +861,10 @@ INIT_PZVAL(*rval); ZVAL_STRINGL(*rval, str, len, 1); + ++(BG(unserialize).num_vars); return 1; } -#line 856 "ext/standard/var_unserializer.c" +#line 868 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@ -940,7 +952,7 @@ } yy63: ++YYCURSOR; -#line 529 "ext/standard/var_unserializer.re" +#line 541 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 use_double: @@ -948,9 +960,10 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); + ++(BG(unserialize).num_vars); return 1; } -#line 954 "ext/standard/var_unserializer.c" +#line 967 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@ -1009,7 +1022,7 @@ yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 514 "ext/standard/var_unserializer.re" +#line 525 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); @@ -1022,9 +1035,10 @@ ZVAL_DOUBLE(*rval, -php_get_inf()); } + ++(BG(unserialize).num_vars); return 1; } -#line 1028 "ext/standard/var_unserializer.c" +#line 1042 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@ -1051,7 +1065,7 @@ if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; -#line 487 "ext/standard/var_unserializer.re" +#line 497 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 int digits = YYCURSOR - start - 3; @@ -1076,9 +1090,10 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_LONG(*rval, parse_iv(start + 2)); + ++(BG(unserialize).num_vars); return 1; } -#line 1082 "ext/standard/var_unserializer.c" +#line 1097 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@ -1086,24 +1101,26 @@ yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 480 "ext/standard/var_unserializer.re" +#line 489 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_BOOL(*rval, parse_iv(start + 2)); + ++(BG(unserialize).num_vars); return 1; } -#line 1097 "ext/standard/var_unserializer.c" +#line 1113 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; -#line 473 "ext/standard/var_unserializer.re" +#line 481 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); + ++(BG(unserialize).num_vars); return 1; } -#line 1107 "ext/standard/var_unserializer.c" +#line 1124 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@ -1126,7 +1143,7 @@ if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; -#line 450 "ext/standard/var_unserializer.re" +#line 457 "ext/standard/var_unserializer.re" { long id; @@ -1147,9 +1164,10 @@ Z_ADDREF_PP(rval); Z_UNSET_ISREF_PP(rval); + ++(BG(unserialize).num_vars); return 1; } -#line 1153 "ext/standard/var_unserializer.c" +#line 1171 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@ -1172,7 +1190,7 @@ if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; -#line 429 "ext/standard/var_unserializer.re" +#line 435 "ext/standard/var_unserializer.re" { long id; @@ -1191,11 +1209,12 @@ Z_ADDREF_PP(rval); Z_SET_ISREF_PP(rval); + ++(BG(unserialize).num_vars); return 1; } -#line 1197 "ext/standard/var_unserializer.c" +#line 1216 "ext/standard/var_unserializer.c" } -#line 749 "ext/standard/var_unserializer.re" +#line 768 "ext/standard/var_unserializer.re" return 0; Index: trunk/ext/standard/var.c =================================================================== --- trunk/ext/standard/var.c (revision 321767) +++ trunk/ext/standard/var.c (working copy) @@ -928,8 +928,9 @@ int buf_len; const unsigned char *p; php_unserialize_data_t var_hash; + long max_vars = PG(max_input_vars); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &buf, &buf_len, &max_vars) == FAILURE) { RETURN_FALSE; } @@ -939,10 +940,13 @@ p = (const unsigned char*) buf; PHP_VAR_UNSERIALIZE_INIT(var_hash); + PHP_VAR_UNSERIALIZE_MAX_VARS(max_vars); if (!php_var_unserialize(&return_value, &p, p + buf_len, &var_hash TSRMLS_CC)) { PHP_VAR_UNSERIALIZE_DESTROY(var_hash); zval_dtor(return_value); - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len); + if (!BG(unserialize).max_vars || BG(unserialize).num_vars < BG(unserialize).max_vars) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len); + } RETURN_FALSE; } PHP_VAR_UNSERIALIZE_DESTROY(var_hash); Index: trunk/ext/standard/php_var.h =================================================================== --- trunk/ext/standard/php_var.h (revision 321767) +++ trunk/ext/standard/php_var.h (working copy) @@ -95,9 +95,13 @@ } else { \ (var_hash_ptr) = (php_serialize_data_t)BG(unserialize).var_hash; \ ++BG(unserialize).level; \ - } \ + } \ + BG(unserialize).num_vars = 0; \ + BG(unserialize).max_vars = 0; \ } while (0) +#define PHP_VAR_UNSERIALIZE_MAX_VARS(max_vars) BG(unserialize).max_vars = max_vars; + #define PHP_VAR_UNSERIALIZE_DESTROY(var_hash_ptr) \ do { \ /* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ \ Index: trunk/ext/standard/var_unserializer.re =================================================================== --- trunk/ext/standard/var_unserializer.re (revision 321767) +++ trunk/ext/standard/var_unserializer.re (working copy) @@ -280,6 +280,7 @@ FREE_ZVAL(key); return 0; } + --(BG(unserialize).num_vars); if (Z_TYPE_P(key) != IS_LONG && Z_TYPE_P(key) != IS_STRING) { zval_dtor(key); @@ -301,12 +302,14 @@ switch (Z_TYPE_P(key)) { case IS_LONG: if (zend_hash_index_find(ht, Z_LVAL_P(key), (void **)&old_data)==SUCCESS) { + --(BG(unserialize).num_vars); var_push_dtor(var_hash, old_data); } zend_hash_index_update(ht, Z_LVAL_P(key), &data, sizeof(data), NULL); break; case IS_STRING: if (zend_symtable_find(ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, (void **)&old_data)==SUCCESS) { + --(BG(unserialize).num_vars); var_push_dtor(var_hash, old_data); } zend_symtable_update(ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &data, sizeof(data), NULL); @@ -415,15 +418,18 @@ if (YYCURSOR >= YYLIMIT) { return 0; } + + if (BG(unserialize).max_vars && BG(unserialize).num_vars >= BG(unserialize).max_vars) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unserialized variables exceeded %ld", BG(unserialize).max_vars); + return 0; + } if (var_hash && cursor[0] != 'R') { var_push(var_hash, rval); } start = cursor; - - /*!re2c "R:" iv ";" { @@ -444,6 +450,7 @@ Z_ADDREF_PP(rval); Z_SET_ISREF_PP(rval); + ++(BG(unserialize).num_vars); return 1; } @@ -467,6 +474,7 @@ Z_ADDREF_PP(rval); Z_UNSET_ISREF_PP(rval); + ++(BG(unserialize).num_vars); return 1; } @@ -474,6 +482,7 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); + ++(BG(unserialize).num_vars); return 1; } @@ -481,6 +490,7 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_BOOL(*rval, parse_iv(start + 2)); + ++(BG(unserialize).num_vars); return 1; } @@ -508,6 +518,7 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_LONG(*rval, parse_iv(start + 2)); + ++(BG(unserialize).num_vars); return 1; } @@ -523,6 +534,7 @@ ZVAL_DOUBLE(*rval, -php_get_inf()); } + ++(BG(unserialize).num_vars); return 1; } @@ -533,6 +545,7 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); + ++(BG(unserialize).num_vars); return 1; } @@ -561,6 +574,7 @@ INIT_PZVAL(*rval); ZVAL_STRINGL(*rval, str, len, 1); + ++(BG(unserialize).num_vars); return 1; } @@ -590,6 +604,7 @@ INIT_PZVAL(*rval); ZVAL_STRINGL(*rval, str, len, 0); + ++(BG(unserialize).num_vars); return 1; } @@ -606,6 +621,7 @@ array_init_size(*rval, elements); + ++(BG(unserialize).num_vars); if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_PP(rval), elements, 0)) { return 0; } @@ -617,6 +633,7 @@ INIT_PZVAL(*rval); + ++(BG(unserialize).num_vars); return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } @@ -719,6 +736,7 @@ *p = YYCURSOR; if (custom_object) { + ++(BG(unserialize).num_vars); int ret = object_custom(UNSERIALIZE_PASSTHRU, ce); if (ret && incomplete_class) { @@ -735,6 +753,7 @@ } efree(class_name); + ++(BG(unserialize).num_vars); return object_common2(UNSERIALIZE_PASSTHRU, elements); } Index: trunk/ext/json/json.c =================================================================== --- trunk/ext/json/json.c (revision 321767) +++ trunk/ext/json/json.c (working copy) @@ -99,6 +99,7 @@ REGISTER_LONG_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_MAX_VARS", PHP_JSON_ERROR_MAX_VARS, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_ERROR_STATE_MISMATCH", PHP_JSON_ERROR_STATE_MISMATCH, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_ERROR_CTRL_CHAR", PHP_JSON_ERROR_CTRL_CHAR, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_ERROR_SYNTAX", PHP_JSON_ERROR_SYNTAX, CONST_CS | CONST_PERSISTENT); @@ -602,7 +603,7 @@ } /* }}} */ -PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC) /* {{{ */ +PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth, long max_vars TSRMLS_DC) /* {{{ */ { int utf16_len; zval *z; @@ -627,7 +628,7 @@ } ALLOC_INIT_ZVAL(z); - jp = new_JSON_parser(depth); + jp = new_JSON_parser(depth, max_vars); if (parse_JSON_ex(jp, z, utf16, utf16_len, options TSRMLS_CC)) { *return_value = *z; } @@ -671,7 +672,6 @@ } /* }}} */ - /* {{{ proto string json_encode(mixed data [, int options]) Returns the JSON representation of a value */ static PHP_FUNCTION(json_encode) @@ -703,8 +703,9 @@ zend_bool assoc = 0; /* return JS objects as PHP objects by default */ long depth = JSON_PARSER_DEFAULT_DEPTH; long options = 0; + long max_vars = PG(max_input_vars); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bll", &str, &str_len, &assoc, &depth, &options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|blll", &str, &str_len, &assoc, &depth, &max_vars, &options) == FAILURE) { return; } @@ -721,7 +722,7 @@ options &= ~PHP_JSON_OBJECT_AS_ARRAY; } - php_json_decode_ex(return_value, str, str_len, options, depth TSRMLS_CC); + php_json_decode_ex(return_value, str, str_len, options, depth, max_vars TSRMLS_CC); } /* }}} */ Index: trunk/ext/json/tests/008.phpt =================================================================== --- trunk/ext/json/tests/008.phpt (revision 321767) +++ trunk/ext/json/tests/008.phpt (working copy) @@ -7,7 +7,7 @@ $json = '{"largenum":123456789012345678901234567890}'; $x = json_decode($json); var_dump($x->largenum); -$x = json_decode($json, false, 512, JSON_BIGINT_AS_STRING); +$x = json_decode($json, false, 512, 1000, JSON_BIGINT_AS_STRING); var_dump($x->largenum); echo "Done\n"; ?> Index: trunk/ext/json/tests/max_input_vars.phpt =================================================================== --- trunk/ext/json/tests/max_input_vars.phpt (revision 0) +++ trunk/ext/json/tests/max_input_vars.phpt (revision 0) @@ -0,0 +1,40 @@ +--TEST-- +json_decode() with max_input_vars +--SKIPIF-- +<?php if (!extension_loaded("json")) print "skip"; ?> +--FILE-- +<?php + +$a = array(1,2,3,4); //an array, and 1,2,3,4 total 5 elements +$str = json_encode($a); + +print_r(json_decode($str, false, 512, 4)); +var_dump(json_last_error() == JSON_ERROR_MAX_VARS); +print_r(json_decode($str, false, 512, 5)); + +$a = array(1,array(1),3); //an array, 1, an array, 1, 3 total 5 elements +$str = json_encode($a); +print_r(json_decode($str, true, 512, 4)); +var_dump(json_last_error() == JSON_ERROR_MAX_VARS); +print_r(json_decode($str, true, 512, 5)); +?> +--EXPECT-- +bool(true) +Array +( + [0] => 1 + [1] => 2 + [2] => 3 + [3] => 4 +) +bool(true) +Array +( + [0] => 1 + [1] => Array + ( + [0] => 1 + ) + + [2] => 3 +) Index: trunk/ext/json/tests/json_decode_error.phpt =================================================================== --- trunk/ext/json/tests/json_decode_error.phpt (revision 321767) +++ trunk/ext/json/tests/json_decode_error.phpt (working copy) @@ -20,7 +20,7 @@ echo "\n-- Testing json_decode() function with more than expected no. of arguments --\n"; $extra_arg = 10; -var_dump( json_decode('"abc"', TRUE, 512, 0, $extra_arg) ); +var_dump( json_decode('"abc"', TRUE, 512, 0, 1000, $extra_arg) ); ?> ===Done=== @@ -34,6 +34,6 @@ -- Testing json_decode() function with more than expected no. of arguments -- -Warning: json_decode() expects at most 4 parameters, 5 given in %s on line %d +Warning: json_decode() expects at most 5 parameters, 6 given in %s on line %d NULL ===Done=== Index: trunk/ext/json/JSON_parser.c =================================================================== --- trunk/ext/json/JSON_parser.c (revision 321767) +++ trunk/ext/json/JSON_parser.c (working copy) @@ -239,11 +239,12 @@ JSON_checker_char will delete the JSON_checker object if it sees an error. */ JSON_parser -new_JSON_parser(int depth) +new_JSON_parser(int depth, long max_vars) { JSON_parser jp = (JSON_parser)emalloc(sizeof(struct JSON_parser_struct)); jp->state = GO; jp->depth = depth; + jp->max_vars = max_vars; jp->top = -1; jp->error_code = PHP_JSON_ERROR_NONE; jp->stack = (int*)ecalloc(depth, sizeof(int)); @@ -443,6 +444,7 @@ int next_state; /* the next state */ int the_index; int assoc = options & PHP_JSON_OBJECT_AS_ARRAY; + long num_vars = 1; /* the container is also counted in */ smart_str buf = {0}; smart_str key = {0}; @@ -453,6 +455,11 @@ JSON_RESET_TYPE(); for (the_index = 0; the_index < length; the_index += 1) { + if (num_vars >= jp->max_vars) { + jp->error_code = PHP_JSON_ERROR_MAX_VARS; + FREE_BUFFERS(); + return false; + } next_char = utf16_json[the_index]; if (next_char >= 128) { next_class = C_ETC; @@ -555,8 +562,10 @@ } else { add_assoc_zval_ex(jp->the_zstack[jp->top], (key.len ? key.c : ""), (key.len ? (key.len + 1) : sizeof("")), mval); } + key.len = 0; buf.len = 0; + ++num_vars; JSON_RESET_TYPE(); } @@ -574,10 +583,15 @@ { zval *mval; smart_str_0(&buf); - + if (zend_hash_num_elements(HASH_OF(jp->the_zstack[jp->top])) >= jp->max_vars) { + buf.len = 0; + JSON_RESET_TYPE(); + continue; + } json_create_zval(&mval, &buf, type, options); add_next_index_zval(jp->the_zstack[jp->top], mval); buf.len = 0; + ++num_vars; JSON_RESET_TYPE(); } @@ -615,6 +629,7 @@ if (jp->top > 1) { attach_zval(jp, jp->top - 1, jp->top, &key, assoc TSRMLS_CC); + ++num_vars; } JSON_RESET_TYPE(); @@ -643,6 +658,7 @@ if (jp->top > 1) { attach_zval(jp, jp->top - 1, jp->top, &key, assoc TSRMLS_CC); + ++num_vars; } JSON_RESET_TYPE(); @@ -700,6 +716,7 @@ } else { add_assoc_zval_ex(jp->the_zstack[jp->top], (key.len ? key.c : ""), (key.len ? (key.len + 1) : sizeof("")), mval); } + ++num_vars; key.len = 0; } jp->state = KE; @@ -708,6 +725,7 @@ case MODE_ARRAY: if (type != -1) { add_next_index_zval(jp->the_zstack[jp->top], mval); + ++num_vars; } jp->state = VA; break; Index: trunk/ext/json/php_json.h =================================================================== --- trunk/ext/json/php_json.h (revision 321767) +++ trunk/ext/json/php_json.h (working copy) @@ -49,7 +49,7 @@ #endif PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC); -PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC); +PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth, long max_vars TSRMLS_DC); extern zend_class_entry *php_json_serializable_ce; @@ -74,7 +74,7 @@ static inline void php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, long depth TSRMLS_DC) { - php_json_decode_ex(return_value, str, str_len, assoc ? PHP_JSON_OBJECT_AS_ARRAY : 0, depth TSRMLS_CC); + php_json_decode_ex(return_value, str, str_len, assoc ? PHP_JSON_OBJECT_AS_ARRAY : 0, depth, 0 TSRMLS_CC); } Index: trunk/ext/json/JSON_parser.h =================================================================== --- trunk/ext/json/JSON_parser.h (revision 321767) +++ trunk/ext/json/JSON_parser.h (working copy) @@ -15,20 +15,22 @@ int top; int error_code; int* stack; + long max_vars; zval **the_zstack; zval *the_static_zstack[JSON_PARSER_DEFAULT_DEPTH]; } * JSON_parser; enum error_codes { PHP_JSON_ERROR_NONE = 0, - PHP_JSON_ERROR_DEPTH, + PHP_JSON_ERROR_DEPTH, PHP_JSON_ERROR_STATE_MISMATCH, PHP_JSON_ERROR_CTRL_CHAR, PHP_JSON_ERROR_SYNTAX, - PHP_JSON_ERROR_UTF8 + PHP_JSON_ERROR_UTF8, + PHP_JSON_ERROR_MAX_VARS, }; -extern JSON_parser new_JSON_parser(int depth); +extern JSON_parser new_JSON_parser(int depth, long max_vars); extern int parse_JSON_ex(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int options TSRMLS_DC); extern int free_JSON_parser(JSON_parser jp); Index: trunk/main/rfc1867.c =================================================================== --- trunk/main/rfc1867.c (revision 321767) +++ trunk/main/rfc1867.c (working copy) @@ -686,6 +686,7 @@ zend_llist header; void *event_extra_data = NULL; unsigned int llen = 0; + long num_vars = 0; int upload_cnt = INI_INT("max_file_uploads"); const zend_encoding *internal_encoding = zend_multibyte_get_internal_encoding(TSRMLS_C); php_rfc1867_getword_t getword; @@ -896,6 +897,11 @@ efree(param); efree(value); + ++num_vars; + if (PG(max_input_vars) && num_vars >= PG(max_input_vars)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Post variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); + goto fileupload_done; + } continue; } Index: trunk/main/php_variables.c =================================================================== --- trunk/main/php_variables.c (revision 321767) +++ trunk/main/php_variables.c (working copy) @@ -178,15 +178,10 @@ } else { escaped_index = index; if (zend_symtable_find(symtable1, escaped_index, index_len + 1, (void **) &gpc_element_p) == FAILURE - || Z_TYPE_PP(gpc_element_p) != IS_ARRAY) { - if (zend_hash_num_elements(symtable1) <= PG(max_input_vars)) { - if (zend_hash_num_elements(symtable1) == PG(max_input_vars)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); - } - MAKE_STD_ZVAL(gpc_element); - array_init(gpc_element); - zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); - } + || Z_TYPE_PP(gpc_element_p) != IS_ARRAY) { + MAKE_STD_ZVAL(gpc_element); + array_init(gpc_element); + zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); } if (index != escaped_index) { efree(escaped_index); @@ -225,14 +220,7 @@ zend_symtable_exists(symtable1, escaped_index, index_len + 1)) { zval_ptr_dtor(&gpc_element); } else { - if (zend_hash_num_elements(symtable1) <= PG(max_input_vars)) { - if (zend_hash_num_elements(symtable1) == PG(max_input_vars)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); - } - zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); - } else { - zval_ptr_dtor(&gpc_element); - } + zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); } if (escaped_index != index) { efree(escaped_index); @@ -246,6 +234,7 @@ { char *var, *val, *e, *s, *p; zval *array_ptr = (zval *) arg; + long num_vars = 0; if (SG(request_info).post_data == NULL) { return; @@ -270,6 +259,11 @@ } efree(val); } + ++num_vars; + if (PG(max_input_vars) && num_vars >= PG(max_input_vars)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Post variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); + return; + } s = p + 1; } if (s < e) { Index: branches/PHP_5_3/ext/standard/basic_functions.h =================================================================== --- branches/PHP_5_3/ext/standard/basic_functions.h (revision 321767) +++ branches/PHP_5_3/ext/standard/basic_functions.h (working copy) @@ -208,6 +208,10 @@ /* var.c */ zend_class_entry *incomplete_class; + struct { + long max_vars; + long num_vars; + } unserialize; /* url_scanner_ex.re */ url_adapt_state_ex_t url_adapt_state_ex; Index: branches/PHP_5_3/ext/standard/var.c =================================================================== --- branches/PHP_5_3/ext/standard/var.c (revision 321767) +++ branches/PHP_5_3/ext/standard/var.c (working copy) @@ -923,8 +923,9 @@ int buf_len; const unsigned char *p; php_unserialize_data_t var_hash; + long max_vars = PG(max_input_vars); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &buf, &buf_len, &max_vars) == FAILURE) { RETURN_FALSE; } @@ -934,10 +935,13 @@ p = (const unsigned char*) buf; PHP_VAR_UNSERIALIZE_INIT(var_hash); + PHP_VAR_UNSERIALIZE_MAX_VARS(max_vars); if (!php_var_unserialize(&return_value, &p, p + buf_len, &var_hash TSRMLS_CC)) { PHP_VAR_UNSERIALIZE_DESTROY(var_hash); zval_dtor(return_value); - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len); + if (!BG(unserialize).max_vars || BG(unserialize).num_vars < BG(unserialize).max_vars) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len); + } RETURN_FALSE; } PHP_VAR_UNSERIALIZE_DESTROY(var_hash); Index: branches/PHP_5_3/ext/standard/php_var.h =================================================================== --- branches/PHP_5_3/ext/standard/php_var.h (revision 321767) +++ branches/PHP_5_3/ext/standard/php_var.h (working copy) @@ -21,6 +21,7 @@ #ifndef PHP_VAR_H #define PHP_VAR_H +#include "ext/standard/basic_functions.h" #include "ext/standard/php_smart_str_public.h" PHP_FUNCTION(var_dump); @@ -57,10 +58,15 @@ #define PHP_VAR_UNSERIALIZE_INIT(var_hash) \ (var_hash).first = 0; \ - (var_hash).first_dtor = 0 + (var_hash).first_dtor = 0; \ + BG(unserialize).num_vars = 0; \ + BG(unserialize).max_vars = 0 + #define PHP_VAR_UNSERIALIZE_DESTROY(var_hash) \ var_destroy(&(var_hash)) +#define PHP_VAR_UNSERIALIZE_MAX_VARS(max_vars) BG(unserialize).max_vars = max_vars + 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_destroy(php_unserialize_data_t *var_hash); Index: branches/PHP_5_3/ext/standard/var_unserializer.re =================================================================== --- branches/PHP_5_3/ext/standard/var_unserializer.re (revision 321767) +++ branches/PHP_5_3/ext/standard/var_unserializer.re (working copy) @@ -269,6 +269,7 @@ FREE_ZVAL(key); return 0; } + --(BG(unserialize).num_vars); if (Z_TYPE_P(key) != IS_LONG && Z_TYPE_P(key) != IS_STRING) { zval_dtor(key); @@ -398,6 +399,11 @@ limit = cursor = *p; + if (BG(unserialize).max_vars && BG(unserialize).num_vars >= BG(unserialize).max_vars) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unserialized variables exceeded %ld", BG(unserialize).max_vars); + return 0; + } + if (var_hash && cursor[0] != 'R') { var_push(var_hash, rval); } @@ -425,7 +431,7 @@ *rval = *rval_ref; Z_ADDREF_PP(rval); Z_SET_ISREF_PP(rval); - + ++(BG(unserialize).num_vars); return 1; } @@ -449,6 +455,7 @@ Z_ADDREF_PP(rval); Z_UNSET_ISREF_PP(rval); + ++(BG(unserialize).num_vars); return 1; } @@ -456,6 +463,7 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); + ++(BG(unserialize).num_vars); return 1; } @@ -463,6 +471,7 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_BOOL(*rval, parse_iv(start + 2)); + ++(BG(unserialize).num_vars); return 1; } @@ -490,6 +499,7 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_LONG(*rval, parse_iv(start + 2)); + ++(BG(unserialize).num_vars); return 1; } @@ -505,6 +515,7 @@ ZVAL_DOUBLE(*rval, -php_get_inf()); } + ++(BG(unserialize).num_vars); return 1; } @@ -515,6 +526,7 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); + ++(BG(unserialize).num_vars); return 1; } @@ -543,6 +555,7 @@ INIT_PZVAL(*rval); ZVAL_STRINGL(*rval, str, len, 1); + ++(BG(unserialize).num_vars); return 1; } @@ -572,6 +585,7 @@ INIT_PZVAL(*rval); ZVAL_STRINGL(*rval, str, len, 0); + ++(BG(unserialize).num_vars); return 1; } @@ -588,6 +602,7 @@ array_init_size(*rval, elements); + ++(BG(unserialize).num_vars); if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_PP(rval), elements, 0)) { return 0; } @@ -599,6 +614,7 @@ INIT_PZVAL(*rval); + ++(BG(unserialize).num_vars); return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } @@ -701,6 +717,7 @@ *p = YYCURSOR; if (custom_object) { + ++(BG(unserialize).num_vars); int ret = object_custom(UNSERIALIZE_PASSTHRU, ce); if (ret && incomplete_class) { @@ -717,6 +734,7 @@ } efree(class_name); + ++(BG(unserialize).num_vars); return object_common2(UNSERIALIZE_PASSTHRU, elements); } Index: branches/PHP_5_3/main/rfc1867.c =================================================================== --- branches/PHP_5_3/main/rfc1867.c (revision 321767) +++ branches/PHP_5_3/main/rfc1867.c (working copy) @@ -779,6 +779,7 @@ void *event_extra_data = NULL; int llen = 0; int upload_cnt = INI_INT("max_file_uploads"); + long total_num_vars = 0; if (SG(post_max_size) > 0 && SG(request_info).content_length > SG(post_max_size)) { sapi_module.sapi_error(E_WARNING, "POST Content-Length of %ld bytes exceeds the limit of %ld bytes", SG(request_info).content_length, SG(post_max_size)); @@ -962,6 +963,11 @@ efree(param); efree(value); + ++total_num_vars; + if (PG(max_input_vars) && total_num_vars >= PG(max_input_vars)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Post variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); + goto fileupload_done; + } continue; } Index: branches/PHP_5_3/main/php_version.h =================================================================== --- branches/PHP_5_3/main/php_version.h (revision 321767) +++ branches/PHP_5_3/main/php_version.h (working copy) @@ -2,7 +2,7 @@ /* edit configure.in to change version number */ #define PHP_MAJOR_VERSION 5 #define PHP_MINOR_VERSION 3 -#define PHP_RELEASE_VERSION 9 +#define PHP_RELEASE_VERSION 7 #define PHP_EXTRA_VERSION "RC5-dev" -#define PHP_VERSION "5.3.9RC5-dev" -#define PHP_VERSION_ID 50309 +#define PHP_VERSION "5.3.7RC5-dev" +#define PHP_VERSION_ID 50307 Index: branches/PHP_5_3/main/php_variables.c =================================================================== --- branches/PHP_5_3/main/php_variables.c (revision 321767) +++ branches/PHP_5_3/main/php_variables.c (working copy) @@ -191,14 +191,9 @@ } if (zend_symtable_find(symtable1, escaped_index, index_len + 1, (void **) &gpc_element_p) == FAILURE || Z_TYPE_PP(gpc_element_p) != IS_ARRAY) { - if (zend_hash_num_elements(symtable1) <= PG(max_input_vars)) { - if (zend_hash_num_elements(symtable1) == PG(max_input_vars)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); - } - MAKE_STD_ZVAL(gpc_element); - array_init(gpc_element); - zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); - } + MAKE_STD_ZVAL(gpc_element); + array_init(gpc_element); + zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); } if (index != escaped_index) { efree(escaped_index); @@ -241,14 +236,7 @@ zend_symtable_exists(symtable1, escaped_index, index_len + 1)) { zval_ptr_dtor(&gpc_element); } else { - if (zend_hash_num_elements(symtable1) <= PG(max_input_vars)) { - if (zend_hash_num_elements(symtable1) == PG(max_input_vars)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); - } - zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); - } else { - zval_ptr_dtor(&gpc_element); - } + zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); } if (escaped_index != index) { efree(escaped_index); @@ -262,6 +250,7 @@ { char *var, *val, *e, *s, *p; zval *array_ptr = (zval *) arg; + long num_vars = 0; if (SG(request_info).post_data == NULL) { return; @@ -286,6 +275,10 @@ } efree(val); } + if (++num_vars >= PG(max_input_vars)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Post variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); + return; + } s = p + 1; } if (s < e) { Index: branches/PHP_5_4/ext/standard/basic_functions.h =================================================================== --- branches/PHP_5_4/ext/standard/basic_functions.h (revision 321767) +++ branches/PHP_5_4/ext/standard/basic_functions.h (working copy) @@ -212,6 +212,8 @@ struct { void *var_hash; unsigned level; + long max_vars; + long num_vars; } unserialize; /* url_scanner_ex.re */ Index: branches/PHP_5_4/ext/standard/tests/serialize/serialization_error_001.phpt =================================================================== --- branches/PHP_5_4/ext/standard/tests/serialize/serialization_error_001.phpt (revision 321767) +++ branches/PHP_5_4/ext/standard/tests/serialize/serialization_error_001.phpt (working copy) @@ -28,15 +28,15 @@ --EXPECTF-- *** Testing serialize()/unserialize() : error conditions *** -Warning: serialize() expects exactly 1 parameter, 0 given in %s on line 16 +Warning: serialize() expects exactly 1 parameter, 0 given in %sserialization_error_001.php on line %d NULL -Warning: unserialize() expects exactly 1 parameter, 0 given in %s on line 17 +Warning: unserialize() expects at least 1 parameter, 0 given in %sserialization_error_001.php on line %d bool(false) -Warning: serialize() expects exactly 1 parameter, 2 given in %s on line 20 +Warning: serialize() expects exactly 1 parameter, 2 given in %sserialization_error_001.php on line %d NULL -Warning: unserialize() expects exactly 1 parameter, 2 given in %s on line 21 +Notice: unserialize(): Error at offset 0 of 1 bytes in %sserialization_error_001.php on line %d bool(false) Done Index: branches/PHP_5_4/ext/standard/tests/serialize/unserialize_max_vars.phpt =================================================================== --- branches/PHP_5_4/ext/standard/tests/serialize/unserialize_max_vars.phpt (revision 0) +++ branches/PHP_5_4/ext/standard/tests/serialize/unserialize_max_vars.phpt (revision 0) @@ -0,0 +1,57 @@ +--TEST-- +Test unserialize() functions with max_input_vars +--FILE-- +<?php +$str = serialize(array(1,2,3)); // there will be 4 items, array, and 1, 2, 3 + +print_r(unserialize($str, 3)); +print_r(unserialize($str, 4)); + +$str = serialize(array(array(),array(2,3))); // there will be 5 items, array, and array, array, 2, 3 +print_r(unserialize($str, 3)); +print_r(unserialize($str, 5)); + +$obj = (object)(array(array(),array(2,3))); +$str = serialize($obj); + +print_r(unserialize($str, 3)); +print_r(unserialize($str, 5)); +?> +--EXPECTF-- +Warning: unserialize(): Unserialized variables exceeded 3 in %sunserialize_max_vars.php on line %d +Array +( + [0] => 1 + [1] => 2 + [2] => 3 +) + +Warning: unserialize(): Unserialized variables exceeded 3 in %sunserialize_max_vars.php on line %d +Array +( + [0] => Array + ( + ) + + [1] => Array + ( + [0] => 2 + [1] => 3 + ) + +) + +Warning: unserialize(): Unserialized variables exceeded 3 in %sunserialize_max_vars.php on line %d +stdClass Object +( + [0] => Array + ( + ) + + [1] => Array + ( + [0] => 2 + [1] => 3 + ) + +) Index: branches/PHP_5_4/ext/standard/var_unserializer.c =================================================================== --- branches/PHP_5_4/ext/standard/var_unserializer.c (revision 321767) +++ branches/PHP_5_4/ext/standard/var_unserializer.c (working copy) @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 on Wed Nov 9 19:37:48 2011 */ +/* Generated by re2c 0.13.5 on Wed Jan 4 23:12:56 2012 */ #line 1 "ext/standard/var_unserializer.re" /* +----------------------------------------------------------------------+ @@ -276,6 +276,7 @@ FREE_ZVAL(key); return 0; } + --(BG(unserialize).num_vars); if (Z_TYPE_P(key) != IS_LONG && Z_TYPE_P(key) != IS_STRING) { zval_dtor(key); @@ -297,12 +298,14 @@ switch (Z_TYPE_P(key)) { case IS_LONG: if (zend_hash_index_find(ht, Z_LVAL_P(key), (void **)&old_data)==SUCCESS) { + --(BG(unserialize).num_vars); var_push_dtor(var_hash, old_data); } zend_hash_index_update(ht, Z_LVAL_P(key), &data, sizeof(data), NULL); break; case IS_STRING: if (zend_symtable_find(ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, (void **)&old_data)==SUCCESS) { + --(BG(unserialize).num_vars); var_push_dtor(var_hash, old_data); } zend_symtable_update(ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &data, sizeof(data), NULL); @@ -411,17 +414,20 @@ if (YYCURSOR >= YYLIMIT) { return 0; } + + if (BG(unserialize).max_vars && BG(unserialize).num_vars >= BG(unserialize).max_vars) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unserialized variables exceeded %ld", BG(unserialize).max_vars); + return 0; + } if (var_hash && cursor[0] != 'R') { var_push(var_hash, rval); } start = cursor; - - -#line 425 "ext/standard/var_unserializer.c" +#line 431 "ext/standard/var_unserializer.c" { YYCTYPE yych; static const unsigned char yybm[] = { @@ -481,9 +487,9 @@ yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy95; yy3: -#line 747 "ext/standard/var_unserializer.re" +#line 766 "ext/standard/var_unserializer.re" { return 0; } -#line 487 "ext/standard/var_unserializer.c" +#line 493 "ext/standard/var_unserializer.c" yy4: yych = *(YYMARKER = ++YYCURSOR); if (yych == ':') goto yy89; @@ -526,13 +532,13 @@ goto yy3; yy14: ++YYCURSOR; -#line 741 "ext/standard/var_unserializer.re" +#line 760 "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 536 "ext/standard/var_unserializer.c" +#line 542 "ext/standard/var_unserializer.c" yy16: yych = *++YYCURSOR; goto yy3; @@ -562,7 +568,7 @@ yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 624 "ext/standard/var_unserializer.re" +#line 641 "ext/standard/var_unserializer.re" { size_t len, len2, len3, maxlen; long elements; @@ -661,6 +667,7 @@ *p = YYCURSOR; if (custom_object) { + ++(BG(unserialize).num_vars); int ret = object_custom(UNSERIALIZE_PASSTHRU, ce); if (ret && incomplete_class) { @@ -677,9 +684,10 @@ } efree(class_name); + ++(BG(unserialize).num_vars); return object_common2(UNSERIALIZE_PASSTHRU, elements); } -#line 683 "ext/standard/var_unserializer.c" +#line 691 "ext/standard/var_unserializer.c" yy25: yych = *++YYCURSOR; if (yych <= ',') { @@ -704,15 +712,16 @@ yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 616 "ext/standard/var_unserializer.re" +#line 632 "ext/standard/var_unserializer.re" { INIT_PZVAL(*rval); + ++(BG(unserialize).num_vars); return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } -#line 716 "ext/standard/var_unserializer.c" +#line 725 "ext/standard/var_unserializer.c" yy32: yych = *++YYCURSOR; if (yych == '+') goto yy33; @@ -733,7 +742,7 @@ yych = *++YYCURSOR; if (yych != '{') goto yy18; ++YYCURSOR; -#line 596 "ext/standard/var_unserializer.re" +#line 611 "ext/standard/var_unserializer.re" { long elements = parse_iv(start + 2); /* use iv() not uiv() in order to check data range */ @@ -747,13 +756,14 @@ array_init_size(*rval, elements); + ++(BG(unserialize).num_vars); if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_PP(rval), elements, 0)) { return 0; } return finish_nested_data(UNSERIALIZE_PASSTHRU); } -#line 757 "ext/standard/var_unserializer.c" +#line 767 "ext/standard/var_unserializer.c" yy39: yych = *++YYCURSOR; if (yych == '+') goto yy40; @@ -774,7 +784,7 @@ yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 567 "ext/standard/var_unserializer.re" +#line 581 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -801,9 +811,10 @@ INIT_PZVAL(*rval); ZVAL_STRINGL(*rval, str, len, 0); + ++(BG(unserialize).num_vars); return 1; } -#line 807 "ext/standard/var_unserializer.c" +#line 818 "ext/standard/var_unserializer.c" yy46: yych = *++YYCURSOR; if (yych == '+') goto yy47; @@ -824,7 +835,7 @@ yych = *++YYCURSOR; if (yych != '"') goto yy18; ++YYCURSOR; -#line 539 "ext/standard/var_unserializer.re" +#line 552 "ext/standard/var_unserializer.re" { size_t len, maxlen; char *str; @@ -850,9 +861,10 @@ INIT_PZVAL(*rval); ZVAL_STRINGL(*rval, str, len, 1); + ++(BG(unserialize).num_vars); return 1; } -#line 856 "ext/standard/var_unserializer.c" +#line 868 "ext/standard/var_unserializer.c" yy53: yych = *++YYCURSOR; if (yych <= '/') { @@ -940,7 +952,7 @@ } yy63: ++YYCURSOR; -#line 529 "ext/standard/var_unserializer.re" +#line 541 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 use_double: @@ -948,9 +960,10 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); + ++(BG(unserialize).num_vars); return 1; } -#line 954 "ext/standard/var_unserializer.c" +#line 967 "ext/standard/var_unserializer.c" yy65: yych = *++YYCURSOR; if (yych <= ',') { @@ -1009,7 +1022,7 @@ yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 514 "ext/standard/var_unserializer.re" +#line 525 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); @@ -1022,9 +1035,10 @@ ZVAL_DOUBLE(*rval, -php_get_inf()); } + ++(BG(unserialize).num_vars); return 1; } -#line 1028 "ext/standard/var_unserializer.c" +#line 1042 "ext/standard/var_unserializer.c" yy76: yych = *++YYCURSOR; if (yych == 'N') goto yy73; @@ -1051,7 +1065,7 @@ if (yych <= '9') goto yy79; if (yych != ';') goto yy18; ++YYCURSOR; -#line 487 "ext/standard/var_unserializer.re" +#line 497 "ext/standard/var_unserializer.re" { #if SIZEOF_LONG == 4 int digits = YYCURSOR - start - 3; @@ -1076,9 +1090,10 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_LONG(*rval, parse_iv(start + 2)); + ++(BG(unserialize).num_vars); return 1; } -#line 1082 "ext/standard/var_unserializer.c" +#line 1097 "ext/standard/var_unserializer.c" yy83: yych = *++YYCURSOR; if (yych <= '/') goto yy18; @@ -1086,24 +1101,26 @@ yych = *++YYCURSOR; if (yych != ';') goto yy18; ++YYCURSOR; -#line 480 "ext/standard/var_unserializer.re" +#line 489 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_BOOL(*rval, parse_iv(start + 2)); + ++(BG(unserialize).num_vars); return 1; } -#line 1097 "ext/standard/var_unserializer.c" +#line 1113 "ext/standard/var_unserializer.c" yy87: ++YYCURSOR; -#line 473 "ext/standard/var_unserializer.re" +#line 481 "ext/standard/var_unserializer.re" { *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); + ++(BG(unserialize).num_vars); return 1; } -#line 1107 "ext/standard/var_unserializer.c" +#line 1124 "ext/standard/var_unserializer.c" yy89: yych = *++YYCURSOR; if (yych <= ',') { @@ -1126,7 +1143,7 @@ if (yych <= '9') goto yy91; if (yych != ';') goto yy18; ++YYCURSOR; -#line 450 "ext/standard/var_unserializer.re" +#line 457 "ext/standard/var_unserializer.re" { long id; @@ -1147,9 +1164,10 @@ Z_ADDREF_PP(rval); Z_UNSET_ISREF_PP(rval); + ++(BG(unserialize).num_vars); return 1; } -#line 1153 "ext/standard/var_unserializer.c" +#line 1171 "ext/standard/var_unserializer.c" yy95: yych = *++YYCURSOR; if (yych <= ',') { @@ -1172,7 +1190,7 @@ if (yych <= '9') goto yy97; if (yych != ';') goto yy18; ++YYCURSOR; -#line 429 "ext/standard/var_unserializer.re" +#line 435 "ext/standard/var_unserializer.re" { long id; @@ -1191,11 +1209,12 @@ Z_ADDREF_PP(rval); Z_SET_ISREF_PP(rval); + ++(BG(unserialize).num_vars); return 1; } -#line 1197 "ext/standard/var_unserializer.c" +#line 1216 "ext/standard/var_unserializer.c" } -#line 749 "ext/standard/var_unserializer.re" +#line 768 "ext/standard/var_unserializer.re" return 0; Index: branches/PHP_5_4/ext/standard/var.c =================================================================== --- branches/PHP_5_4/ext/standard/var.c (revision 321767) +++ branches/PHP_5_4/ext/standard/var.c (working copy) @@ -928,8 +928,9 @@ int buf_len; const unsigned char *p; php_unserialize_data_t var_hash; + long max_vars = PG(max_input_vars); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &buf, &buf_len) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &buf, &buf_len, &max_vars) == FAILURE) { RETURN_FALSE; } @@ -939,10 +940,13 @@ p = (const unsigned char*) buf; PHP_VAR_UNSERIALIZE_INIT(var_hash); + PHP_VAR_UNSERIALIZE_MAX_VARS(max_vars); if (!php_var_unserialize(&return_value, &p, p + buf_len, &var_hash TSRMLS_CC)) { PHP_VAR_UNSERIALIZE_DESTROY(var_hash); zval_dtor(return_value); - php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len); + if (!BG(unserialize).max_vars || BG(unserialize).num_vars < BG(unserialize).max_vars) { + php_error_docref(NULL TSRMLS_CC, E_NOTICE, "Error at offset %ld of %d bytes", (long)((char*)p - buf), buf_len); + } RETURN_FALSE; } PHP_VAR_UNSERIALIZE_DESTROY(var_hash); Index: branches/PHP_5_4/ext/standard/php_var.h =================================================================== --- branches/PHP_5_4/ext/standard/php_var.h (revision 321767) +++ branches/PHP_5_4/ext/standard/php_var.h (working copy) @@ -95,9 +95,13 @@ } else { \ (var_hash_ptr) = (php_serialize_data_t)BG(unserialize).var_hash; \ ++BG(unserialize).level; \ - } \ + } \ + BG(unserialize).num_vars = 0; \ + BG(unserialize).max_vars = 0; \ } while (0) +#define PHP_VAR_UNSERIALIZE_MAX_VARS(max_vars) BG(unserialize).max_vars = max_vars; + #define PHP_VAR_UNSERIALIZE_DESTROY(var_hash_ptr) \ do { \ /* fprintf(stderr, "UNSERIALIZE_DESTROY == lock: %u, level: %u\n", BG(serialize_lock), BG(unserialize).level); */ \ Index: branches/PHP_5_4/ext/standard/var_unserializer.re =================================================================== --- branches/PHP_5_4/ext/standard/var_unserializer.re (revision 321767) +++ branches/PHP_5_4/ext/standard/var_unserializer.re (working copy) @@ -280,6 +280,7 @@ FREE_ZVAL(key); return 0; } + --(BG(unserialize).num_vars); if (Z_TYPE_P(key) != IS_LONG && Z_TYPE_P(key) != IS_STRING) { zval_dtor(key); @@ -301,12 +302,14 @@ switch (Z_TYPE_P(key)) { case IS_LONG: if (zend_hash_index_find(ht, Z_LVAL_P(key), (void **)&old_data)==SUCCESS) { + --(BG(unserialize).num_vars); var_push_dtor(var_hash, old_data); } zend_hash_index_update(ht, Z_LVAL_P(key), &data, sizeof(data), NULL); break; case IS_STRING: if (zend_symtable_find(ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, (void **)&old_data)==SUCCESS) { + --(BG(unserialize).num_vars); var_push_dtor(var_hash, old_data); } zend_symtable_update(ht, Z_STRVAL_P(key), Z_STRLEN_P(key) + 1, &data, sizeof(data), NULL); @@ -415,15 +418,18 @@ if (YYCURSOR >= YYLIMIT) { return 0; } + + if (BG(unserialize).max_vars && BG(unserialize).num_vars >= BG(unserialize).max_vars) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unserialized variables exceeded %ld", BG(unserialize).max_vars); + return 0; + } if (var_hash && cursor[0] != 'R') { var_push(var_hash, rval); } start = cursor; - - /*!re2c "R:" iv ";" { @@ -444,6 +450,7 @@ Z_ADDREF_PP(rval); Z_SET_ISREF_PP(rval); + ++(BG(unserialize).num_vars); return 1; } @@ -467,6 +474,7 @@ Z_ADDREF_PP(rval); Z_UNSET_ISREF_PP(rval); + ++(BG(unserialize).num_vars); return 1; } @@ -474,6 +482,7 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_NULL(*rval); + ++(BG(unserialize).num_vars); return 1; } @@ -481,6 +490,7 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_BOOL(*rval, parse_iv(start + 2)); + ++(BG(unserialize).num_vars); return 1; } @@ -508,6 +518,7 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_LONG(*rval, parse_iv(start + 2)); + ++(BG(unserialize).num_vars); return 1; } @@ -523,6 +534,7 @@ ZVAL_DOUBLE(*rval, -php_get_inf()); } + ++(BG(unserialize).num_vars); return 1; } @@ -533,6 +545,7 @@ *p = YYCURSOR; INIT_PZVAL(*rval); ZVAL_DOUBLE(*rval, zend_strtod((const char *)start + 2, NULL)); + ++(BG(unserialize).num_vars); return 1; } @@ -561,6 +574,7 @@ INIT_PZVAL(*rval); ZVAL_STRINGL(*rval, str, len, 1); + ++(BG(unserialize).num_vars); return 1; } @@ -590,6 +604,7 @@ INIT_PZVAL(*rval); ZVAL_STRINGL(*rval, str, len, 0); + ++(BG(unserialize).num_vars); return 1; } @@ -606,6 +621,7 @@ array_init_size(*rval, elements); + ++(BG(unserialize).num_vars); if (!process_nested_data(UNSERIALIZE_PASSTHRU, Z_ARRVAL_PP(rval), elements, 0)) { return 0; } @@ -617,6 +633,7 @@ INIT_PZVAL(*rval); + ++(BG(unserialize).num_vars); return object_common2(UNSERIALIZE_PASSTHRU, object_common1(UNSERIALIZE_PASSTHRU, ZEND_STANDARD_CLASS_DEF_PTR)); } @@ -719,6 +736,7 @@ *p = YYCURSOR; if (custom_object) { + ++(BG(unserialize).num_vars); int ret = object_custom(UNSERIALIZE_PASSTHRU, ce); if (ret && incomplete_class) { @@ -735,6 +753,7 @@ } efree(class_name); + ++(BG(unserialize).num_vars); return object_common2(UNSERIALIZE_PASSTHRU, elements); } Index: branches/PHP_5_4/ext/json/json.c =================================================================== --- branches/PHP_5_4/ext/json/json.c (revision 321767) +++ branches/PHP_5_4/ext/json/json.c (working copy) @@ -99,6 +99,7 @@ REGISTER_LONG_CONSTANT("JSON_ERROR_NONE", PHP_JSON_ERROR_NONE, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_ERROR_DEPTH", PHP_JSON_ERROR_DEPTH, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("JSON_ERROR_MAX_VARS", PHP_JSON_ERROR_MAX_VARS, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_ERROR_STATE_MISMATCH", PHP_JSON_ERROR_STATE_MISMATCH, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_ERROR_CTRL_CHAR", PHP_JSON_ERROR_CTRL_CHAR, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("JSON_ERROR_SYNTAX", PHP_JSON_ERROR_SYNTAX, CONST_CS | CONST_PERSISTENT); @@ -602,7 +603,7 @@ } /* }}} */ -PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC) /* {{{ */ +PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth, long max_vars TSRMLS_DC) /* {{{ */ { int utf16_len; zval *z; @@ -627,7 +628,7 @@ } ALLOC_INIT_ZVAL(z); - jp = new_JSON_parser(depth); + jp = new_JSON_parser(depth, max_vars); if (parse_JSON_ex(jp, z, utf16, utf16_len, options TSRMLS_CC)) { *return_value = *z; } @@ -671,7 +672,6 @@ } /* }}} */ - /* {{{ proto string json_encode(mixed data [, int options]) Returns the JSON representation of a value */ static PHP_FUNCTION(json_encode) @@ -703,8 +703,9 @@ zend_bool assoc = 0; /* return JS objects as PHP objects by default */ long depth = JSON_PARSER_DEFAULT_DEPTH; long options = 0; + long max_vars = PG(max_input_vars); - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bll", &str, &str_len, &assoc, &depth, &options) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|blll", &str, &str_len, &assoc, &depth, &max_vars, &options) == FAILURE) { return; } @@ -721,7 +722,7 @@ options &= ~PHP_JSON_OBJECT_AS_ARRAY; } - php_json_decode_ex(return_value, str, str_len, options, depth TSRMLS_CC); + php_json_decode_ex(return_value, str, str_len, options, depth, max_vars TSRMLS_CC); } /* }}} */ Index: branches/PHP_5_4/ext/json/tests/008.phpt =================================================================== --- branches/PHP_5_4/ext/json/tests/008.phpt (revision 321767) +++ branches/PHP_5_4/ext/json/tests/008.phpt (working copy) @@ -7,7 +7,7 @@ $json = '{"largenum":123456789012345678901234567890}'; $x = json_decode($json); var_dump($x->largenum); -$x = json_decode($json, false, 512, JSON_BIGINT_AS_STRING); +$x = json_decode($json, false, 512, 1000, JSON_BIGINT_AS_STRING); var_dump($x->largenum); echo "Done\n"; ?> Index: branches/PHP_5_4/ext/json/tests/json_decode_error.phpt =================================================================== --- branches/PHP_5_4/ext/json/tests/json_decode_error.phpt (revision 321767) +++ branches/PHP_5_4/ext/json/tests/json_decode_error.phpt (working copy) @@ -20,7 +20,7 @@ echo "\n-- Testing json_decode() function with more than expected no. of arguments --\n"; $extra_arg = 10; -var_dump( json_decode('"abc"', TRUE, 512, 0, $extra_arg) ); +var_dump( json_decode('"abc"', TRUE, 512, 0, 1000, $extra_arg) ); ?> ===Done=== @@ -34,6 +34,6 @@ -- Testing json_decode() function with more than expected no. of arguments -- -Warning: json_decode() expects at most 4 parameters, 5 given in %s on line %d +Warning: json_decode() expects at most 5 parameters, 6 given in %s on line %d NULL ===Done=== Index: branches/PHP_5_4/ext/json/JSON_parser.c =================================================================== --- branches/PHP_5_4/ext/json/JSON_parser.c (revision 321767) +++ branches/PHP_5_4/ext/json/JSON_parser.c (working copy) @@ -239,11 +239,12 @@ JSON_checker_char will delete the JSON_checker object if it sees an error. */ JSON_parser -new_JSON_parser(int depth) +new_JSON_parser(int depth, long max_vars) { JSON_parser jp = (JSON_parser)emalloc(sizeof(struct JSON_parser_struct)); jp->state = GO; jp->depth = depth; + jp->max_vars = max_vars; jp->top = -1; jp->error_code = PHP_JSON_ERROR_NONE; jp->stack = (int*)ecalloc(depth, sizeof(int)); @@ -443,6 +444,7 @@ int next_state; /* the next state */ int the_index; int assoc = options & PHP_JSON_OBJECT_AS_ARRAY; + long num_vars = 1; /* the container is also counted in */ smart_str buf = {0}; smart_str key = {0}; @@ -453,6 +455,11 @@ JSON_RESET_TYPE(); for (the_index = 0; the_index < length; the_index += 1) { + if (jp->max_vars && num_vars >= jp->max_vars) { + jp->error_code = PHP_JSON_ERROR_MAX_VARS; + FREE_BUFFERS(); + return false; + } next_char = utf16_json[the_index]; if (next_char >= 128) { next_class = C_ETC; @@ -555,8 +562,10 @@ } else { add_assoc_zval_ex(jp->the_zstack[jp->top], (key.len ? key.c : ""), (key.len ? (key.len + 1) : sizeof("")), mval); } + key.len = 0; buf.len = 0; + ++num_vars; JSON_RESET_TYPE(); } @@ -574,10 +583,10 @@ { zval *mval; smart_str_0(&buf); - json_create_zval(&mval, &buf, type, options); add_next_index_zval(jp->the_zstack[jp->top], mval); buf.len = 0; + ++num_vars; JSON_RESET_TYPE(); } @@ -602,6 +611,7 @@ if (jp->top == 1) { obj = z; } else { + ++num_vars; ALLOC_INIT_ZVAL(obj); } @@ -611,6 +621,7 @@ array_init(obj); } + ++num_vars; jp->the_zstack[jp->top] = obj; if (jp->top > 1) { @@ -643,6 +654,7 @@ if (jp->top > 1) { attach_zval(jp, jp->top - 1, jp->top, &key, assoc TSRMLS_CC); + ++num_vars; } JSON_RESET_TYPE(); @@ -700,6 +712,7 @@ } else { add_assoc_zval_ex(jp->the_zstack[jp->top], (key.len ? key.c : ""), (key.len ? (key.len + 1) : sizeof("")), mval); } + ++num_vars; key.len = 0; } jp->state = KE; @@ -708,6 +721,7 @@ case MODE_ARRAY: if (type != -1) { add_next_index_zval(jp->the_zstack[jp->top], mval); + ++num_vars; } jp->state = VA; break; Index: branches/PHP_5_4/ext/json/php_json.h =================================================================== --- branches/PHP_5_4/ext/json/php_json.h (revision 321767) +++ branches/PHP_5_4/ext/json/php_json.h (working copy) @@ -49,7 +49,7 @@ #endif PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC); -PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth TSRMLS_DC); +PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, int options, long depth, long max_vars TSRMLS_DC); extern zend_class_entry *php_json_serializable_ce; @@ -74,7 +74,7 @@ static inline void php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, long depth TSRMLS_DC) { - php_json_decode_ex(return_value, str, str_len, assoc ? PHP_JSON_OBJECT_AS_ARRAY : 0, depth TSRMLS_CC); + php_json_decode_ex(return_value, str, str_len, assoc ? PHP_JSON_OBJECT_AS_ARRAY : 0, depth, 0 TSRMLS_CC); } Index: branches/PHP_5_4/ext/json/JSON_parser.h =================================================================== --- branches/PHP_5_4/ext/json/JSON_parser.h (revision 321767) +++ branches/PHP_5_4/ext/json/JSON_parser.h (working copy) @@ -15,20 +15,22 @@ int top; int error_code; int* stack; + long max_vars; zval **the_zstack; zval *the_static_zstack[JSON_PARSER_DEFAULT_DEPTH]; } * JSON_parser; enum error_codes { PHP_JSON_ERROR_NONE = 0, - PHP_JSON_ERROR_DEPTH, + PHP_JSON_ERROR_DEPTH, PHP_JSON_ERROR_STATE_MISMATCH, PHP_JSON_ERROR_CTRL_CHAR, PHP_JSON_ERROR_SYNTAX, - PHP_JSON_ERROR_UTF8 + PHP_JSON_ERROR_UTF8, + PHP_JSON_ERROR_MAX_VARS, }; -extern JSON_parser new_JSON_parser(int depth); +extern JSON_parser new_JSON_parser(int depth, long max_vars); extern int parse_JSON_ex(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int options TSRMLS_DC); extern int free_JSON_parser(JSON_parser jp); Index: branches/PHP_5_4/main/rfc1867.c =================================================================== --- branches/PHP_5_4/main/rfc1867.c (revision 321767) +++ branches/PHP_5_4/main/rfc1867.c (working copy) @@ -686,6 +686,7 @@ zend_llist header; void *event_extra_data = NULL; unsigned int llen = 0; + long num_vars = 0; int upload_cnt = INI_INT("max_file_uploads"); const zend_encoding *internal_encoding = zend_multibyte_get_internal_encoding(TSRMLS_C); php_rfc1867_getword_t getword; @@ -896,6 +897,11 @@ efree(param); efree(value); + ++num_vars; + if (PG(max_input_vars) && num_vars >= PG(max_input_vars)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Post variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); + goto fileupload_done; + } continue; } Index: branches/PHP_5_4/main/php_version.h =================================================================== --- branches/PHP_5_4/main/php_version.h (revision 321767) +++ branches/PHP_5_4/main/php_version.h (working copy) @@ -3,6 +3,6 @@ #define PHP_MAJOR_VERSION 5 #define PHP_MINOR_VERSION 4 #define PHP_RELEASE_VERSION 0 -#define PHP_EXTRA_VERSION "RC5-dev" -#define PHP_VERSION "5.4.0RC5-dev" +#define PHP_EXTRA_VERSION "RC2-dev" +#define PHP_VERSION "5.4.0RC2-dev" #define PHP_VERSION_ID 50400 Index: branches/PHP_5_4/main/php_variables.c =================================================================== --- branches/PHP_5_4/main/php_variables.c (revision 321767) +++ branches/PHP_5_4/main/php_variables.c (working copy) @@ -178,15 +178,10 @@ } else { escaped_index = index; if (zend_symtable_find(symtable1, escaped_index, index_len + 1, (void **) &gpc_element_p) == FAILURE - || Z_TYPE_PP(gpc_element_p) != IS_ARRAY) { - if (zend_hash_num_elements(symtable1) <= PG(max_input_vars)) { - if (zend_hash_num_elements(symtable1) == PG(max_input_vars)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); - } - MAKE_STD_ZVAL(gpc_element); - array_init(gpc_element); - zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); - } + || Z_TYPE_PP(gpc_element_p) != IS_ARRAY) { + MAKE_STD_ZVAL(gpc_element); + array_init(gpc_element); + zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); } if (index != escaped_index) { efree(escaped_index); @@ -225,14 +220,7 @@ zend_symtable_exists(symtable1, escaped_index, index_len + 1)) { zval_ptr_dtor(&gpc_element); } else { - if (zend_hash_num_elements(symtable1) <= PG(max_input_vars)) { - if (zend_hash_num_elements(symtable1) == PG(max_input_vars)) { - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Input variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); - } - zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); - } else { - zval_ptr_dtor(&gpc_element); - } + zend_symtable_update(symtable1, escaped_index, index_len + 1, &gpc_element, sizeof(zval *), (void **) &gpc_element_p); } if (escaped_index != index) { efree(escaped_index); @@ -246,6 +234,7 @@ { char *var, *val, *e, *s, *p; zval *array_ptr = (zval *) arg; + long num_vars = 0; if (SG(request_info).post_data == NULL) { return; @@ -270,6 +259,11 @@ } efree(val); } + ++num_vars; + if (PG(max_input_vars) && num_vars >= PG(max_input_vars)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Post variables exceeded %ld. To increase the limit change max_input_vars in php.ini.", PG(max_input_vars)); + return; + } s = p + 1; } if (s < e) { |
Copyright © 2001-2024 The PHP Group All rights reserved. |
Last updated: Thu Oct 31 23:01:28 2024 UTC |