6

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!

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

AHM
  • 5,145
  • 34
  • 37
  • In this case yes, but that's not the point - in the real app where this is used, the string can contain lot's of different values, and I match it against different regular expressions to figure out what it contains. This is a minimal example that shows the bug/problem I am experiencing. – AHM Aug 11 '10 at 10:06
  • Run a script with `phpinfo();` on your servers. When you look at the PCRE Library Version you'll probably notice that the Windows version of PHP is using a different version of the PCRE lib as the Debian package... If that's the case that would explain the difference in how your script is handled (try recompiling PHP with the same version of the lib and you'll probably end up with the same result on both servers). – wimvds Aug 11 '10 at 10:48
  • Can you please post your update as an answer. – Toby Allen Mar 01 '13 at 07:00
  • No, the answer that solved the problem I was having is the answer that I marked as answer. My update was just to clarify for others that this is caused by a low stacksize. Apparently, at least in the Windows build of Apache I was using at the time, there was no way to increase the stacksize - so my solution was to change my regex. – AHM Mar 01 '13 at 08:53
  • Is there a way to programmatically detect the conditions that would lead to this crash? Or have you seen code that does this? – Steve Clay Jun 24 '13 at 20:19
  • I think that a general purpose way of detecting this problem would be difficult - you would have to have some way of analyzing regular expressions and detect how much stack they would require. – AHM Jun 25 '13 at 16:19

4 Answers4

3

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.

user187291
  • 53,363
  • 19
  • 95
  • 127
  • 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 Aug 11 '10 at 10:34
  • 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 Aug 11 '10 at 10:43
  • 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 Aug 11 '10 at 11:23
1

Increase the Apache stack and the problem will go away, or use the httpd config to increase it.

Most Apache builds have a way too small stack size while we have a normal one for the PHP builds. It explains why the same expressions will work in CLI and not in Apache (stack is per process and the process is owned/defined by Apache).

Pierre
  • 716
  • 4
  • 10
1

Thanks greatly to this post. It solved my problem.

The config for Apache stack on windows is the reason. Quick way to increase Apache stack size is using the ThreadStackSize directive in the Apache's configuration (http://httpd.apache.org/docs/2.2/mod/mpm_common.html#ThreadStackSize)

It might looks like this:

<IfModule mpm_winnt_module>
   ThreadStackSize 8*1024*1024
</IfModule>

It sets stack size to 8 MB, the same as the default value on Linux.

(Thanks for https://bugs.php.net/bug.php?id=47689)

Emeric.Lee
  • 11
  • 2
0

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 González
  • 142,137
  • 41
  • 261
  • 360
  • 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 Aug 11 '10 at 10:31
  • 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 González Aug 11 '10 at 10:58
  • this is not a php crash, I bet is from the libpcre – mathk Aug 11 '10 at 11:45