]> granicus.if.org Git - php/commitdiff
fix #41118 (PHP does not handle overflow of octal integers)
authorAntony Dovgal <tony2001@php.net>
Thu, 19 Apr 2007 11:24:48 +0000 (11:24 +0000)
committerAntony Dovgal <tony2001@php.net>
Thu, 19 Apr 2007 11:24:48 +0000 (11:24 +0000)
MFB a small part of optimization patch applied only to 5_2

Zend/tests/hex_overflow_32bit.phpt [new file with mode: 0644]
Zend/tests/oct_overflow_32bit.phpt [new file with mode: 0644]
Zend/zend_language_scanner.l
Zend/zend_operators.h
Zend/zend_strtod.c
Zend/zend_strtod.h

diff --git a/Zend/tests/hex_overflow_32bit.phpt b/Zend/tests/hex_overflow_32bit.phpt
new file mode 100644 (file)
index 0000000..34306cc
--- /dev/null
@@ -0,0 +1,27 @@
+--TEST--
+testing integer overflow (32bit)
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platform only"); ?>
+--FILE--
+<?php
+
+$doubles = array(
+       0x1736123FFFAAA,
+       0XFFFFFFFFFFFFFFFFFF,
+       0xAAAAAAAAAAAAAAEEEEEEEEEBBB,
+       0x66666666666666666777777,
+       );
+
+foreach ($doubles as $d) {
+       $l = $d;
+       var_dump($l);
+}
+
+echo "Done\n";
+?>
+--EXPECTF--    
+float(4083360297110%d)
+float(4.7223664828%dE+21)
+float(1.3521606402%dE+31)
+float(1.9807040628%dE+27)
+Done
diff --git a/Zend/tests/oct_overflow_32bit.phpt b/Zend/tests/oct_overflow_32bit.phpt
new file mode 100644 (file)
index 0000000..77befba
--- /dev/null
@@ -0,0 +1,29 @@
+--TEST--
+testing integer overflow (32bit)
+--SKIPIF--
+<?php if (PHP_INT_SIZE != 4) die("skip this test is for 32bit platform only"); ?>
+--FILE--
+<?php
+
+$doubles = array(
+       076545676543223,
+       032325463734,
+       077777797777777,
+       07777777777777977777777777,
+       03333333333333382222222222222,
+       );
+
+foreach ($doubles as $d) {
+       $l = (double)$d;
+       var_dump($l);
+}
+
+echo "Done\n";
+?>
+--EXPECTF--    
+float(4308640384%d)
+float(3545655%d)
+float(262143)
+float(549755813%d)
+float(1884877076%d)
+Done
index 0fb4b65c896624d4370a6fe69057a3ef46097943..b992324dbbeefe13d5c280a5a09e6abd4b86c2bf 100644 (file)
@@ -1865,36 +1865,45 @@ NEWLINE ("\r"|"\n"|"\r\n")
 
 
 <ST_IN_SCRIPTING>{LNUM} {
-       errno = 0;
-       Z_LVAL_P(zendlval) = strtol(yytext, NULL, 0);
-       if (errno == ERANGE) { /* overflow */
-               Z_DVAL_P(zendlval) = zend_strtod(yytext, NULL);
-               Z_TYPE_P(zendlval) = IS_DOUBLE;
-               return T_DNUMBER;
-       } else {
-               Z_TYPE_P(zendlval) = IS_LONG;
-               return T_LNUMBER;
-       }
+    if (yyleng < MAX_LENGTH_OF_LONG - 1) { /* Won't overflow */
+        zendlval->value.lval = strtol(yytext, NULL, 0);
+    } else {
+        errno = 0;
+        zendlval->value.lval = strtol(yytext, NULL, 0);
+        if (errno == ERANGE) { /* Overflow */
+            if (yytext[0] == '0') { /* octal overflow */
+                zendlval->value.dval = zend_oct_strtod(yytext, NULL);
+            } else {
+                zendlval->value.dval = zend_strtod(yytext, NULL);
+            }
+            zendlval->type = IS_DOUBLE;
+            return T_DNUMBER;
+        }
+    }
+
+    zendlval->type = IS_LONG;
+    return T_LNUMBER;
 }
 
 <ST_IN_SCRIPTING>{HNUM} {
-       errno = 0;
-       Z_LVAL_P(zendlval) = strtoul(yytext, NULL, 16);
-       if (errno == ERANGE) { /* overflow */
-               /* not trying strtod - it returns trash on 0x-es */
-               Z_LVAL_P(zendlval) = LONG_MAX; /* maximal long */
-               zend_error(E_NOTICE,"Hex number is too big: %s", yytext);
-       } else {
-               if (Z_LVAL_P(zendlval) < 0) {
-                       /* maintain consistency with the old way */
-                       Z_DVAL_P(zendlval) = (unsigned long) Z_LVAL_P(zendlval);
-                       Z_TYPE_P(zendlval) = IS_DOUBLE;
-                       return T_DNUMBER;
-               }
-               Z_TYPE_P(zendlval) = IS_LONG;
-       }
-       Z_TYPE_P(zendlval) = IS_LONG;
-       return T_LNUMBER;
+    char *hex = yytext + 2; /* Skip "0x" */
+    int len = yyleng - 2;
+
+    /* Skip any leading 0s */
+    while (*hex == '0') {
+        hex++;
+        len--;
+    }
+
+    if (len < SIZEOF_LONG * 2 || (len == SIZEOF_LONG * 2 && *hex <= '7')) {
+        zendlval->value.lval = strtol(hex, NULL, 16);
+        zendlval->type = IS_LONG;
+        return T_LNUMBER;
+    } else {
+        zendlval->value.dval = zend_hex_strtod(hex, NULL);
+        zendlval->type = IS_DOUBLE;
+        return T_DNUMBER;
+    }
 }
 
 <ST_DOUBLE_QUOTES,ST_BACKQUOTE,ST_HEREDOC>{LNUM}|{HNUM} { /* treat numbers (almost) as strings inside encapsulated strings */
index 90025f57a38fe01a669a58f17e95c647a3f9d964..44c4b6a8506214ec5124657af6bf49e9ad440d34 100644 (file)
 #include "ext/bcmath/libbcmath/src/bcmath.h"
 #endif
 
+#if SIZEOF_LONG == 4
+#define MAX_LENGTH_OF_LONG 11
+static const char long_min_digits[] = "2147483648";
+#elif SIZEOF_LONG == 8
 #define MAX_LENGTH_OF_LONG 20
+static const char long_min_digits[] = "9223372036854775808";
+#else
+#error "Unknown SIZEOF_LONG"
+#endif
+
 #define MAX_LENGTH_OF_DOUBLE 32
 
 BEGIN_EXTERN_C()
index 23dd743ef9229b943e94c6bb2f8fe6f776f2d173..227d9667ebb6bf8b9eed11410b97efa41ba6c935 100644 (file)
@@ -2671,6 +2671,69 @@ ZEND_API double zend_u_strtod(const UChar *nptr, UChar **endptr) /* {{{ */
 }
 /* }}} */
 
