Become a fan of Slashdot on Facebook

 



Forgot your password?
typodupeerror
Bug PHP Security

PHP Floating Point Bug Crashes Servers 213 213

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) on Thursday January 06, 2011 @03:15PM (#34780846)

      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) * on Thursday January 06, 2011 @10:52PM (#34786796) Homepage

        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.NET really fill PHP's shoes?
        • Most of all: If it's so terrible (as this nightmare of a bug demonstrates) why is it in use by many of the most popular websites and web software packages?

        They develop and maintain a new language, originally created by someone with no experience, for a very hostile environment, staying fairly backwards compatible while still improving the language constantly, as an independent company using an FOSS license, and ending up as one of the main web-dev languages despite lots of well-funded/well-hyped/well-established competition..
        I don't think the company that pulls that off deserves so much scorn..

    • by antdude (79039) on Thursday January 06, 2011 @05:05PM (#34782676) Homepage Journal

      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) on Thursday January 06, 2011 @03:33PM (#34781150) Homepage
      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.
    • by flyingfsck (986395) on Thursday January 06, 2011 @03:35PM (#34781204)
      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.

      • by TheRaven64 (641858) on Thursday January 06, 2011 @04:29PM (#34782014) Journal
        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!
        • by BradleyUffner (103496) on Thursday January 06, 2011 @04:35PM (#34782120) Homepage

          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.

        • by localman57 (1340533) on Thursday January 06, 2011 @04:41PM (#34782240)
          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 unit, static locations are allocated for this temporary memory, because stack accesses are often very slow on low end micros without good indexed-address instruction sets. Therefore, it is expected that the floating point libraries are not to be used reentrantly. If you launch multiple threads (or do floats at different interrupt levels) you have to save the float library static space as part of your context.
      • by kestasjk (933987) * on Thursday January 06, 2011 @10:16PM (#34786500) Homepage
        It's because they're stupid, that's why.. That's why everyone does everything..
    • by Yetihehe (971185) on Thursday January 06, 2011 @04:02PM (#34781618)

      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).

    • by Wrath0fb0b (302444) on Thursday January 06, 2011 @04:22PM (#34781938)

      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 operation, rebox the value into the Python type, then context switch back to the interpreter. Here's the strdod() example in python (I think) with the error handling


      int
      Py_strtod(PyObject const *string, PyObject *target)
      {
              target = 0;
              if ( !PyString_check(string) )
                      return -1; /* Should we set something so the caller knows why this failed? */
              const char* const cstr = PyString_AsString(string);
              const double cdoub = strtod(cstr,NULL);
              if ( 0 != errno ) /* something something */
              target = PyFloat_FromDouble(cdoub);
              if ( PyError_Occured() ) /* something something */

              return 0; // Success
      }

      So in the process of using the faster C function, we've changed context a twice and had to check for a number of errors that are impossible to throw otherwise. Is it worth it for strtod()? Maybe, but not surely.

      • 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.

    • 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 convert back and forth a lot. Mostly they don't, but since when did that cover 100% of cases?)

      Which isn't to say that if you're is reimplementing a system library function, you can get out of testing it very thoroughly. Oh no, not at all...

      • by Obfuscant (592200) on Thursday January 06, 2011 @06:45PM (#34784062)
        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 programming language that the time it takes to convert a string to binary double is criti-fucking-al. And it is well known that there are no standard, optimized math libraries that can do that job.

        I mean, all of the bigger climate simulation models are written in PHP, don't you know? FORTRAN is just too damn slow for such things.

        As for the previous poster who complained about how hard it is to make C calls from Python (as an example of how hard it is for PHP to do the same), well, yes, if you had to make your own call to the C function strtod you might have a point. But that call would be buried internal to the PHP system and wrapped by a PHP function so the programmer wouldn't have to do the hard work, and it would be done at the internals level.

        Which isn't to say that if you're is reimplementing a system library function, you can get out of testing it very thoroughly. Oh no, not at all...

        But it is to say that if you are reimplementing the C library in PHP you are doing something very very wrong and very very stupid.

  • by ickleberry (864871) <web@pineapple.vg> on Thursday January 06, 2011 @03:21PM (#34780954) Homepage
    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";
  • by hhedeshian (1343143) on Thursday January 06, 2011 @03:33PM (#34781148)
    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.
  • I guess I'll have to keep the copy of the combo somewhere other than on my PHP server now...
  • by MostAwesomeDude (980382) on Thursday January 06, 2011 @04:08PM (#34781708) Homepage

    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?

  • 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.

  • by hawguy (1600213) on Thursday January 06, 2011 @04:19PM (#34781898)

    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 appear to be multithreaded code where another thread could stomp on the variable, and it just seems to be straight arithmetic, it doesn't seem like they are handing it off to a math coprocessor and then later waiting for the variable to be set.

    Does the volatile keyword change the compiler optimizations in a way that avoids the problem?

    • 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) on Thursday January 06, 2011 @06:03PM (#34783526)

        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) on Thursday January 06, 2011 @06:59PM (#34784230)
          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 bit to go high on a serial input device, as the CPU simply bit-compares the data in an internal register.

          This is why you use the native libraries for such things when running on a variety of devices, so that people who understand that specific hardware and system better than you do can deal with such specificity and you can deal with writing a good programming language.

          • by hawguy (1600213) on Thursday January 06, 2011 @07:41PM (#34784766)

            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 appropriate precision before doing a comparison with a variable of less precision?

            • by TheSunborn (68004) <tiller@@@daimi...au...dk> on Thursday January 06, 2011 @09:15PM (#34785956)
              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 correct even if they would not be equal.
              */

              }

      • by shutdown -p now (807394) on Thursday January 06, 2011 @06:31PM (#34783894) Journal

        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?

    • by adamofgreyskull (640712) on Thursday January 06, 2011 @05:15PM (#34782830)
      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 exactly what assembler output it will result in, but that's a ridiculous claim on any sort of optimizing compiler--a whole lot of stuff you didn't specify goes on.

      • by Obfuscant (592200) on Thursday January 06, 2011 @07:16PM (#34784476)

        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 > 0)

                        do something else;

                end

        end

        cannot optimize out the (x>0) test and something else code if x is marked volatile.

        C purists will say that you can look at code and tell exactly what assembler output it will result in, but that's a ridiculous claim on any sort of optimizing compiler--a whole lot of stuff you didn't specify goes on.

        C purists stopped being able to say that a long long time ago. Or int64 time ago.

        I was once using an SGI MIPS C compiler, writing code I needed to run as fast as computerly possible. I was doing all the standard and even some pretty advanced hand optimization steps and getting nowhere. I finally realized that the O3 option to the compiler was already doing pretty much everything I was doing by hand and I was wasting my time. And then I ran across an early version of the Intel C compiler which was happily optimizing out an entire chunk of code that wasn't optimizable. (It was something to do with the instruction pipeline and branch look-aheads that wasn't being handled right, not a true "optimization". That means the code was still there, just lost in the pipeline when a branch took place.) So, it is as easy to say that there is a lot of stuff going on that you didn't know about, and a lot of stuff that isn't going on that you think should be.

  • by j741 (788258) on Thursday January 06, 2011 @05:04PM (#34782662) Journal

    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) on Thursday January 06, 2011 @10:37PM (#34786686)

      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 to do.)

      Tcl, too, has its own conterparts to strtod and sprintf("%g"), because it depends on float->string->float being 100% lossless and on having the string representation of a float be the shortest string that reconverts to the given number. It's quite tricky to get right; this particular corner case is the tip of the iceberg.

  • by Dark$ide (732508) on Thursday January 06, 2011 @07:36PM (#34784718) Journal
    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

"If there isn't a population problem, why is the government putting cancer in the cigarettes?" -- the elder Steptoe, c. 1970

Working...