geissert Sat, 06 Mar 2010 18:54:55 +0000 Revision: http://svn.php.net/viewvc?view=revision&revision=295896
Log: Detect overflows before they occur in the filter extension (bug #51023) Thanks to Sean Finney for the patch Bug: http://bugs.php.net/51023 (Open) ext/filter/tests/046.phpt fails, does not detect int overflow (with -O2 gcc 4.4) Changed paths: U php/php-src/branches/PHP_5_2/ext/filter/logical_filters.c U php/php-src/branches/PHP_5_2/ext/filter/tests/046.phpt U php/php-src/branches/PHP_5_3/ext/filter/logical_filters.c U php/php-src/branches/PHP_5_3/ext/filter/tests/046.phpt U php/php-src/trunk/ext/filter/logical_filters.c U php/php-src/trunk/ext/filter/tests/046.phpt
Modified: php/php-src/branches/PHP_5_2/ext/filter/logical_filters.c =================================================================== --- php/php-src/branches/PHP_5_2/ext/filter/logical_filters.c 2010-03-06 16:55:43 UTC (rev 295895) +++ php/php-src/branches/PHP_5_2/ext/filter/logical_filters.c 2010-03-06 18:54:55 UTC (rev 295896) @@ -68,7 +68,7 @@ static int php_filter_parse_int(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */ long ctx_value; - int sign = 0; + int sign = 0, digit = 0; const char *end = str + str_len; switch (*str) { @@ -82,7 +82,7 @@ /* must start with 1..9*/ if (str < end && *str >= '1' && *str <= '9') { - ctx_value = ((*(str++)) - '0'); + ctx_value = ((sign)?-1:1) * ((*(str++)) - '0'); } else { return -1; } @@ -95,19 +95,18 @@ while (str < end) { if (*str >= '0' && *str <= '9') { - ctx_value = (ctx_value * 10) + (*(str++) - '0'); + digit = (*(str++) - '0'); + if ( (!sign) && ctx_value <= (LONG_MAX-digit)/10 ) { + ctx_value = (ctx_value * 10) + digit; + } else if ( sign && ctx_value >= (LONG_MIN+digit)/10) { + ctx_value = (ctx_value * 10) - digit; + } else { + return -1; + } } else { return -1; } } - if (sign) { - ctx_value = -ctx_value; - if (ctx_value > 0) { /* overflow */ - return -1; - } - } else if (ctx_value < 0) { /* overflow */ - return -1; - } *ret = ctx_value; return 1; Modified: php/php-src/branches/PHP_5_2/ext/filter/tests/046.phpt =================================================================== --- php/php-src/branches/PHP_5_2/ext/filter/tests/046.phpt 2010-03-06 16:55:43 UTC (rev 295895) +++ php/php-src/branches/PHP_5_2/ext/filter/tests/046.phpt 2010-03-06 18:54:55 UTC (rev 295896) @@ -4,16 +4,46 @@ <?php if (!extension_loaded("filter")) die("skip"); ?> --FILE-- <?php -$s = sprintf("%d", PHP_INT_MAX); -var_dump(is_long(filter_var($s, FILTER_VALIDATE_INT))); +$max = sprintf("%d", PHP_INT_MAX); +switch($max) { +case "2147483647": /* 32-bit systems */ + $min = "-2147483648"; + $overflow = "2147483648"; + $underflow = "-2147483649"; + break; +case "9223372036854775807": /* 64-bit systems */ + $min = "-9223372036854775808"; + $overflow = "9223372036854775808"; + $underflow = "-9223372036854775809"; + break; +default: + die("failed: unknown value for PHP_MAX_INT"); + break; +} -$s = sprintf("%.0f", PHP_INT_MAX+1); -var_dump(filter_var($s, FILTER_VALIDATE_INT)); +function test_validation($val, $msg) { + $f = filter_var($val, FILTER_VALIDATE_INT); + echo "$msg filtered: "; var_dump($f); // filtered value (or false) + echo "$msg is_long: "; var_dump(is_long($f)); // test validation + echo "$msg equal: "; var_dump($val == $f); // test equality of result +} -$s = sprintf("%d", -PHP_INT_MAX); -var_dump(is_long(filter_var($s, FILTER_VALIDATE_INT))); +// PHP_INT_MAX +test_validation($max, "max"); +test_validation($overflow, "overflow"); +test_validation($min, "min"); +test_validation($underflow, "underflow"); ?> ---EXPECT-- -bool(true) -bool(false) -bool(true) +--EXPECTF-- +max filtered: int(%d) +max is_long: bool(true) +max equal: bool(true) +overflow filtered: bool(false) +overflow is_long: bool(false) +overflow equal: bool(false) +min filtered: int(-%d) +min is_long: bool(true) +min equal: bool(true) +underflow filtered: bool(false) +underflow is_long: bool(false) +underflow equal: bool(false) Modified: php/php-src/branches/PHP_5_3/ext/filter/logical_filters.c =================================================================== --- php/php-src/branches/PHP_5_3/ext/filter/logical_filters.c 2010-03-06 16:55:43 UTC (rev 295895) +++ php/php-src/branches/PHP_5_3/ext/filter/logical_filters.c 2010-03-06 18:54:55 UTC (rev 295896) @@ -68,7 +68,7 @@ static int php_filter_parse_int(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */ long ctx_value; - int sign = 0; + int sign = 0, digit = 0; const char *end = str + str_len; switch (*str) { @@ -82,7 +82,7 @@ /* must start with 1..9*/ if (str < end && *str >= '1' && *str <= '9') { - ctx_value = ((*(str++)) - '0'); + ctx_value = ((sign)?-1:1) * ((*(str++)) - '0'); } else { return -1; } @@ -95,19 +95,18 @@ while (str < end) { if (*str >= '0' && *str <= '9') { - ctx_value = (ctx_value * 10) + (*(str++) - '0'); + digit = (*(str++) - '0'); + if ( (!sign) && ctx_value <= (LONG_MAX-digit)/10 ) { + ctx_value = (ctx_value * 10) + digit; + } else if ( sign && ctx_value >= (LONG_MIN+digit)/10) { + ctx_value = (ctx_value * 10) - digit; + } else { + return -1; + } } else { return -1; } } - if (sign) { - ctx_value = -ctx_value; - if (ctx_value > 0) { /* overflow */ - return -1; - } - } else if (ctx_value < 0) { /* overflow */ - return -1; - } *ret = ctx_value; return 1; Modified: php/php-src/branches/PHP_5_3/ext/filter/tests/046.phpt =================================================================== --- php/php-src/branches/PHP_5_3/ext/filter/tests/046.phpt 2010-03-06 16:55:43 UTC (rev 295895) +++ php/php-src/branches/PHP_5_3/ext/filter/tests/046.phpt 2010-03-06 18:54:55 UTC (rev 295896) @@ -4,16 +4,46 @@ <?php if (!extension_loaded("filter")) die("skip"); ?> --FILE-- <?php -$s = sprintf("%d", PHP_INT_MAX); -var_dump(is_long(filter_var($s, FILTER_VALIDATE_INT))); +$max = sprintf("%d", PHP_INT_MAX); +switch($max) { +case "2147483647": /* 32-bit systems */ + $min = "-2147483648"; + $overflow = "2147483648"; + $underflow = "-2147483649"; + break; +case "9223372036854775807": /* 64-bit systems */ + $min = "-9223372036854775808"; + $overflow = "9223372036854775808"; + $underflow = "-9223372036854775809"; + break; +default: + die("failed: unknown value for PHP_MAX_INT"); + break; +} -$s = sprintf("%.0f", PHP_INT_MAX+1); -var_dump(filter_var($s, FILTER_VALIDATE_INT)); +function test_validation($val, $msg) { + $f = filter_var($val, FILTER_VALIDATE_INT); + echo "$msg filtered: "; var_dump($f); // filtered value (or false) + echo "$msg is_long: "; var_dump(is_long($f)); // test validation + echo "$msg equal: "; var_dump($val == $f); // test equality of result +} -$s = sprintf("%d", -PHP_INT_MAX); -var_dump(is_long(filter_var($s, FILTER_VALIDATE_INT))); +// PHP_INT_MAX +test_validation($max, "max"); +test_validation($overflow, "overflow"); +test_validation($min, "min"); +test_validation($underflow, "underflow"); ?> ---EXPECT-- -bool(true) -bool(false) -bool(true) +--EXPECTF-- +max filtered: int(%d) +max is_long: bool(true) +max equal: bool(true) +overflow filtered: bool(false) +overflow is_long: bool(false) +overflow equal: bool(false) +min filtered: int(-%d) +min is_long: bool(true) +min equal: bool(true) +underflow filtered: bool(false) +underflow is_long: bool(false) +underflow equal: bool(false) Modified: php/php-src/trunk/ext/filter/logical_filters.c =================================================================== --- php/php-src/trunk/ext/filter/logical_filters.c 2010-03-06 16:55:43 UTC (rev 295895) +++ php/php-src/trunk/ext/filter/logical_filters.c 2010-03-06 18:54:55 UTC (rev 295896) @@ -68,7 +68,7 @@ static int php_filter_parse_int(const char *str, unsigned int str_len, long *ret TSRMLS_DC) { /* {{{ */ long ctx_value; - int sign = 0; + int sign = 0, digit = 0; const char *end = str + str_len; switch (*str) { @@ -82,7 +82,7 @@ /* must start with 1..9*/ if (str < end && *str >= '1' && *str <= '9') { - ctx_value = ((*(str++)) - '0'); + ctx_value = ((sign)?-1:1) * ((*(str++)) - '0'); } else { return -1; } @@ -95,19 +95,18 @@ while (str < end) { if (*str >= '0' && *str <= '9') { - ctx_value = (ctx_value * 10) + (*(str++) - '0'); + digit = (*(str++) - '0'); + if ( (!sign) && ctx_value <= (LONG_MAX-digit)/10 ) { + ctx_value = (ctx_value * 10) + digit; + } else if ( sign && ctx_value >= (LONG_MIN+digit)/10) { + ctx_value = (ctx_value * 10) - digit; + } else { + return -1; + } } else { return -1; } } - if (sign) { - ctx_value = -ctx_value; - if (ctx_value > 0) { /* overflow */ - return -1; - } - } else if (ctx_value < 0) { /* overflow */ - return -1; - } *ret = ctx_value; return 1; Modified: php/php-src/trunk/ext/filter/tests/046.phpt =================================================================== --- php/php-src/trunk/ext/filter/tests/046.phpt 2010-03-06 16:55:43 UTC (rev 295895) +++ php/php-src/trunk/ext/filter/tests/046.phpt 2010-03-06 18:54:55 UTC (rev 295896) @@ -4,16 +4,46 @@ <?php if (!extension_loaded("filter")) die("skip"); ?> --FILE-- <?php -$s = sprintf("%d", PHP_INT_MAX); -var_dump(is_long(filter_var($s, FILTER_VALIDATE_INT))); +$max = sprintf("%d", PHP_INT_MAX); +switch($max) { +case "2147483647": /* 32-bit systems */ + $min = "-2147483648"; + $overflow = "2147483648"; + $underflow = "-2147483649"; + break; +case "9223372036854775807": /* 64-bit systems */ + $min = "-9223372036854775808"; + $overflow = "9223372036854775808"; + $underflow = "-9223372036854775809"; + break; +default: + die("failed: unknown value for PHP_MAX_INT"); + break; +} -$s = sprintf("%.0f", PHP_INT_MAX+1); -var_dump(filter_var($s, FILTER_VALIDATE_INT)); +function test_validation($val, $msg) { + $f = filter_var($val, FILTER_VALIDATE_INT); + echo "$msg filtered: "; var_dump($f); // filtered value (or false) + echo "$msg is_long: "; var_dump(is_long($f)); // test validation + echo "$msg equal: "; var_dump($val == $f); // test equality of result +} -$s = sprintf("%d", -PHP_INT_MAX); -var_dump(is_long(filter_var($s, FILTER_VALIDATE_INT))); +// PHP_INT_MAX +test_validation($max, "max"); +test_validation($overflow, "overflow"); +test_validation($min, "min"); +test_validation($underflow, "underflow"); ?> ---EXPECT-- -bool(true) -bool(false) -bool(true) +--EXPECTF-- +max filtered: int(%d) +max is_long: bool(true) +max equal: bool(true) +overflow filtered: bool(false) +overflow is_long: bool(false) +overflow equal: bool(false) +min filtered: int(-%d) +min is_long: bool(true) +min equal: bool(true) +underflow filtered: bool(false) +underflow is_long: bool(false) +underflow equal: bool(false)
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php