]> granicus.if.org Git - php/commit
Fix zend_dval_to_lval outside 64bit integers range
authorGustavo Lopes <glopes@nebm.ist.utl.pt>
Sun, 17 Feb 2013 22:40:26 +0000 (23:40 +0100)
committerGustavo Lopes <glopes@nebm.ist.utl.pt>
Sat, 23 Feb 2013 16:23:49 +0000 (17:23 +0100)
commit77566edbafb969e166239b3fbc929588c6630ee9
tree9ed1cca5a8b711c312c7395caa41f9eb2b0cca72
parent64a2a8a7536de781aac015e7392cb56308d8aed0
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.
Zend/tests/bug39018.phpt
Zend/tests/dval_to_lval_32.phpt [new file with mode: 0644]
Zend/tests/dval_to_lval_64.phpt [new file with mode: 0644]
Zend/zend_operators.h