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

Reply via email to