views:

95

answers:

2

I have discovered that the following PHP code crashes when it runs on a Windows Apache server:

<?php

$test = "0,4,447,11329,316,415,142,5262,6001,9527,11259,11236,1140,9770,9177,9043,11090,268,5270,9907,9196,10226,".
        "9399,1476,9368,6929,1659,11074,10869,8774,739,11344,10482,6399,8910,8997,11198,137,10148,10371,5128,767,2483,".
        "211,9973,10726,9299,778,11157,9497,275,9418,11141,241,5288,11324,776,10960,9289,7944,222,10686,11428,9723,".
        "10615,11399,9869,9083,10180,10043,9957,9387,9215,8869,9667,10174,10902,6607,9282,1259,395,10411,152,9344,8949,".
        "10923,8976,11042,11519,10704,10979,216,10044,9201,1721,5831,881,9721,1757,11054,1335,6151,9526,9081,111,498,".
        "2960,438,5313,206,318,10820,8192,6039,9161,11012,1717,1360,10757,4314,11280,9647,9346,10546,11006,9553,10365,".
        "6148,10565,4532,2776,4124,8853,6145,478,4539,540,9981,726,7186,11122,324,10524,1139,7900,9581,6869,1724,10851,".
        "10059,10018,11032,1290,3818,782,796,917,8740,6935,11439,10799,10948,249,2068,8778,6289,295,2766,9425,791,309,".
        "4753,10418,771,260,10835,10441,6434,10164,10475,10842,9013,11224,2247,8972,2141,2078,2152,475,9077,6291,10285,".
        "8067,753,6660,10889,431,2503,6007,9180,810,11447,2461,3689,7104,10150,10921,895,10598,747,10570,305,4497,11055,".
        "11496,10938,10722,8761,10086,11482,6780,6685,6918,10286,10659,9996,4074,9118,907,5192,283,2230,8884,6966,".
        "8820,8132,3598,9599,6796,11257,7049,5992,8637,4168,9017,7950,7165,10721,10037,1071,8044,759,11429,6380,".
        "10239,1593,9455,9704,10357,6737,2958,4051,9754,6564,11407,8716,7485,1528,6857,7406,9579,7259,1609,7820,".
        "4448,10289,1123,7005,8123,9316,914,9655,5280,9710,7822,510,10795,10476,8706,6160,8248,6978,9300,10643,".
        "7106,10250,519,7860,4733,904,8773,4714,8695,8633,6105,3312,11548,9580,10389,4886,4587,513,8485,4606,".
        "6471,581,526,637,3523,3772,3153,9336,9120,7633,3755,10087,524,10015,8563,556,1230,570,3652,569"
        .",8473,10209,3886,573,5363,4715,3865,9452,1218,7066,575,577,4724,7655"
        ;

$hest = preg_match('/^\d+(?:,\d+)+$/', $test);
var_dump($hest);
?>

Interestingly enough, the code works if the string is a little shorter - if I comment out the last part (line) of the string, the code runs and works as expected.

However, as the code is presented here, the code causes Apache to crash and respawn - no error is logged. If I try to run the code directly through PHP it works as expected, so the problem is somehow related to Apache.

I have experimented with the "pcre.recursion_limit" setting, and found that if I lower this to 689 it doesn't die, but instead the preg_match() call fails with a PREG_RECURSION_LIMIT_ERROR. For higher values of pcre.recursion_limit Apache dies.

Apache 2.2.11 PHP 5.3.0

I have also tested this on a Debian server where I do NOT see the error, and with different versions of PHP and Apache on Windows, where the error occurs as described above, so it seems that it may be Windows related.

Have anyone seens this before? I would really like a hint of some sort!

-- Anders

UPDATE: I is because of a "bug" in Apache for Windows - well, not really a bug, but it seems that Apache is compiled with a small stacksize, and that causes this error in PCRE when PHP uses it. See http://bugs.php.net/bug.php?id=47689

A: 

A crash normally means you've hit a PHP bug. It happens now and then. Unless you're able to upgrade your PHP installation, you'll have to rewrite your code. Anyway, the high recursion level suggests that you should not be using regular expressions or, at least, you should first split the input string in smaller pieces.

Álvaro G. Vicario
Yeah, but it confuses me that the code works when I run it with just PHP (as in "php.exe test.php" from the command line). The crash only happens when it runs through Apache, and it seems, only on Windows. I think this may be related to stacksize (from http://www.pcre.org/pcre.txt) but I have no idea how to reconfigure Apaches default stacksize, or why the default should be different between Windows and Linux versions of Apache...
AHM
Command line PHP is `php.exe` and Apache module is `php5apache2_2.dll`. Although they probably share 99% of the code, they are different programs. And perhaps you've hit an *Apache* bug. Sorry for not being more helpful.
Álvaro G. Vicario
this is not a php crash, I bet is from the libpcre
mathk
+3  A: 

It's always a good idea to lower "pcre.recursion_limit", because the default high value can corrupt the process stack (see http://php.net/manual/en/pcre.configuration.php) - this is exactly what happens with your mod_php install. Since preg functions don't throw an error when recursion/backtracking limits are reached, it may be useful to have a wrapper like

function match($re, $text) {
    preg_match($re, $text, $m);
    if(preg_last_error())
         trigger_error("preg: " . preg_last_error());
    return $m;
 }

At least, this lets you know when something goes wrong.

Besides that, try to simplify your patterns when possible, for example /^\d[\d,]*\d$/ does the same as above, but with zero recursion.

stereofrog
Yeah, I played with this and found that if I lower the recursion limit to 689 (which seems very low) I get the PREG_RECURSION_LIMIT_ERROR. For values of 690 or higher, Apache crashes and respawns without logging any errors.
AHM
@AHM: there seems to be a simple rule - two recursive calls for each group. You have 348 chunks in your string, therefore the limit is 348 * 2 = 696
stereofrog
Yeah, that seems to make sense. Apparently 690 stackframes is as much as mod_php for Windows wants to allow PCRE to use. For now I changed the regex as you suggested, but I am still mystified by why the limit is so low, and why there is no way to raise it...
AHM
@AHM: this looks relevant: http://bugs.php.net/bug.php?id=47689
stereofrog
Yeah, it is that bug I am encountering! I can't get the editbin fix to work though, but I am pretty sure this is it. Thank you!
AHM