]> granicus.if.org Git - php/commitdiff
Detect overflows before they occur in the filter extension (bug #51023)
authorRaphael Geissert <geissert@php.net>
Sat, 6 Mar 2010 18:54:55 +0000 (18:54 +0000)
committerRaphael Geissert <geissert@php.net>
Sat, 6 Mar 2010 18:54:55 +0000 (18:54 +0000)
Thanks to Sean Finney for the patch

ext/filter/logical_filters.c
ext/filter/tests/046.phpt

index 61f5bc0b4ec547a5d59d368b7dafa491193f755f..8b4f58b51af459777ac460ebc8093c5b3da3ac76 100644 (file)
@@ -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 @@ static int php_filter_parse_int(const char *str, unsigned int str_len, long *ret
 
        /* 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 @@ static int php_filter_parse_int(const char *str, unsigned int str_len, long *ret
 
        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;
index 8133289461b82eaff7e4970d0c7ef5b6dad2c9e0..bc454420ad6146963fd617a3cbc08d20ae6bf184 100755 (executable)
@@ -4,16 +4,46 @@ Integer overflow
 <?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)