fieser Bug in PHP 5.3 [Update]

Man stelle sich folgenden Quellcode in einem Onlinebanking-Formular vor:

<?php
if (!empty($_POST['ueberweisungsbetrag']) && filter_var($_POST['ueberweisungsbetrag'], FILTER_VALIDATE_FLOAT)!==false)
{
    $ueberweisungsbetrag = $_POST['ueberweisungsbetrag'];
}
else
{
    $ueberweisungsbetrag = 0;
}
?>
<input type="text" name="ueberweisungsbetrag" value="<?php echo $ueberweisungsbetrag; ?>" />

Sieht ja eigentlich erstmal ganz vernünftig aus, oder? Validierung des einigegebenen $_POST-Betrags, um dem Benutzer bei einem Fehler das erneute eintippen zu ersparen. Wenn man nun allerdings die verdammt kleine Gleitkommazahl 2.2250738585072011e-308 als Betrag eintippt, hat das den gleichen Effekt wie

while (true) {}

Nämlich: Fatal error: Maximum execution time of 30 seconds exceeded in /var/www/test.php on line 3. Diese ominöse Zahl führt also PHP-intern zu einem verrecken des Scripts. Das passiert immer, wenn die Zahl auch wirklich als Zahl interpretiert wird.

Heißt:

print $_GET['floatval']; // doc.php?floatval=2.2250738585072011e-308 -> no problem - treated as string
print "2.2250738585072011e-308"; // no problem - string
print 2.2250738585072011e-308; //crash!
print "2.2250738585072011e-308"*1; // crash!
print (float)"2.2250738585072011e-308"; // crash!
filter_var("2.2250738585072011e-308", FILTER_VALIDATE_FLOAT); // crash!

Wenn irgendwo also $_GET-Variablen validiert werden (=als Zahl interpretiert werden), könnte man so recht billig eine DoS-Attacke fahren.
Es muss übrigens genau diese Zahl sein, bei einer minimalen Abwandlung geht alles gut. Es ist auch nur die 32 Bit Version betroffen. Bei meinen Tests unter Ubuntu (PHP Version 5.3.3-1) und Windows XAMPP (PHP Version 5.3.1) war das wunderbar reproduzierbar. Laut Golem seien die PHP-Versionen 5.3.3-6, 5.3.2-1 und 5.3.1 betroffen. Ein Update gibt es noch nicht (Stand: 04.01.2010 2011 – mit Dank an den Klugscheißer in den Kommentaren ;)).

Es gibt bereits ein PHP-Bug-Ticket dazu, gefunden hat den Bug dieser Blogger.

[Update 06.01.2011]

Bei den Hacker News gabs eine interessante Diskussion, die auch folgenden Kommentar hervorbrachte:

This is the nature of floating point numbers: they’re not exactually [sic] „exact“ at all. Converting a fixed fraction decimal number into a floating point means turning an exact number into its best approximation. In order to get the approximation as close as possible to the original number, a floating point conversion algorithm will perform several runs until the error between the original number and the floating point representation is smaller than some very small value. This leads to problems when either the error can’t get smaller than the required precision, or when the error doesn’t decrease per iteration. In both cases an algorithm that doesn’t have error detection will be stuck in an infinite loop.

Laut Johannes Schlüter sind übrigens auch alte PHP-Versionen bis runter zur 3.0 betroffen. Für die aktuelleren 5.3 und 5.2 Zweige wurden jeweils aktualisierte Versionen (5.3.5 bzw. 5.2.17) released, die diesen Bug beheben. Auf älteren Versionen muss mit -mfpmath=sse bzw. -ffloat-store PHP neu kompiliert werden. Übrigens sind laut Zend nur die 32Bit-Versionen von Linux befallen, unter Windows jedoch 32Bit und 64Bit-Builds.

Error: No site found with the domain 'test.basti1012.bplaced.net' (Learn more)