Forgot your password?
typodupeerror
Bug PHP Security

PHP Floating Point Bug Crashes Servers 213

Posted by timothy
from the inexactly-wrong dept.
angry tapir writes "A newly unearthed bug in certain versions of the PHP scripting language could crash servers when the software is given the task of converting a large floating point number, raising the possibility that the glitch could be exploited by hackers. The bug will cause the PHP processing software to enter an infinite loop when it tries to convert the series of digits "2.2250738585072011e-308" from the string format into the floating point format. The bug only seems to affect version 5.2 and 5.3 of the language." Adds reader alphadogg: "Computer scientist Rick Regan first reported the bug on Monday, and the PHP development team issued patches the following day."
This discussion has been archived. No new comments can be posted.

PHP Floating Point Bug Crashes Servers

Comments Filter:
  • 1 day turn-around (Score:4, Informative)

    by iONiUM (530420) on Thursday January 06, 2011 @02:58PM (#34780564) Homepage Journal

    The 1 day turn around for a patch is pretty impressive. I wish some bigger companies would offer such fast patches against vulnerabilities..

    • by Lennie (16154)

      1 day, really that long ? ;-)

      I've sometimes even seen 1 hour or even minutes from certain OSS projects. I guess it was trivial to fix.

    • Re:1 day turn-around (Score:5, Informative)

      by I8TheWorm (645702) * on Thursday January 06, 2011 @03:16PM (#34780866) Journal

      It's because they're not spending their time improving thread-safe modules, ternary operators, flip flopping again on defaulting magic_quotes, or understanding pragmatism :)

      • by kestasjk (933987) *

        That's the first time I've heard someone complain about Zend not being pragmatic.. Usually the argument is that they're too pragmatic and need to break backwards compatibility, fix inconsistencies, and make the language less forgiving.

        As someone who has used PHP quite a bit I'm curious about what you think the alternative is though:

        • Is the RoR dev team really more pragmatic than Zend, and does it have better threading performance?
        • Is the Perl team spending its time more effectively?
        • Could a proprietary ASP.NE
    • by antdude (79039)

      But was it tested well? Does it break anything else? :P

  • Hmmmm (Score:5, Funny)

    by Anonymous Coward on Thursday January 06, 2011 @02:59PM (#34780592)

    Step 1: Write stuff in PHP
    Step 2: ???
    Step 2.9999990834239320: Profit!

  • you actually havent found it ! hahaha. something like that ?

    a better patch performance than this would be to actually go back in time and fix the bug before it is discovered. but then again, there would be no bug and no bugfixing would be needed. alternate timecycle breakdown ?
  • by TheRaven64 (641858) on Thursday January 06, 2011 @03:13PM (#34780824) Journal

    Maybe I'm missing something, but why does PHP have its own version of strtod()? It's a standard C99 function, so you'll find it in libc or equivalent in any C99-compliant platform (including Windows) and more effort has probably gone into optimising that version than the PHP version, although if you're converting from strings to floating point values anywhere performance critical then you're probably Doing It Wrong.

    Did the Zend team think that there weren't enough security holes in PHP and decide to increase the attack surface?

    • by EkriirkE (1075937)
      There are several bugs in their conversion functions (data integrity)- apparently they don't bother fixing them unless it does something like take down a server.
    • Yes, but C wasn't invented here....
    • by fishbowl (7759) on Thursday January 06, 2011 @03:36PM (#34781226)

      >Maybe I'm missing something, but why does PHP have its own version of strtod()?

      That's a very good question. PHP's strtod is quite complicated, has its own memory allocator, does its own localization and rounding, and it is going to some lengths to ensure its own thread safety. If I were to guess, my guess would be that some of the target platforms for PHP/Zend are deficient in these areas.

      • Localization I can almost understand. OS X is the only platform I know of that lets you specify a per-thread locale that functions like this respect. But memory allocator? It's converting a string to a double - it shouldn't need a memory allocator at all, it just scans a string and collects the digits into a mantissa and exponent then. And thread safety? It's a pure function! It doesn't need any thread safety!
        • Localization I can almost understand. OS X is the only platform I know of that lets you specify a per-thread locale that functions like this respect. But memory allocator? It's converting a string to a double - it shouldn't need a memory allocator at all, it just scans a string and collects the digits into a mantissa and exponent then. And thread safety? It's a pure function! It doesn't need any thread safety!

          each thread in windows has its own independent localization settings also.

          • And thread safety? It's a pure function! It doesn't need any thread safety!

            strtod() has no side effects, but it still reads global variables. Does your locale use a period or a comma as a decimal point? And did another thread change the locale behind your function's back?

        • I don't think you understand how floats work. You don't "just collect them". You have to convert them into a fraction represented as a power of two. The exponent is a power of two, not a power of 10. The mantissa is the numerator of that fraction, typically with the leading 1 removed. Granted, this is something that could occur on the stack.

          As for thread safety, there's a lot of math that has to happen to cacluate that fraction. On many systems, particularly embedded systems on micros without a float
          • An interesting side effect of this, by the way, is that the number 0.1 cannot be exactly represented as an IEEE floating point number.
      • by kestasjk (933987) *
        It's because they're stupid, that's why.. That's why everyone does everything..
    • by Yetihehe (971185)

      if you're using PHP anywhere performance critical then you're probably Doing It Wrong.

      FTFY. (disclaimer: I'm programming in php daily and I like it).

    • Maybe I'm missing something, but why does PHP have its own version of strtod()? It's a standard C99 function, so you'll find it in libc or equivalent in any C99-compliant platform (including Windows) and more effort has probably gone into optimising that version than the PHP version

      Sometimes the overhead required to take values between languages actually makes doing it in the lower performance language slower, even if it's more highly optimized. This obviously manifests itself much more readily in quicker functions like strtod() where the overhead is likely to be large relative to the actual execution time. .

      Just for a taste, in Python (I'm no php expert) in order to call a C function you must context switch (flush registers), unbox the value into the native type, perform the operatio

      • by DavidTC (10147)

        I don't think you understood the question. The question is why PHP build its own implementation of a rather low-level library function that's actually part of the C99 standard. (And I'm pretty sure PHP requires C99 to compile.)

        No one's saying people who program in PHP should do anything different. They're saying that PHP itself should do things using standard libraries.

        One of the reason standard libraries exist is to get bugs like this being centralized to one location, so they're only fixed once ever.

    • by dkf (304284)

      Maybe I'm missing something, but why does PHP have its own version of strtod()? It's a standard C99 function, so you'll find it in libc or equivalent in any C99-compliant platform (including Windows) and more effort has probably gone into optimising that version than the PHP version, although if you're converting from strings to floating point values anywhere performance critical then you're probably Doing It Wrong.

      A number of languages have their own versions of strtod(), and it's because the system version is often deeply sub-optimal. (Going the other direction is even worse, especially if you want minimal representations of numbers.) Typically, system library authors stop once they've got something that works well enough, whereas other clients of that sort of functionality are much more exposed to it and so care much more about it being optimal. (After all, system library authors tend to think that people don't con

      • by Obfuscant (592200)
        A number of languages have their own versions of strtod(), and it's because the system version is often deeply sub-optimal. (Going the other direction is even worse, especially if you want minimal representations of numbers.) Typically, system library authors stop once they've got something that works well enough, whereas other clients of that sort of functionality are much more exposed to it and so care much more about it being optimal.

        Yes, because PHP is so widely known as a hard-number-crunching progra

  • Yes each time someone access it will start an infinite loop, but each PHP page has a max. processing time (usually set to around 30 seconds). So still quite a number of requests to the offending page are needed to bring the server down.

    To try the bug for yourself: $a = (float) "2.2250738585072011e-308";
  • Sweet. PHP finally has the qualifications to enter the X86 CPU market.
  • by gstrickler (920733) on Thursday January 06, 2011 @03:43PM (#34781330)
    I mean, for all practical purposes, it's an infinitely small number, so why shouldn't it be an infinite loop?
  • Very large? (Score:5, Interesting)

    by GrAfFiT (802657) on Thursday January 06, 2011 @03:45PM (#34781352) Homepage
    Am I the only one to notice that 2.2250738585072011e-308 is not very large?
    Apparently, some journalists need a patch too.

    My 2.2250738585072011e-308 cents.
    • And yep TFA did qualify it as the largest subnormal double-precision number. Of course the journalist probably didn't know what that meant.

    • by Chapter80 (926879)

      Am I the only one to notice that 2.2250738585072011e-308 is not very large?

      It depends on what font you display it in.

      It is a string, after all.

    • by gmuslera (3436)
      At least in spanish, a possible translation of large, that also is almost letter by letter the original word, is "largo", that in fact is more about lenght than size. And that number with all the digits qualify as a very long string. Maybe the original joiurnalist wrote that in spanish, or have spanish as mother tonge (ok, or maybe another latin derived language where that happens).
  • I guess I'll have to keep the copy of the combo somewhere other than on my PHP server now...
  • The inconsistent type system, lack of Unicode support, lack of namespaces, quirky parser, and other stupidities (== vs. ===) weren't enough, so. Is this bug inane enough to actually get people to realize that PHP bites?

    • by LingNoi (1066278)

      PHP has namespaces.

    • Re: (Score:3, Insightful)

      i've been saying this about intel chips for DECADES... why won't anyone listen? INTEL CHIPS SUCK! they once handled a single floating point edge case incorrectly! AND THEN THEY FIXED IT! WHY WOULD ANYONE USE AN INTEL CHIP?

      ignorant hypocritical marketeering = the highest level of insight.

      slashdot = stagnated.

      • by wmbetts (1306001)

        I wish I could mod you up. Pretty much everything the OP complained about has been addressed.

    • by Bigbutt (65939)

      Alternate suggestions? Just curious as I do some PHP scripting for various things.

      [John]

      • by wmbetts (1306001)

        The OP is full of shit. It does have unicode support and does support namespaces. The generalization of the parser I have no idea what he's talking about and == vs. === makes sense if you know what you're comparing.

        • Re: (Score:2, Informative)

          by Ant P. (974313)

          PHP Warning: assert(): Assertion "strlen("£") == 1" failed in /home/ant/nou.php on line 3

          Awesome Unicode support there, buddy. Performance of C combined with the user-friendliness of an interpreted high-level language! Oh wait.

          Who's full of shit again?

          • Re: (Score:2, Troll)

            by wmbetts (1306001)

            PHP Warning: assert(): Assertion "strlen("£") == 1" failed in /home/ant/nou.php on line 3

            Awesome Unicode support there, buddy. Performance of C combined with the user-friendliness of an interpreted high-level language! Oh wait.

            Who's full of shit again?

            Awesome incorrect use of a language while trying to sound smart. If you don't even know how to use a language please refrain from commenting on perceived deficiencies.

            ?php
            mb_internal_encoding('UTF-8');
            if (mb_strlen("£") == 1) {
            print "lol@op being full of shit\n";
            }

            • by Ant P. (974313) on Thursday January 06, 2011 @06:41PM (#34784018) Homepage

              "Correct" use of the language is to ignore string functions entirely and use an optional extension, because they don't actually support Unicode. In 2011. Amazing.

              You know, C actually has a valid excuse for that sort of thing. But I'm sure you'd rather call people names all day like a retarded skript kiddie, than admit PHP's Unicode handling is ass-backwards and crippled compared to everything else out there.

            • Python 2 has Unicode string literals...

              $ python
              Python 2.6.6 (r266:84292, Sep 15 2010, 16:22:56)
              [GCC 4.4.5] on linux2
              Type "help", "copyright", "credits" or "license" for more information.
              >>> if len(u"£") == 1: ... print "Lawl @ some kid thinking PHP's a real language" ...
              Lawl @ some kid thinking PHP's a real language

              And of course, Python 3's strings *default* to Unicode...

              $ python3
              Python 3.1.2 (release31-maint, Sep 17 2010, 20:27:33)
              [GCC 4.4.5] on linux2
              Type "help", "copyright", "credits" or

            • mb_internal_encoding('UTF-8');

              Fatal error: mb_internal_encoding is not defined because mbstring is not enabled by default [php.net]. And no, you can't install this extension on your hosting company's copy of PHP.

          • by spitzak (4019)

            If you think strlen should return the number of "characters" in Unicode then you are the one that is full of shit.

            I assume this is returning the length of the UTF-8 encoding, which is an actually useful value.

            Only beginner programmers think "characters" are important. Eventually they will learn, but only after making boneheaded screwups like you just did. Your sort of thinking is why I18N is not working after 20 years of trying.

      • Re: (Score:3, Insightful)

        by jjohnson (62583)

        The only reason to use PHP is if you're scripting in a shared hosting environment where PHP or Perl CGI scripts are the only options available.

        If you're not, you should look at either Python or Ruby. Either will be vastly better, along with some less popular choices like Lua, Groovy, Scala, Erlang...

        Basically, anything else. It's not that PHP can't be sufficient, it's that it has a long and hideously compromised development history and actually demands far more of you, the scripter, to make it safe and us

    • by sootman (158191)

      > *Now* can we admit PHP... ... is perfectly suitable for literally thousands upon thousands of tasks?

      In other news, Euclidian geometry cannot correctly describe Mercury's orbit. Therefore the value of everything ever done with this type of math, from the pyramids to Apollo 11, is negated.

      THERE IS NO PERFECT LANGUAGE. PHP is 100% good enough for absolutely everything I do day to day, and has been a paying gig for nearly a decade. My job does not require me to perform calculations on very large numbers.

  • by MrP- (45616) <rob@@@elitemrp...net> on Thursday January 06, 2011 @04:13PM (#34781814) Homepage

    Search http://us3.php.net/ [php.net] for 2.2250738585072011e-308 and the page hangs and then returns an error. Search for something else like 2.2250738585072011e-307 and it's fine.

    Searching the main www.php.net isn't affected.

    So looks like some parts of php.net are still running the affceted version.

  • I don't understand why the patch solves the problem....though I haven't done any serious software development for years. It looks like all they did was add the "volatile" keyword to a variable declaration.

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

    From:

    double aadj, aadj1, adj;

    To:

    volatile double aadj, aadj1, adj;

    But after quickly reviewing the code, I don't see why the volatile keyword fixes this problem. It doesn't appe

    • by jspenguin1 (883588) <jspenguin@gmail.com> on Thursday January 06, 2011 @05:15PM (#34782820) Homepage

      The x87 registers are all 80 bits long, while standard doubles are only 64 bits. You can get into a situation where two floating point registers contain different values that round to the same double value, yet they don't compare equal. Adding the volatile keyword forces the compiler to copy the registers to the stack and read them back every time they are accessed, truncating them to 64 bits. The patch is only needed on x86 because x86_64 uses SSE3 for floating point, which works with 64-bit floats natively.

      • by hawguy (1600213)

        Sounds like a very reasonable explanation and makes sense. But....shouldn't the compiler already know this and pull the variable out of the 80 bit FPU register into a 64 bit double before doing an == comparison between doubles? Seems like this problem would nearly always be the case when doing floating point equality comparisons.

        • by Obfuscant (592200)
          But....shouldn't the compiler already know this ...

          If you are using memory-mapped IO, the compiler won't know if you are reading from an external device like an FPU or a simple memory location.

          This kind of coding is standard for IO libraries. Things like reading values from memory mapped UART registers. A smart compiler will optimize the actual memory access away and rely on the previous read that is has stored in a local register. Without calling the address volatile you can wait all day for the IORDY b

          • by hawguy (1600213)

            I understand why you'd use a volatile variable for something that can be touched by an external event or process, but that's not what happened here. I'm talking about this specific case involving the FPU where it was the compiler that put my variable into the FPU register, it didn't happen by some external event that the compiler had no knowledge of.

            If the compiler is the one that decided to move my variable into a high precision FPU register, then shouldn't it be smart enough to convert it to the appropria

            • by TheSunborn (68004)
              But the definition of correct is not obvious when talking about floating point results which can't be represented exact.

              Look at this small c function

              void fun(double a,double b) {
              double c=a*b; // Line 1
              // Misc other code
              double d=c*2; // Line 3
              /*
              The value of d here may differ based on compiler optimisation, because the representation of the variable c may be either 64bit or 80 bit depending on if the value were written to memory, and then read back or not between Line 1 and Line 3. And both result would be co
      • You can get into a situation where two floating point registers contain different values that round to the same double value, yet they don't compare equal

        Pretty much every FAQ on the use of floating point types in programming has "do not ever compare two floating point values for equality" in the first 3 items. Why the hell do they need a comparison like that there?

    • There's a link from Rasmus' tweet [twitter.com] to a GCC "bug" that explains the problem and potential fixes [gnu.org], one of which is:

      (2) A partial but simple solution: Do comparisons on volatile variables only.

      Hope this has been enlightening!

    • I'm too lazy to actually figure out exactly what's happening, but that's most likely correct. The volatile keyword prevents the compiler from doing things like moving values into registers, and the patch mentions that it is a problem with the FPU. I'm guessing that there's some sort of approximation algorithm happening, which stops when the error is sufficiently low. If that value got moved to something with different precision, it could stop converging.

      C purists will say that you can look at code and tell

      • by Obfuscant (592200)

        The volatile keyword prevents the compiler from doing things like moving values into registers,

        What "volatile" actually means is that the memory location underlying the variable is able to change without the CPU knowing it and thus not only can the variable not be assigned to a register (which is what the "register" keyword causes), it must be accessed anew each time it is referenced. Thus, code that says something like:


        if( x==0 )

        do something;

        if( x >

  • I wonder how many of you read this article's summary and them immediately wrote a quick PHP script to perform this exact conversion. If you did, did you crash your machine or did you encounter a disappointing non-event?

    • by kennykb (547805)

      I didn't test PHP, because I never use it.

      I did, however, do the responsible thing and ask myself, "does code that I maintain have a similar bug, and does its test suite validate it?" I checked the Tcl test suite, and discovered that it lacked a test for the case. I added test cases for input and output conversion of the largest positive subnormal, the smallest negative subnormal, the smallest positive normal, and the largest negative normal. (All the test cases passed, so committing them ended what I had

  • I ran this:
    <php echo "start\n"; $a = (float) "2.2250738585072011e-308"; echo $a,"\n"; echo "end\n"; ?>

    It got this:
    [Thu Jan 06 23:30:49 2011] [error] [client 10.1.1.30] PHP Fatal error: Maximum execution time of 30 seconds exceeded in /home/dougie/public_html/test_crud/bug.php on line 3

Simplicity does not precede complexity, but follows it.

Working...