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.
<?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:
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?
Yes, it’s serious.
1/4/2011: There is a lot of discussion about this, in addition to the comments below; for example, see
- Hacker News.
- My PHP bug report.
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:
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.”
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)
tried it on PHP Version 5.3.2-1ubuntu4.5 does not seem to be affected either.
PHP 5.3.3 (cli) (built: Aug 22 2010 19:41:55) everything works fine.
Hangs on: Debian PHP 5.3.3-6 with Suhosin-Patch (cli) (built: Dec 7 2010 18:23:49)
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?
As a reference, this very bug is being discussed in Hacker News:
PHP 5.3.3-1ubuntu9.1 with Suhosin-Patch appears to have the problem, on my Ubuntu 10.10 machine. Interesting!
Running PHP 5.3.3, and tested that code with not problems !
PHP 5.2.16 under suphp is apparently not affected.
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.
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.
I can reproduce this on 5.3.2 running on Ubuntu 10.04.1 on a 32 bit machine.
You received answers, no fix yet but you can’t say you did get any response yet to your bug report.
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.
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)
This doesn’t effect PHP 4, at least not the build I am running, it’s a bit old on this machine.
For those on 64-bit machines, won’t the equivalent (much larger) 64 bit value likely result in the same bug?
@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.
doesnt hang on PHP Version 5.2.8 XAMPP – Ubuntu
Does not hang http://www.php.net/cal.php?id=222.50738585072011e-310 🙁
> 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);”
$ php -r “\$d = ‘2.2250738585072011e-308’; echo \$d + 0;”
php -r “var_dump((float) ‘2.2250738585072011e-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
> 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.
Works fine for me (PHP 5.2.10 w/ suhosin patch, ubuntu 9.10, on Intel P4)
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!
I hadn’t gotten a response as of the time I published this article, but I see there are some now.
Does NOT hang on a 32bit Ubuntu 8.04.4, dual AMD Opteron 246.
Forgot to add: PHP 5.2.4-2ubuntu5.12 with Suhosin-Patch 0.9.6.2 (cli) (built: Sep 20 2010 13:18:10)
32bit only then?
Does not hang on php 5.2.13 on 64 bit RHEL 5.4
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.
Report as a bug to PHP team. Highlight as a security issue. Very simple to patch.
NOT affect PHP 5.3.3-1ubuntu9.1 with Suhosin-Patch (cli) (built: Oct 15 2010 14:00:18)
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.
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.
So, um, I’d be interested to know what led you to write that statement in the first place?
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”;’
john@context$ php –version
PHP 5.2.10-2ubuntu6 with Suhosin-Patch 0.9.7 (cli)
I’m writing an article about subnormal floating-point numbers and I was playing with the numbers at the “edges.”
Yes, all variations of the exponent seem to cause it, like
[2011-01-02 15:49 UTC] email@example.com
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 🙂
s,god,good, lapsus 😉
Awesome. Very interesting
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.
cthulhu:~$ php -r “var_dump(2.2250738585072011e-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)
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)
Linux 2.6.32-22-generic #36-Ubuntu SMP Thu Jun 3 22:02:19 UTC 2010 i686 GNU/Linux
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.
no Josh, that is called a “string”
reddit being down is not a once in a lifetime event
I thought perhaps their server attempts to convert it’s search input into an integer for some reason. The coincidence was just too bizarre.
Great find. It’s shocking this hasn’t been noted prior considering just how long it may have been in the wold. Lets hope for a quick fix.
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 🙂
PHP Version 5.2.6-1+lenny9 (32-bit)
OK. But in the future, please consider changing your “no news is bad news” policy.
Windows XP SP3
Athlon XP 2600+
I’m experiencing this bug as well.
We have slapped together a quick workaround.
Have to use substring-search because “2.2250738585072011e-308” could also be spelled “02.2250738585072011e-308” or “002.2250738585072011e-308” etc.
Quick workaround can be found here:
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.
> Your script doesn’t handle those.
It does. It searches for the substring ‘50738585072011e’ in all user-request vars.
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?
Good Point. Changed the script to remove the “.” and changed the string that is searched for.
Better now ?
That will probably do it, if you change ‘250738585072011’ to ‘22250738585072011’.
Done. Would have worked with “’250738585072011” as well but is less unlikely to catch “false positives” now.
Yes, false positives were my concern.
Very nice and funny finding 🙂
How did you find the bug?
See comment #38 above.
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.
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.
affected, too. Note: php5-cgi!
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.
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?
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?
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.
Updated the workaround now:
– handles arrays
– terminates execution instead of deleting the offending variable
– performance improved
@Rafi Thanks! we deployed your fix, very easy.
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.
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
I’ve confirmed it happens on Apache/2.2.8 (Win32) PHP/5.2.5.
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.
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
Sorry, that’s PHP 5.3.3-1ubuntu9.1
Are you running 32bit Centos or 64?
My bad, thought I pasted it completely, it’s 64bit.
Here are a few approaches to remediating this nasty bug while we are waiting for vendor patches.
@Jim Manico :
You’ve posted to the wrong blog entry. This one is about PHP.
Comments are closed.