PHP Hangs On Numeric Value 2.2250738585072011e-308

I stumbled upon a very strange bug in PHP; this statement sends it into an infinite loop:

<?php $d = 2.2250738585072011e-308; ?>

(The same thing happens if you write the number without scientific notation — 324 decimal places.)

I hit this bug in the two places I tested for it: on Windows (PHP 5.3.1 under XAMPP 1.7.3), and on Linux (PHP Version 5.3.2-1ubuntu4.5) — both on an Intel Core Duo processor. I’ve written a bug report.

What’s Going On?

As a string, 2.2250738585072011e-308 causes no problems; it’s when it’s treated as a numeric value that the bug hits.

This works:

<?php $d = '2.2250738585072011e-308'; echo $d; ?>

but this doesn’t:

<?php $d = '2.2250738585072011e-308'; echo $d + 0; ?>

I don’t have a debug build of PHP available, but I did poke around in the source code (version 5.2.8). I found zend_strtod.c, which appears to be based on a very old version of David Gay’s strtod() function. I don’t know if zend_strtod() is even called in this case, but it was simple enough for me to isolate it and run it outside of PHP — under Visual C++ in fact. It did not hang on that input. (Update: zend_strtod() is called, and I can hang zend_strtod() in Visual C++ if I compile with all optimizations.)

What’s Special About 2.2250738585072011e-308?

2.2250738585072011e-308 represents the largest subnormal double-precision floating-point number; written as a hexadecimal floating-point constant, it’s 0x0.fffffffffffffp-1022. 2.2250738585072011e-308 is one of five 17-digit decimal values that convert (correctly) to 0x0.fffffffffffffp-1022:

  • 2.2250738585072007e-308
  • 2.2250738585072008e-308
  • 2.2250738585072009e-308
  • 2.2250738585072010e-308
  • 2.2250738585072011e-308

Only 2.2250738585072011e-308 causes the problem. It happens to be the largest of the five decimal values, so I guess that matters somehow.

If you have any thoughts on what the bug is, please let me (or PHP) know.

Is This Serious?

I’ve gotten no response to my bug report yet, but I can’t help but wonder: is this serious? Could you bring down a Web server by entering this constant in a PHP-based form?

Update

Yes, it’s serious.

1/4/2011: There is a lot of discussion about this, in addition to the comments below; for example, see

And as pointed out in the comments, equivalent forms of the number also cause the problem:

  • Variations on the placement of the decimal point: 0.22250738585072011e-307, 22.250738585072011e-309, 22250738585072011e-324, etc.
  • Leading zeros: e.g., 02.2250738585072011e-308.
  • Trailing zeros: e.g., 2.22507385850720110e-308.
  • Leading zeros in the exponent: e.g., 2.2250738585072011e-0308.
  • (Combinations of the above)

You can also alter the full 324 decimal place version of the number with leading and trailing zeros.

1/22/2011: I thought of another set of numbers that cause the same problem — numbers of 18 digits or more but with the same 17 digit prefix. For example:

  • 2.22507385850720111e-308
  • 2.225073858507201123e-308
  • 2.22507385850720113099e-308
  • 2.225073858507201100001e-308
  • 2.2250738585072011123456789012345678901234567890123456789e-308
  • etc.

In decimal these numbers are distinct, but they all map to the same floating-point number.

It seems the only ones that cause a problem though are those whose 18th digit is 0, 1, 2, or 3 (2.22507385850720114e-308 doesn’t cause the problem, for example).

Update: The Bug Has Been Fixed

The bug fix was released on 1/6/2011; this is from the PHP news release:

This release resolves a critical issue … where conversions from string to double might cause the PHP interpreter to hang on systems using x87 FPU registers.

The problem is known to only affect x86 32-bit PHP processes, regardless of whether the system hosting PHP is 32-bit or 64-bit.

Update: An Analysis of the Bug and Its Fix

I’ve found the specific cause of the bug, and why the fix fixes it; please see my article “Why “Volatile” Fixes the 2.2250738585072011e-308 Bug.”

Update: The Real Problem Is the Old Copy of dtoa.c

This bug could have been avoided altogether if zend_strtod() was kept in sync with David Gay’s strtod(); see my article “A Better Fix for the PHP 2.2250738585072011e-308 Bug.”

Dingbat
RSS feed icon
RSS e-mail icon