+ZEND_API double zend_hex_strtod(const char *str, char **endptr) /* {{{ */
+{
+    const char *s = str;
+    char c;
+    int any = 0;
+    double value = 0;
+
+    if (*s == '0' && (s[1] == 'x' || s[1] == 'X')) {
+        s += 2;
+    }
+
+    while ((c = *s++)) {
+        if (c >= '0' && c <= '9') {
+            c -= '0';
+        } else if (c >= 'A' && c <= 'F') {
+            c -= 'A' - 10;
+        } else if (c >= 'a' && c <= 'f') {
+            c -= 'a' - 10;
+        } else {
+            break;
+        }
+
+        any = 1;
+        value = value * 16 + c;
+    }
+
+    if (endptr != NULL) {
+        *endptr = (char *)(any ? s - 1 : str);
+    }
+
+    return value;
+}
+/* }}} */
+
+ZEND_API double zend_oct_strtod(const char *str, char **endptr) /* {{{ */
+{
+    const char *s = str;
+    char c;
+    double value = 0;
+    int any = 0;
+
+    /* skip leading zero */
+    s++;
+
+    while ((c = *s++)) {
+        if (c > '7') {
+            /* break and return the current value if the number is not well-formed
+             * that's what Linux strtol() does
+             */
+            break;
+        }
+        value = value * 8 + c - '0';
+        any = 1;
+    }
+
+    if (endptr != NULL) {
+        *endptr = (char *)(any ? s - 1 : str);
+    }
+
+    return value;
+}
+/* }}} */
+
 /*
  * Local variables:
  * tab-width: 4
index 7837a6311cd675219aca3d6a5ba23330fcb19530..329c19f68ac59154cd48a0d18c9ec166537b931d 100644 (file)
@@ -29,6 +29,8 @@ ZEND_API void zend_freedtoa(char *s);
 ZEND_API char * zend_dtoa(double _d, int mode, int ndigits, int *decpt, int *sign, char **rve);
 ZEND_API double zend_strtod(const char *s00, char **se);
 ZEND_API double zend_u_strtod(const UChar *nptr, UChar **endptr);
+ZEND_API double zend_hex_strtod(const char *str, char **endptr);
+ZEND_API double zend_oct_strtod(const char *str, char **endptr);
 ZEND_API int zend_startup_strtod(void);
 ZEND_API int zend_shutdown_strtod(void);
 END_EXTERN_C()