Patch max_input_vars.patch for *General Issues Bug #60655
Patch version 2012-01-05 05:04 UTC
Return to Bug #60655 |
Download this patch
Patch Revisions:
Developer: laruence@php.net
Index: 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/ext/json/json.c
===================================================================
--- branches/PHP_5_3/ext/json/json.c (revision 321767)
+++ branches/PHP_5_3/ext/json/json.c (working copy)
@@ -76,6 +76,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);
@@ -485,7 +486,7 @@
}
/* }}} */
-PHP_JSON_API void php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, long depth TSRMLS_DC) /* {{{ */
+PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, zend_bool assoc, long depth, long max_vars TSRMLS_DC) /* {{{ */
{
int utf16_len;
zval *z;
@@ -510,7 +511,7 @@
}
ALLOC_INIT_ZVAL(z);
- jp = new_JSON_parser(depth);
+ jp = new_JSON_parser(depth, max_vars);
if (parse_JSON(jp, z, utf16, utf16_len, assoc TSRMLS_CC)) {
*return_value = *z;
}
@@ -584,8 +585,9 @@
int str_len;
zend_bool assoc = 0; /* return JS objects as PHP objects by default */
long depth = JSON_PARSER_DEFAULT_DEPTH;
+ long max_vars = PG(max_input_vars);
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bl", &str, &str_len, &assoc, &depth) == FAILURE) {
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|bll", &str, &str_len, &assoc, &depth, &max_vars) == FAILURE) {
return;
}
@@ -595,7 +597,7 @@
RETURN_NULL();
}
- php_json_decode(return_value, str, str_len, assoc, depth TSRMLS_CC);
+ php_json_decode_ex(return_value, str, str_len, assoc, depth, max_vars TSRMLS_CC);
}
/* }}} */
Index: branches/PHP_5_3/ext/json/tests/json_decode_error.phpt
===================================================================
--- branches/PHP_5_3/ext/json/tests/json_decode_error.phpt (revision 321767)
+++ branches/PHP_5_3/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, $extra_arg) );
+var_dump( json_decode('"abc"', TRUE, 512, 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 3 parameters, 4 given in %s on line %d
+Warning: json_decode() expects at most 4 parameters, 5 given in %s on line %d
NULL
===Done===
Index: branches/PHP_5_3/ext/json/JSON_parser.c
===================================================================
--- branches/PHP_5_3/ext/json/JSON_parser.c (revision 321767)
+++ branches/PHP_5_3/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));
@@ -426,6 +427,7 @@
int next_class; /* the next character class */
int next_state; /* the next state */
int the_index;
+ long num_vars = 1; /* the container is also counted in */
smart_str buf = {0};
smart_str key = {0};
@@ -436,6 +438,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;
@@ -540,6 +547,7 @@
}
key.len = 0;
buf.len = 0;
+ ++num_vars;
JSON_RESET_TYPE();
}
@@ -561,6 +569,7 @@
json_create_zval(&mval, &buf, type);
add_next_index_zval(jp->the_zstack[jp->top], mval);
buf.len = 0;
+ ++num_vars;
JSON_RESET_TYPE();
}
@@ -585,6 +594,7 @@
if (jp->top == 1) {
obj = z;
} else {
+ ++num_vars;
ALLOC_INIT_ZVAL(obj);
}
@@ -626,6 +636,7 @@
if (jp->top > 1) {
attach_zval(jp, jp->top - 1, jp->top, &key, assoc TSRMLS_CC);
+ ++num_vars;
}
JSON_RESET_TYPE();
@@ -683,6 +694,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;
@@ -691,6 +703,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_3/ext/json/php_json.h
===================================================================
--- branches/PHP_5_3/ext/json/php_json.h (revision 321767)
+++ branches/PHP_5_3/ext/json/php_json.h (working copy)
@@ -48,7 +48,11 @@
#endif
PHP_JSON_API void php_json_encode(smart_str *buf, zval *val, int options TSRMLS_DC);
-PHP_JSON_API void php_json_decode(zval *return_value, char *str, int str_len, zend_bool assoc, long depth TSRMLS_DC);
+PHP_JSON_API void php_json_decode_ex(zval *return_value, char *str, int str_len, zend_bool assoc, long depth, long max_vars TSRMLS_DC);
+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, depth, 0 TSRMLS_CC);
+}
#define PHP_JSON_HEX_TAG (1<<0)
#define PHP_JSON_HEX_AMP (1<<1)
Index: branches/PHP_5_3/ext/json/JSON_parser.h
===================================================================
--- branches/PHP_5_3/ext/json/JSON_parser.h (revision 321767)
+++ branches/PHP_5_3/ext/json/JSON_parser.h (working copy)
@@ -14,6 +14,7 @@
int top;
int error_code;
int* stack;
+ long max_vars;
zval **the_zstack;
zval *the_static_zstack[JSON_PARSER_DEFAULT_DEPTH];
} * JSON_parser;
@@ -24,10 +25,11 @@
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(JSON_parser jp, zval *z, unsigned short utf16_json[], int length, int assoc TSRMLS_DC);
extern int free_JSON_parser(JSON_parser jp);
#endif
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) {
|