Re: [PHP-CVS] com php-src: Fix zend_dval_to_lval outside 64bit integers range: Zend/tests/bug39018.phpt Zend/tests/dval_to_lval_32.phpt Zend/tests/dval_to_lval_64.phpt Zend/zend_operators.h

2013-02-23 Thread Laruence
please look at this: https://bugs.php.net/bug.php?id=64290

thanks

On Sun, Feb 24, 2013 at 12:23 AM, Gustavo André dos Santos Lopes
 wrote:
> Commit:77566edbafb969e166239b3fbc929588c6630ee9
> Author:Gustavo Lopes  Sun, 17 Feb 2013 
> 23:40:26 +0100
> Parents:   64a2a8a7536de781aac015e7392cb56308d8aed0
> Branches:  PHP-5.5 master
>
> Link:   
> http://git.php.net/?p=php-src.git;a=commitdiff;h=77566edbafb969e166239b3fbc929588c6630ee9
>
> Log:
> Fix zend_dval_to_lval outside 64bit integers range
>
> PHP should preserve the least significant bits when casting from double
> to long. Zend.m4 contains this:
>
> AC_DEFINE([ZEND_DVAL_TO_LVAL_CAST_OK], 1, [Define if double cast to long 
> preserves least significant bits])
>
> If ZEND_DVAL_TO_LVAL_CAST_OK is not defined, zend_operators.h had an
> inline implementation of zend_dval_to_lval() that would do a cast to an
> int64_t (when sizeof(long) == 4), then a cast to unsigned long and
> finally the cast to long.
>
> While this works well for doubles inside the range of values of the type
> used in the first cast (int64_t in the 32-bit version and unsigned long
> in the 64-bit version), if outside the range, it is undefined behavior
> that WILL give varying and not particularly useful results.
>
> This commit uses fmod() to first put the double in a range that can
> safely be cast to unsigned long and then casts this unsigned long to
> long. This last cast is implementation defined, but it's very likely
> that this gives the expected result (i.e. the internal 2's complement
> representation is unchanged) on all platforms that PHP supports. In any
> case, the previous implementationa already had this assumption.
>
> This alternative code path is indeed significantly slower than simply
> casting the double (almost an order of magnitude), but that should not
> matter because casting doubles with a very high absolute value is a
> rare event.
>
> Changed paths:
>   M  Zend/tests/bug39018.phpt
>   A  Zend/tests/dval_to_lval_32.phpt
>   A  Zend/tests/dval_to_lval_64.phpt
>   M  Zend/zend_operators.h
>
>
> Diff:
> diff --git a/Zend/tests/bug39018.phpt b/Zend/tests/bug39018.phpt
> index 32566ba..a00e1fb 100644
> --- a/Zend/tests/bug39018.phpt
> +++ b/Zend/tests/bug39018.phpt
> @@ -64,6 +64,8 @@ print "\nDone\n";
>  --EXPECTF--
>  Notice: String offset cast occurred in %s on line %d
>
> +Notice: Uninitialized string offset: %s in %s on line 6
> +
>  Notice: Uninitialized string offset: 0 in %s on line %d
>
>  Notice: Uninitialized string offset: 0 in %s on line %d
> diff --git a/Zend/tests/dval_to_lval_32.phpt b/Zend/tests/dval_to_lval_32.phpt
> new file mode 100644
> index 000..ddb16cc
> --- /dev/null
> +++ b/Zend/tests/dval_to_lval_32.phpt
> @@ -0,0 +1,29 @@
> +--TEST--
> +zend_dval_to_lval preserves low bits  (32 bit long)
> +--SKIPIF--
> + +if (PHP_INT_SIZE != 4)
> +die("skip for machines with 32-bit longs");
> +?>
> +--FILE--
> + +   /* test doubles around -4e21 */
> +   $values = [
> +   -4001048576.,
> +   -4000524288.,
> +   -40.,
> +   -3999475712.,
> +   -3998951424.,
> +   ];
> +
> +   foreach ($values as $v) {
> +   var_dump((int)$v);
> +   }
> +
> +?>
> +--EXPECT--
> +int(-2056257536)
> +int(-2055733248)
> +int(-2055208960)
> +int(-2054684672)
> +int(-2054160384)
> diff --git a/Zend/tests/dval_to_lval_64.phpt b/Zend/tests/dval_to_lval_64.phpt
> new file mode 100644
> index 000..da7f56d
> --- /dev/null
> +++ b/Zend/tests/dval_to_lval_64.phpt
> @@ -0,0 +1,29 @@
> +--TEST--
> +zend_dval_to_lval preserves low bits  (64 bit long)
> +--SKIPIF--
> + +if (PHP_INT_SIZE != 8)
> +die("skip for machines with 64-bit longs");
> +?>
> +--FILE--
> + +   /* test doubles around -4e21 */
> +   $values = [
> +   -4001048576.,
> +   -4000524288.,
> +   -40.,
> +   -3999475712.,
> +   -3998951424.,
> +   ];
> +
> +   foreach ($values as $v) {
> +   var_dump((int)$v);
> +   }
> +
> +?>
> +--EXPECT--
> +int(2943463994971652096)
> +int(2943463994972176384)
> +int(2943463994972700672)
> +int(2943463994973224960)
> +int(2943463994973749248)
> diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h
> index 93c60e4..a3a432f 100644
> --- a/Zend/zend_operators.h
> +++ b/Zend/zend_operators.h
> @@ -68,22 +68,36 @@ END_EXTERN_C()
>
>  #if ZEND_DVAL_TO_LVAL_CAST_OK
>  # define zend_dval_to_lval(d) ((long) (d))
> -#elif SIZEOF_LONG == 4 && defined(HAVE_ZEND_LONG64)
> +#elif SIZEOF_LONG == 4
>  static zend_always_inline long zend_dval_to_lval(double d)
>  {
> if (d > LONG_MAX || d < LONG_MIN) {
> -   return (long)(unsigned long)(zend_long64) d;
> +   double  two_pow_32 = pow(2., 32.),
> +   

[PHP-CVS] 回复: [PHP-CVS] com php-src: Fix zend_dval_to_lval outside 64bit integers range: Zend/tests/bug39018.phpt Zend/tests/dval_to_lval_32.phpt Zend/tests/dval_to_lval_64.phpt Zend/zend_operators.h

2013-02-23 Thread Reeze Xia
Hi Gustavo,
I got two tests failed on Mac OS X:

➜  php-src-master git:(master) ✗ cat  Zend/tests/dval_to_lval_64.diff
001+ int(-9223372036854775808)
002+ int(-9223372036854775808)
003+ int(-9223372036854775808)
004+ int(-9223372036854775808)
005+ int(-9223372036854775808)
001- int(2943463994971652096)
002- int(2943463994972176384)
003- int(2943463994972700672)
004- int(2943463994973224960)
005- int(2943463994973749248)


➜  php-src-master git:(master) ✗ cat Zend/tests/bug39018.diff
003- Notice: Uninitialized string offset: %s in %s on line 6
004-  


$ uname -a
Darwin MacBookPro.local 12.2.0 Darwin Kernel Version 12.2.0: RELEASE_X86_64 
x86_64


--  
reeze | reeze.cn

已使用 Sparrow (http://www.sparrowmailapp.com/?sig)  

在 2013年2月24日星期日,上午12:23,Gustavo André dos Santos Lopes 写道:

> Commit: 77566edbafb969e166239b3fbc929588c6630ee9
> Author: Gustavo Lopes  (mailto:glo...@nebm.ist.utl.pt)> Sun, 17 Feb 2013 23:40:26 +0100
> Parents: 64a2a8a7536de781aac015e7392cb56308d8aed0
> Branches: PHP-5.5 master
>  
> Link: 
> http://git.php.net/?p=php-src.git;a=commitdiff;h=77566edbafb969e166239b3fbc929588c6630ee9
>  
> Log:
> Fix zend_dval_to_lval outside 64bit integers range
>  
> PHP should preserve the least significant bits when casting from double
> to long. Zend.m4 contains this:
>  
> AC_DEFINE([ZEND_DVAL_TO_LVAL_CAST_OK], 1, [Define if double cast to long 
> preserves least significant bits])
>  
> If ZEND_DVAL_TO_LVAL_CAST_OK is not defined, zend_operators.h had an
> inline implementation of zend_dval_to_lval() that would do a cast to an
> int64_t (when sizeof(long) == 4), then a cast to unsigned long and
> finally the cast to long.
>  
> While this works well for doubles inside the range of values of the type
> used in the first cast (int64_t in the 32-bit version and unsigned long
> in the 64-bit version), if outside the range, it is undefined behavior
> that WILL give varying and not particularly useful results.
>  
> This commit uses fmod() to first put the double in a range that can
> safely be cast to unsigned long and then casts this unsigned long to
> long. This last cast is implementation defined, but it's very likely
> that this gives the expected result (i.e. the internal 2's complement
> representation is unchanged) on all platforms that PHP supports. In any
> case, the previous implementationa already had this assumption.
>  
> This alternative code path is indeed significantly slower than simply
> casting the double (almost an order of magnitude), but that should not
> matter because casting doubles with a very high absolute value is a
> rare event.
>  
> Changed paths:
> M Zend/tests/bug39018.phpt
> A Zend/tests/dval_to_lval_32.phpt
> A Zend/tests/dval_to_lval_64.phpt
> M Zend/zend_operators.h
>  
>  
> Diff:
> diff --git a/Zend/tests/bug39018.phpt b/Zend/tests/bug39018.phpt
> index 32566ba..a00e1fb 100644
> --- a/Zend/tests/bug39018.phpt
> +++ b/Zend/tests/bug39018.phpt
> @@ -64,6 +64,8 @@ print "\nDone\n";
> --EXPECTF--
> Notice: String offset cast occurred in %s on line %d
>  
> +Notice: Uninitialized string offset: %s in %s on line 6
> +
> Notice: Uninitialized string offset: 0 in %s on line %d
>  
> Notice: Uninitialized string offset: 0 in %s on line %d
> diff --git a/Zend/tests/dval_to_lval_32.phpt b/Zend/tests/dval_to_lval_32.phpt
> new file mode 100644
> index 000..ddb16cc
> --- /dev/null
> +++ b/Zend/tests/dval_to_lval_32.phpt
> @@ -0,0 +1,29 @@
> +--TEST--
> +zend_dval_to_lval preserves low bits (32 bit long)
> +--SKIPIF--
> + +if (PHP_INT_SIZE != 4)
> + die("skip for machines with 32-bit longs");
> +?>
> +--FILE--
> + + /* test doubles around -4e21 */
> + $values = [
> + -4001048576.,
> + -4000524288.,
> + -40.,
> + -3999475712.,
> + -3998951424.,
> + ];
> +
> + foreach ($values as $v) {
> + var_dump((int)$v);
> + }
> +
> +?>
> +--EXPECT--
> +int(-2056257536)
> +int(-2055733248)
> +int(-2055208960)
> +int(-2054684672)
> +int(-2054160384)
> diff --git a/Zend/tests/dval_to_lval_64.phpt b/Zend/tests/dval_to_lval_64.phpt
> new file mode 100644
> index 000..da7f56d
> --- /dev/null
> +++ b/Zend/tests/dval_to_lval_64.phpt
> @@ -0,0 +1,29 @@
> +--TEST--
> +zend_dval_to_lval preserves low bits (64 bit long)
> +--SKIPIF--
> + +if (PHP_INT_SIZE != 8)
> + die("skip for machines with 64-bit longs");
> +?>
> +--FILE--
> + + /* test doubles around -4e21 */
> + $values = [
> + -4001048576.,
> + -4000524288.,
> + -40.,
> + -3999475712.,
> + -3998951424.,
> + ];
> +
> + foreach ($values as $v) {
> + var_dump((int)$v);
> + }
> +
> +?>
> +--EXPECT--
> +int(2943463994971652096)
> +int(2943463994972176384)
> +int(2943463994972700672)
> +int(2943463994973224960)
> +int(2943463994973749248)
> diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h
> index 93c60e4..a3a432f 100644
> --- a/Zend/zend_operators.h
> +++ b/Zend/zend_operators.h
> @@ -68,22 +68,36 @

[PHP-CVS] com php-src: Fix zend_dval_to_lval outside 64bit integers range: Zend/tests/bug39018.phpt Zend/tests/dval_to_lval_32.phpt Zend/tests/dval_to_lval_64.phpt Zend/zend_operators.h

2013-02-23 Thread Gustavo André dos Santos Lopes
Commit:77566edbafb969e166239b3fbc929588c6630ee9
Author:Gustavo Lopes  Sun, 17 Feb 2013 
23:40:26 +0100
Parents:   64a2a8a7536de781aac015e7392cb56308d8aed0
Branches:  PHP-5.5 master

Link:   
http://git.php.net/?p=php-src.git;a=commitdiff;h=77566edbafb969e166239b3fbc929588c6630ee9

Log:
Fix zend_dval_to_lval outside 64bit integers range

PHP should preserve the least significant bits when casting from double
to long. Zend.m4 contains this:

AC_DEFINE([ZEND_DVAL_TO_LVAL_CAST_OK], 1, [Define if double cast to long 
preserves least significant bits])

If ZEND_DVAL_TO_LVAL_CAST_OK is not defined, zend_operators.h had an
inline implementation of zend_dval_to_lval() that would do a cast to an
int64_t (when sizeof(long) == 4), then a cast to unsigned long and
finally the cast to long.

While this works well for doubles inside the range of values of the type
used in the first cast (int64_t in the 32-bit version and unsigned long
in the 64-bit version), if outside the range, it is undefined behavior
that WILL give varying and not particularly useful results.

This commit uses fmod() to first put the double in a range that can
safely be cast to unsigned long and then casts this unsigned long to
long. This last cast is implementation defined, but it's very likely
that this gives the expected result (i.e. the internal 2's complement
representation is unchanged) on all platforms that PHP supports. In any
case, the previous implementationa already had this assumption.

This alternative code path is indeed significantly slower than simply
casting the double (almost an order of magnitude), but that should not
matter because casting doubles with a very high absolute value is a
rare event.

Changed paths:
  M  Zend/tests/bug39018.phpt
  A  Zend/tests/dval_to_lval_32.phpt
  A  Zend/tests/dval_to_lval_64.phpt
  M  Zend/zend_operators.h


Diff:
diff --git a/Zend/tests/bug39018.phpt b/Zend/tests/bug39018.phpt
index 32566ba..a00e1fb 100644
--- a/Zend/tests/bug39018.phpt
+++ b/Zend/tests/bug39018.phpt
@@ -64,6 +64,8 @@ print "\nDone\n";
 --EXPECTF--
 Notice: String offset cast occurred in %s on line %d
 
+Notice: Uninitialized string offset: %s in %s on line 6
+
 Notice: Uninitialized string offset: 0 in %s on line %d
 
 Notice: Uninitialized string offset: 0 in %s on line %d
diff --git a/Zend/tests/dval_to_lval_32.phpt b/Zend/tests/dval_to_lval_32.phpt
new file mode 100644
index 000..ddb16cc
--- /dev/null
+++ b/Zend/tests/dval_to_lval_32.phpt
@@ -0,0 +1,29 @@
+--TEST--
+zend_dval_to_lval preserves low bits  (32 bit long)
+--SKIPIF--
+
+--FILE--
+
+--EXPECT--
+int(-2056257536)
+int(-2055733248)
+int(-2055208960)
+int(-2054684672)
+int(-2054160384)
diff --git a/Zend/tests/dval_to_lval_64.phpt b/Zend/tests/dval_to_lval_64.phpt
new file mode 100644
index 000..da7f56d
--- /dev/null
+++ b/Zend/tests/dval_to_lval_64.phpt
@@ -0,0 +1,29 @@
+--TEST--
+zend_dval_to_lval preserves low bits  (64 bit long)
+--SKIPIF--
+
+--FILE--
+
+--EXPECT--
+int(2943463994971652096)
+int(2943463994972176384)
+int(2943463994972700672)
+int(2943463994973224960)
+int(2943463994973749248)
diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h
index 93c60e4..a3a432f 100644
--- a/Zend/zend_operators.h
+++ b/Zend/zend_operators.h
@@ -68,22 +68,36 @@ END_EXTERN_C()
 
 #if ZEND_DVAL_TO_LVAL_CAST_OK
 # define zend_dval_to_lval(d) ((long) (d))
-#elif SIZEOF_LONG == 4 && defined(HAVE_ZEND_LONG64)
+#elif SIZEOF_LONG == 4
 static zend_always_inline long zend_dval_to_lval(double d)
 {
if (d > LONG_MAX || d < LONG_MIN) {
-   return (long)(unsigned long)(zend_long64) d;
+   double  two_pow_32 = pow(2., 32.),
+   dmod;
+
+   dmod = fmod(d, two_pow_32);
+   if (dmod < 0) {
+   dmod += two_pow_32;
+   }
+   return (long)(unsigned long)dmod;
}
-   return (long) d;
+   return (long)d;
 }
 #else
 static zend_always_inline long zend_dval_to_lval(double d)
 {
/* >= as (double)LONG_MAX is outside signed range */
-   if (d >= LONG_MAX) {
-   return (long)(unsigned long) d;
+   if (d >= LONG_MAX || d < LONG_MIN) {
+   double  two_pow_64 = pow(2., 64.),
+   dmod;
+
+   dmod = fmod(d, two_pow_64);
+   if (dmod < 0) {
+   dmod += two_pow_64;
+   }
+   return (long)(unsigned long)dmod;
}
-   return (long) d;
+   return (long)d;
 }
 #endif
 /* }}} */


--
PHP CVS Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php