102 comments

  1. This doesn’t effect PHP 4, at least not the build I am running, it’s a bit old on this machine.

    PHP 4.4.9 (cli) (built: Sep 17 2008 16:31:15)

  2. Bug appearing at my Core 2 Duo / Win7 / PHP 5.3.0.

    This is *really* serious. In fact, I’ve just tested if the problem happens for GET passed values and it does. Not all the passed data to a website is treated as a number, so not all websites with the PHP versions and configuration that could fail with this bug will be vulnerable, but definitely there is going to be a huge amount of websites that will do. This is really scaring.

    I hope the PHP team patch it soon.

    Meanwhile, a possible workaround would be adding this line at the very top of the execution of php website:

    if (strpos(str_replace(‘.’, ”, serialize($GLOBALS)), ‘22250738585072011’)!==false) die();

    This will stop execution if any decimal version of the number were passed as parameter. Note that 222.50738585072011e-310 cause problems too, and any of the other possibilities to write it.

    Do you know if there are any other possible ways to write the number that causes trouble too?

  3. PHP 5.3.3-1ubuntu9.1 with Suhosin-Patch appears to have the problem, on my Ubuntu 10.10 machine. Interesting!

  4. Many people are reporting different PHP versions and saying whether the problem manifests there or not.

    The relevant variable is actually the CPU architecture.

    On 32-bit systems (i386), it freezes (PHP 5.2.x and 5.3.x). On 64-bit systems (i686, amd64), it does not.

  5. Confirmed the 32 bit issue:
    PHP 5.3.2-1ubuntu4.5 hangs in a 32 bit system.
    PHP 5.3.2-1ubuntu4.5 does not hang in a 64 bit system.

  6. Confirmed to affect i386 (32-bit) FreeBSD 6.x, 7.x, and 8.x with any PHP 5.x.x version, built from ports. CGI vs. CLI doesn’t matter.

    Issue does not affect 64-bit FreeBSD (6.x, 7.x, or 8.x) using PHP 5.x.x.

    You can attempt to reproduce the issue via CLI by doing:

    php -r ‘$d = 2.2250738585072011e-308;’

    I should note that the infinite loop does result in PHP wedging to the point of it taking 100% CPU time. I did not have the time nor desire to run it under gdb and debug this utter nonsense.

  7. On 64 bit archlinux (core 2 duo), I confirm that the following version is not affected:
    PHP 5.3.4 with Suhosin-Patch (cli) (built: Dec 16 2010 21:08:08)

  8. For those on 64-bit machines, won’t the equivalent (much larger) 64 bit value likely result in the same bug?

  9. @RoundSparrow: This is a 64-bit double. I don’t believe 64-bit architectures have 128-bit doubles, if they do then the value you may wish to try will be around 1.189731495357231765085759326628007 e-4932.

  10. > On 32-bit systems (i386), it freezes (PHP 5.2.x and 5.3.x).

    Despite this being a 32-bit (Atom N270) it doesn’t hang on the said problem.

    $ php -r “var_dump(2.2250738585072011e-308);”
    float(2.22507385851E-308)
    $ php -r “\$d = ‘2.2250738585072011e-308’; echo \$d + 0;”
    2.22507385851E-308
    php -r “var_dump((float) ‘2.2250738585072011e-308’);”
    float(2.22507385851E-308)

    $ php -v
    PHP 5.2.10-2ubuntu6.3 with Suhosin-Patch 0.9.7 (cli) (built: Nov 26 2009 14:42:49)
    Copyright (c) 1997-2009 The PHP Group
    Zend Engine v2.2.0, Copyright (c) 1998-2009 Zend Technologies
    $ uname -a
    Linux mobile 2.6.31-17-generic #55~ppa7~loms~karmic-Ubuntu SMP Thu Jan 21 13:37:20 UTC 2010 i686 GNU/Linux

    Any ideas?

  11. http://news.ycombinator.com/item?id=2066352

    > Follow-up: PROBLEM SOLVED.
    > This problem occurs due to IA-32’s 80-bit floating point arithmetic. The simple fix: add a “-ffloat-store” flag to your CFLAGS.
    > The problematic function, zend_strtod, seems to parse the mantissa (2.225…011 part) and the exponent (-308 part) separately, calculate the approximation of m*10^e and successively improve that approximation until the error becomes less than 0.5ulp. The problem is that this particular number causes the infinite loop (i.e. the iteration does not improve the error at all) in 80-bit FP, but does not in 64-bit FP. Since x86-64 in general uses the SSE2 instruction set (with 64-bit FP) instead of the deprecated x87 it does not have this problem.

  12. I got:
    PHP 5.3.3-6 with Suhosin-Patch (cli) (built: Dec 7 2010 18:23:49)
    Copyright (c) 1997-2009 The PHP Group
    Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies
    with Suhosin v0.9.32.1, Copyright (c) 2007-2010, by SektionEins GmbH
    On a debian 64 bit install with latest updates etc.
    And it does hang my system!
    The funny thing is, i ALSO get the bug when using string (GET/POST) functions.
    It doesn’t really affect the whole domain though, apache kills php after a timeout and restarts it, the website stays available to others browsing at that moment. It just jumps to 100% CPU usage on 1 core for a sec.
    Still, badass weird bug, I wonder if they’re accidentally using a 1 to MAX_INTEGER+1 value system instead of 0 to MAX_INTEGER, causing it to overflow somehow ..
    Only wild guesses here.

    Still, nice finding!

  13. BUG CONFIRMED on:

    Operating system: Windows NT 6.0 build 6002 i586 (Windows Vista)

    Web server: Apache/2.2.11 (Win32) PHP/5.2.8

    Server SAPI: apache2handle

    Server modules: core, mod_win32, mpm_winnt, http_core, mod_so, mod_actions, mod_alias, mod_asis, mod_auth_basic, mod_authn_default, mod_authn_file, mod_authz_default, mod_authz_groupfile, mod_authz_host, mod_authz_user, mod_autoindex, mod_cgi, mod_deflate, mod_dir, mod_env, mod_expires, mod_include, mod_isapi, mod_mime, mod_negotiation, mod_rewrite, mod_setenvif, mod_php5

    PHP version: 5.2.8

    Zend engine version: 2.2.0

    PHP extensions: bcmath, calendar, com_dotnet, ctype, session, filter, ftp, hash, iconv, json, odbc, pcre, Reflection, date, libxml, standard, tokenizer, zlib, SimpleXML, dom, SPL, wddx, xml, xmlreader, xmlwriter, apache2handler, bz2, curl, dba, dbase, fdf, gd, gettext, gmp, imap, interbase, ldap, mbstring, mcrypt, mhash, mime_magic, ming, mysql, mysqli, openssl, PDO, PDO_Firebird, pdo_mysql, PDO_ODBC, pdo_sqlite, shmop, snmp, soap, sockets, SQLite, tidy, xmlrpc, xsl, zip, exif, xdebug

    Tested on a simple login form. This is apparently very real, and (subsequently) very serious! I can’t believe how severe the potential consequences are, now that the whole of the Internet knows about it.

  14. Have you tried it on more than one PC? Sounds like a processor glitch – and could be a design flaw in an arithmetic processor. If it works elsewhere, could be.

  15. I’m running WAMP with PHP 5.3.0 and experience the hanging of the php =) Very strange. It does not however “bring down a Web server”. It’s just that request that hangs apparantly. I don’t know how long. I reloaded the page after +- 10 seconds.

  16. PHP 5.2 does not seem to hang on Ubuntu 8.10 64bit, however the value is rounded to only 11 decimal places on my system:

    john@context$ php -r’$d = 2.2250738585072011e-308;echo $d.”\n”;’
    2.22507385851E-308
    john@context$ php –version
    PHP 5.2.10-2ubuntu6 with Suhosin-Patch 0.9.7 (cli)

  17. [2011-01-02 15:49 UTC] pajoye@php.net

    1st reaction to your bug report, that’s before this blog post, which not that god for security related issue by the way.

    It should have been better to wait the fix before going wild and publish it like that 🙂

    Cheers,

  18. @Pierre,

    Yes, technically, “can you take a look at it please?” is a response; but maybe a real response would have been “we can confirm this happens” or “this could be serious” or “this is a security issue”. I have minimal knowledge of PHP — I don’t see how I could have made that determination myself.

  19. cthulhu:~$ php -r “var_dump(2.2250738585072011e-308);”
    float(2.2250738585072E-308)
    cthulhu:~$ uname -a
    FreeBSD * 7.1-STABLE FreeBSD 7.1-STABLE #2: Sun Jan 18 15:34:54 CST 2009 root@*:/usr/obj/usr/src/sys/CTHULHU amd64
    cthulhu:~$ php -v
    PHP 5.2.12 with Suhosin-Patch 0.9.7 (cli) (built: Jan 12 2010 19:21:18)

  20. Does not affect:

    # uname -a
    Linux 2.6.32-27-server #49-Ubuntu SMP Thu Dec 2 02:05:21 UTC 2010 x86_64 GNU/Linux

    # php -v
    PHP 5.3.4 (cli) (built: Dec 12 2010 10:49:53)

  21. I copied the number into reddit’s search to find posts about it, and now reddit is down.

    Am I being paranoid, or did that break the server? I didn’t mean to oh god oh god

    I don’t want to go to jail.

  22. I thought perhaps their server attempts to convert it’s search input into an integer for some reason. The coincidence was just too bizarre.

  23. @Rick Regan

    Consider that we do that already, if it was not reproduceable or not a real bug (bogus), it would have been closed in the 1st place.

    About the security part, right, but that’s rather obvious in this case (endless loop.

    btw, fix has reach SVN already, see the bug report for the links 🙂

  24. @Rafi,

    What about variations on the placement of the decimal point, like 222.50738585072011e-310? What about superfluous leading zeros in the exponent, like 2.2250738585072011e-0308? Your script doesn’t handle those.

  25. @Rick Regan

    You wrote:
    > Your script doesn’t handle those.

    It does. It searches for the substring ‘50738585072011e’ in all user-request vars.

  26. @Rafi,

    Yes, I was wrong about leading zeros in the exponent. But what about 2225.0738585072011e-311? 22250.738585072011e-312? What about trailing 0s before the exponent, like 2.22507385850720110e-308?

  27. Bug does not appear with PHP 5.2.6 / XAMPP 1.6.8a.
    Also does not seem to appear within PHP 5.3.3 (FastCGI), Suhosin Patch 0.9.10.

    cu, w0lf.

  28. For those using Zend Server or Zend Server Community Edition, a hotfix was just released. I have installed it on 2 servers running PHP 5.3 on CentOS 5 and can confirm that the fix works.

  29. I am confused. Does this only affect people with older cpus that do not support SSE2?

    I do not seem to be affected by the bug, yet am on a 32 bit CPU and PHP was compiled with -O2.

    uname -a:

    Linux www 2.6.9-67.0.22.ELsmp #1 SMP Fri Jul 11 10:38:12 EDT 2008 i686 i686 i386 GNU/Linux

    Running the test script outlined above comes back immediately. No hang.

    CPU is Intel(R) Xeon(R) CPU E5430 @ 2.66GHz which support SSE2.

    Since my CPU supports SSE2, would I not be affected by this?

  30. @John Flax,

    This is a comment from Rasmus Lerdorf, in the bug report:

    “If you are on an architecture that uses the x87 FPU and you haven’t forced SSE or float-store then you will see this problem.”

    What version of PHP are you using?

  31. PHP ver is 5.2.14. As far as I know I have not forced float-store or SSE, unless Centos does that by default.

  32. An official, proper fix for this has been committed. Who knows when the PHP team will release a new/updated version of PHP for this though.

    http://svn.php.net/viewvc/php/php-src/trunk/Zend/zend_strtod.c?r1=307095&r2=307094&pathrev=307095

    The “”fix”” Rafi came up with is utter nonsense as well (so many edge cases which aren’t addressed). I’d recommend folks suffering from this put pressure on the PHP team to tag and distribute a new release rather than using horrid hacks like the one here: http://www.aircraft24.com/en/info/php-float-dos-quickfix.htm

  33. @koitsu
    Of course, patching or upgrading PHP to a fixed version is the way to go.

    We just wanted to provide protection for sites that cannot immediately fix their PHP-installation and that want to protect their server against script-kids that try around with GET and POST-vars. Our script does exactly that. It is not a fix but a workaround.

  34. Does not crash on two CentOS servers (same hardware, different PHP versions)

    Intel(R) Xeon(R) CPU L5506 @ 2.13GHz stepping 05

    Linux version 2.6.18-164.11.1.el5

    One runs 5.2.13, the other 5.3.4, from utterramblings

    Does crash on Ubuntu 10.10, PHP version 5.4.4.-1ubuntu9.1
    on an Intel(R) Core(TM) i3 CPU U 380 @ 1.33GHz stepping 05

Comments are closed.