Package: php5
Version: 5.2.0-8
Severity: important
Tags: security

Hello,

while developing my PHP application, I stumbled across PCRE usage which 
crashes the PHP binary. After some trial and error, I was able to reduce 
the problem to the attached piece of PHP code. I was able to reproduce the 
segfault on 3 different machines running Debian, under php 5.2.0-8 
(testing, 2 machines) and 4.3.10-18 (stable).

I also compiled versions of libpcre3 and php5-cli with debugging 
information to get a stack trace. The topmost frames of the stack backtrace 
follow at the end of this message. Inside libpcre3, the code recurses 
through pcre_exec.c lines 677 and 1190 until the stack overflows.

Next, I tried to find out whether the crash is reproducible with a C 
program. But while AFAICT the attached C program does the same as the code 
in php-5.2.0/ext/pcre/php_pcre.c, no segfault happens. So maybe PHP 
corrupts memory between compiling and executing the regex? I don't know! 
:-/ Running "valgrind php5 php-5.2.0-8-segfault.php" doesn't output
anything which looks like a PCRE-related bug.

One more thing: I also tried to trim down the example further by reducing 
the length of the subject string. This gives weird results: When some parts 
of the input are removed, the crash becomes "unreliable" in that executing 
"php5 php-5.2.0-8-segfault.php" will crash sometimes and sometimes it will 
not.

I've "anonymized" my code by replacing alphabetic characters with "x", 
that's why it looks so weird. :)

I'm tagging this "security" as this MAY potentially be a nasty bug which 
might allow more than just segfaults. If you disagree, feel free to remove 
the tag.

Cheers,

  Richard

-- 
  __   _
  |_) /|  Richard Atterer
  | \/¯|  http://geht.net.gibts.bei.atterer.net
  ¯ '` ¯

#8146 0xb7e29c04 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=6, md=0xbf939658, ims=0,
    eptrb=0xbf9319a4, flags=<value optimized out>, rdepth=31) at 
./pcre_exec.c:677
#8147 0xb7e2a5a7 in match (eptr=0xb72ce7a4 ";\n", ecode=<value optimized out>, 
offset_top=6, md=0xbf939658, ims=0, eptrb=0xbf9321a4,
    flags=<value optimized out>, rdepth=30) at ./pcre_exec.c:1190
#8148 0xb7e29c04 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=6, md=0xbf939658, ims=0,
    eptrb=0xbf9321a4, flags=<value optimized out>, rdepth=29) at 
./pcre_exec.c:677
#8149 0xb7e2a5a7 in match (eptr=0xb72ce7a4 ";\n", ecode=<value optimized out>, 
offset_top=6, md=0xbf939658, ims=0, eptrb=0xbf9329a4,
    flags=<value optimized out>, rdepth=28) at ./pcre_exec.c:1190
#8150 0xb7e29c04 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=6, md=0xbf939658, ims=0,
    eptrb=0xbf9329a4, flags=<value optimized out>, rdepth=27) at 
./pcre_exec.c:677
#8151 0xb7e2a5a7 in match (eptr=0xb72ce7a4 ";\n", ecode=<value optimized out>, 
offset_top=6, md=0xbf939658, ims=0, eptrb=0xbf9331a4,
    flags=<value optimized out>, rdepth=26) at ./pcre_exec.c:1190
#8152 0xb7e29c04 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=6, md=0xbf939658, ims=0,
    eptrb=0xbf9331a4, flags=<value optimized out>, rdepth=25) at 
./pcre_exec.c:677
#8153 0xb7e2a5a7 in match (eptr=0xb72ce7a4 ";\n", ecode=<value optimized out>, 
offset_top=6, md=0xbf939658, ims=0, eptrb=0xbf9339a4,
---Type <return> to continue, or q <return> to quit---
    flags=<value optimized out>, rdepth=24) at ./pcre_exec.c:1190
#8154 0xb7e29c04 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=6, md=0xbf939658, ims=0,
    eptrb=0xbf9339a4, flags=<value optimized out>, rdepth=23) at 
./pcre_exec.c:677
#8155 0xb7e2a5a7 in match (eptr=0xb72ce7a4 ";\n", ecode=<value optimized out>, 
offset_top=6, md=0xbf939658, ims=0, eptrb=0xbf9341a4,
    flags=<value optimized out>, rdepth=22) at ./pcre_exec.c:1190
#8156 0xb7e29c04 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=6, md=0xbf939658, ims=0,
    eptrb=0xbf9341a4, flags=<value optimized out>, rdepth=21) at 
./pcre_exec.c:677
#8157 0xb7e2a5a7 in match (eptr=0xb72ce7a4 ";\n", ecode=<value optimized out>, 
offset_top=6, md=0xbf939658, ims=0, eptrb=0xbf9349a4,
    flags=<value optimized out>, rdepth=20) at ./pcre_exec.c:1190
#8158 0xb7e29c04 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=6, md=0xbf939658, ims=0,
    eptrb=0xbf9349a4, flags=<value optimized out>, rdepth=19) at 
./pcre_exec.c:677
#8159 0xb7e2a5a7 in match (eptr=0xb72ce7a4 ";\n", ecode=<value optimized out>, 
offset_top=6, md=0xbf939658, ims=0, eptrb=0xbf9351a4,
    flags=<value optimized out>, rdepth=18) at ./pcre_exec.c:1190
#8160 0xb7e29c04 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=6, md=0xbf939658, ims=0,
    eptrb=0xbf9351a4, flags=<value optimized out>, rdepth=17) at 
./pcre_exec.c:677
#8161 0xb7e2a5a7 in match (eptr=0xb72ce7a4 ";\n", ecode=<value optimized out>, 
offset_top=6, md=0xbf939658, ims=0, eptrb=0xbf9359a4,
    flags=<value optimized out>, rdepth=16) at ./pcre_exec.c:1190
#8162 0xb7e29c04 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=6, md=0xbf939658, ims=0,
    eptrb=0xbf9359a4, flags=<value optimized out>, rdepth=15) at 
./pcre_exec.c:677
#8163 0xb7e2a5a7 in match (eptr=0xb72ce7a4 ";\n", ecode=<value optimized out>, 
offset_top=6, md=0xbf939658, ims=0, eptrb=0xbf9361a4,
    flags=<value optimized out>, rdepth=14) at ./pcre_exec.c:1190
#8164 0xb7e29c04 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=6, md=0xbf939658, ims=0,
    eptrb=0xbf9361a4, flags=<value optimized out>, rdepth=13) at 
./pcre_exec.c:677
#8165 0xb7e2a5a7 in match (eptr=0xb72ce7a4 ";\n", ecode=<value optimized out>, 
offset_top=6, md=0xbf939658, ims=0, eptrb=0xbf9369a4,
    flags=<value optimized out>, rdepth=12) at ./pcre_exec.c:1190
#8166 0xb7e29c04 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=6, md=0xbf939658, ims=0,
    eptrb=0xbf9369a4, flags=<value optimized out>, rdepth=11) at 
./pcre_exec.c:677
#8167 0xb7e2a5a7 in match (eptr=0xb72ce7a4 ";\n", ecode=<value optimized out>, 
offset_top=6, md=0xbf939658, ims=0, eptrb=0xbf9371a4,
    flags=<value optimized out>, rdepth=10) at ./pcre_exec.c:1190
#8168 0xb7e29c04 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=6, md=0xbf939658, ims=0,
    eptrb=0xbf9371a4, flags=<value optimized out>, rdepth=9) at 
./pcre_exec.c:677
#8169 0xb7e2a5a7 in match (eptr=0xb72ce7a4 ";\n", ecode=<value optimized out>, 
offset_top=6, md=0xbf939658, ims=0, eptrb=0xbf9379a4,
    flags=<value optimized out>, rdepth=8) at ./pcre_exec.c:1190
#8170 0xb7e29c04 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=6, md=0xbf939658, ims=0,
    eptrb=0xbf9379a4, flags=<value optimized out>, rdepth=7) at 
./pcre_exec.c:677
#8171 0xb7e2b1d6 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=<value optimized out>, md=0xbf939658,
    ims=0, eptrb=0xbf937da4, flags=<value optimized out>, rdepth=6) at 
./pcre_exec.c:1063
#8172 0xb7e2b1d6 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=<value optimized out>, md=0xbf939658,
    ims=0, eptrb=0xbf9381a4, flags=<value optimized out>, rdepth=5) at 
./pcre_exec.c:1063
#8173 0xb7e2b1d6 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=<value optimized out>, md=0xbf939658,
    ims=0, eptrb=0xbf9385a4, flags=<value optimized out>, rdepth=4) at 
./pcre_exec.c:1063
#8174 0xb7e2e004 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=<value optimized out>, md=0xbf939658,
    ims=0, eptrb=0xbf9395a4, flags=<value optimized out>, rdepth=3) at 
./pcre_exec.c:629
#8175 0xb7e2f269 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=<value optimized out>, md=0xbf939658,
    ims=4, eptrb=0xbf938da4, flags=<value optimized out>, rdepth=2) at 
./pcre_exec.c:2932
---Type <return> to continue, or q <return> to quit---
#8176 0xb7e2e004 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=<value optimized out>, md=0xbf939658,
    ims=0, eptrb=0xbf9395a4, flags=<value optimized out>, rdepth=1) at 
./pcre_exec.c:629
#8177 0xb7e2e004 in match (eptr=<value optimized out>, ecode=<value optimized 
out>, offset_top=<value optimized out>, md=0xbf939658,
    ims=0, eptrb=0xbf9395a4, flags=<value optimized out>, rdepth=0) at 
./pcre_exec.c:629
#8178 0xb7e31af3 in pcre_exec (argument_re=0x8652bf8, extra_data=0xbf9397e4,
    subject=0xb72cd79c "<html>\n<head><?php // -*- php -*-\n/* Output sitemap 
info for directory at $path. Value is site-absolute and\n  must start with a 
slash, so supply \"/\" to output sitemap for whole\n   site. */\nfunction 
/"..., length=4106, start_offset=0,
    options=0, offsets=0xb72cb9d8, offsetcount=12) at ./pcre_exec.c:3851
#8179 0x0809be14 in php_pcre_match_impl (pce=0x8652ee8,
    subject=0xb72cd79c "<html>\n<head><?php // -*- php -*-\n/* Output sitemap 
info for directory at $path. Value is site-absolute and\n  must start with a 
slash, so supply \"/\" to output sitemap for whole\n   site. */\nfunction 
/"..., subject_len=4106,
    return_value=0xb72cb8fc, subpats=0xb72cb8e4, global=0, use_flags=0, 
flags=0, start_offset=0)
    at /home/richard/deb/php-5.2.0/ext/pcre/php_pcre.c:604
#8180 0x0809c94a in php_do_pcre_match (ht=3, return_value=0xb72cb8fc, 
return_value_ptr=0x6, this_ptr=0x0, return_value_used=0, global=0)
    at /home/richard/deb/php-5.2.0/ext/pcre/php_pcre.c:462
#8181 0x082cf83f in zend_do_fcall_common_helper_SPEC (execute_data=0xbf9399ec) 
at /home/richard/deb/php-5.2.0/Zend/zend_vm_execute.h:200
#8182 0x082bf238 in execute (op_array=0xb72cb104) at 
/home/richard/deb/php-5.2.0/Zend/zend_vm_execute.h:92
#8183 0x082a040c in zend_execute_scripts (type=8, retval=0x0, file_count=3) at 
/home/richard/deb/php-5.2.0/Zend/zend.c:1097
#8184 0x0825b6e2 in php_execute_script (primary_file=0xbf93be20) at 
/home/richard/deb/php-5.2.0/main/main.c:1758
#8185 0x0832f5ae in main (argc=2, argv=0xbf93bef4) at 
/home/richard/deb/php-5.2.0/sapi/cli/php_cli.c:1108

Attachment: php-5.2.0-8-segfault.php
Description: application/httpd-php

// gcc -lpcre -g -O2 -Wall -o pcre-segfault pcre-segfault.c && ./pcre-segfault
// This does _not_ crash for me

#include <pcre.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static const char* subject = "<xxxx>\n"
"<xxxx><?xxx // -*- xxx -*-\n"
"/* xxxxxx xxxxxxx xxxx xxx xxxxxxxxx xx $xxxx. xxxxx xx xxxx-xxxxxxxx xxx\n"
"   xxxx xxxxx xxxx x xxxxx, xx xxxxxx \"/\" xx xxxxxx xxxxxxx xxx xxxxx\n"
"   xxxx. */\n"
"xxxxxxxx /*xxxx*/ xxxxxxx(/*xxxxxx*/ $xxx, $xxxxxx = '') {\n"
"  xxxx \"<xx>xxxxxxxxx: $xxx</xx>\\x\";\n"
"  // xxxx xxxxxxxxx xxxxxxxx  xx ($xxx == '/') $xxx = '';\n"
"  $xx = xxxxxxx(xxxxx . $xxx); xx (!$xx) xxxxxx;\n"
"  $xxxxxxxxx = xxxxx; $xxxxx = xxxxx();\n"
"  xxxxx ($xx && xxxxx !== ($x = xxxxxxx($xx))) {\n"
"    xx ($x{0} == '.' || xx_xxxx(xxxxx . \"$xxx/$x\")) xxxxxxxx;\n"
"    xx (xx_xxx(xxxxx . \"$xxx/$x\")) {\n"
"      $xxxxx[] = $x;\n"
"      xxxxxxxx;\n"
"    }\n"
"    xx (xxxxxx($x, -22) != '.xxxxx_xxxxxxx.xxxxxxx'\n"
"        && xxxxxx($x, -26) != '.xxxxx-xxx_xxxxxxx.xxxxxxx')\n"
"      xxxxxxxx;\n"
"    xx (xxxxxx($x, 0, 6) != 'xxxxx.') {\n"
"      $xxxxx[] = $x;\n"
"      xxxxxxxx;\n"
"    }\n"
"    // xxxxx xxxx xxxxxxx, xxxxxx x <xx> xxx xx\n"
"    xxxx $xxxxxx, '<xx>', xxxxxxxxxxx(\"$xxx/$x\", \"$xxx/\");\n"
"    $xxxxxxxxx = xxxx;\n"
"  }\n"
"  xxxxxxxx($xx);\n"
"\n"
"  xx (xxxxx($xxxxx)) {\n"
"    xx ($xxxxxxxxx) xxxx \"</xx>\\x\";\n"
"    xxxxxx;\n"
"  }\n"
"  xx ($xxxxxxxxx) xxxx \"<xx/>\\x\";\n"
"\n"
"  // xxx xxxxxxxxx xxxxxxxx xxxxxxx xxxxx, xxxxxx xxxxxxxxxxx xxx xxxx\n"
"  xxxx $xxxxxx, \"<xx xxxxx=\\\"xxxxxxx\\\">\\x\";\n"
"  xxxx($xxxxx);\n"
"  xxxxxxx ($xxxxx xx $x) {\n"
"    xx (xx_xxx(xxxxx . \"$xxx/$x\"))\n"
"      xxxxxxx(\"$xxx/$x\", \"$xxxxxx  \");\n"
"    xxxx\n"
"      xxxx $xxxxxx, '  <xx>', xxxxxxxxxxx(\"$xxx/$x\"), \"</xx>\\x\";\n"
"  }\n"
"  xxxx $xxxxxx, \"</xx>\\x\";\n"
"  xx ($xxxxxxxxx) xxxx $xxxxxx, \"</xx>\\x\";\n"
"}\n"
"//______________________________________________________________________\n"
"\n"
"/* xxxxxx x xxxxxx xxxx xx xxxxxx xxx xxx $xxxxxxxx. $xxxxxxxx xxxx xx x\n"
"   _xxxxxxx.xxxxxxx xxxx. xx xxx xxxx xxxx xx xxx xxxxxx, xxxx xxx xxxxxxxx\n"
"   xx xxx xxxxx xxx xx xxx xxxxxxxxx xxxxxxxx xxxxxxxx. xxx xxxxxx xxxxxx\n"
"   xxxx xx $xxxxxxxx xxxx xxx \"_xxxxxxx.xxxxxxx\" xxxxxxx, xxxxxx xxxx xxx\n"
"   xxxxxxxx xxxx xx xxxxxxxxxx x $xxxx. */\n"
"xxxxxxxx /*xxxxxx*/ xxxxxxxxxxx(/*xxxxxx*/ $xxxxxxxx, $xxxx = xxxxx) {\n"
"  $xxxx = xxxxxx($xxxxxxxx, 0, -16); // xxxxxx \"_xxxxxxx.xxxxxxx\" xxxx xxx\n"
"  xxxx(\"xxxxxxxxxxxxx $xxxx\");\n"
"  xx ($xxxx === xxxxx) {\n"
"    $xxxx = $xxxx;\n"
"    xx (xxxxxx($xxxx, -16) == '.xxxxx.xxxxx-xxx')\n"
"      $xxxx = xxxxxx($xxxx, 0, -10); // xxxxxx .xxxxx-xxx xx xxxxxxxx xx .xxxxx\n"
"  }\n"
"\n"
"  /* xxxx .xxxxxxx xxxxxxxx. xx xxxx xxx xxxxxxxxx xxxxx xx xxxxx:\n"
"     xxxxxxx /xxx/xxxxx.xxxxx.xx.xxxxx\n"
"     xxxxxxxxx /xxx/xxxxx.xxxxx.xxxxx */\n"
"  xxxxxx $xxxxxxxxx = xxxx;\n"
"  xx ($xxxxxxxxx === xxxx) $xxxxxxxxx = xxxxx_xxxx(xxxxxxx(' ', xxxxxxxxx));\n"
"  $xxxxxxxx = '';\n"
"  $xxxxxxx = xxxxxxxxxxxx_xxxxxxxxxxx(xxxxx . $xxxxxxxx);\n"
"  xxxxxxx ($xxxxxxx xx $xxxx => $xxxxx) {\n"
"    xxxx(\"xx $xxxx => $xxxxx\");\n"
"    xx (xxxxxx($xxxx, 0, xxxxxx($xxxx)) != $xxxx) xxxxxxxx;\n"
"    $xxxxxxxxxx = xxxxxx($xxxx, xxxxxx($xxxx));\n"
"    xxxx(\"xx $xxxx => $xxxxx  '$xxxxxxxxxx'\");\n"
"    xx ($xxxxx == 'xxxxxxxxx' && $xxxxxxxxxx == '.xxxx') {\n"
"      // xxxxxx-xxxxxxxx xxxx xx xxxxxx: xxxx xxxxx xxx <xxxxx> xxxxxxxx\n"
"      $xxxxxxxx = xxxxxxxxxxxx($xxxx);\n"
"      xxxxx;\n"
"    } xxxx xx ($xxxxx == 'xxxxxxx' && $xxxxxxxxxx{0} == '.'\n"
"               && xxxxxx($xxxxxxxxxx, -5) == '.xxxx') {\n"
"      /* xxxxx-xxxxxxxx: xxxxxxx xxx xxxxxxxxxx xxxxxxxxx' xxxx xxxx xxx\n"
"         xxxxxxxxx xxxxxxxx xxxxxxxx */\n"
"      $xxxx = xxxxxx($xxxxxxxxxx, 1, -5);\n"
"      xx (xxxxx_xxx_xxxxxx($xxxx, $xxxxxxxxx))\n"
"        $xxxxxxxx .= \"<$xxxx>\" . xxxxxxxxxxxx($xxxx) . \"</$xxxx>\";\n"
"      xxxx(\"   xxxx='$xxxx'  $xxxxxxxx\");\n"
"    }\n"
"  }\n"
"\n"
"  $xxxxx = xxxxxxxxxxxxxxxx($xxxx);\n"
"  xx ($xxxxxxxx == '') $xxxxxxxx = $xxxxx;\n"
"  xxxxxx \"<x xxxx=\\\"/~xxxxxxx/xx$xxxxx\\\">$xxxxxxxx</x>\";\n"
"\n"
"  //  xxxxxx \"<x xxxx=\\\"/~xxxxxxx/xx$xxxxx\\\">$xxxxx</x>\";\n"
"}\n"
"//______________________________________________________________________\n"
"\n"
"xxxxxxxx xxxxxxxxxxxx(/*xxxxxx*/ $xxxxxxxx) {\n"
"  $xxxx = xxxx_xxx_xxxxxxxx(xxxxx . $xxxxxxxx);\n"
"  xx (xxxx_xxxxx('%<xxxxx>(.*)</xxxxx>%', $xxxx, $x) == 1)\n"
"    xxxxxx xxxxxxxxxxxxxxxx($xxxxxxxx);\n"
"  xxxxxx xxxxxxxxxxxxxxxx($xxxxxxxx);\n"
"}\n"
"//______________________________________________________________________\n"
"\n"
"xxxxxx_xxxxxxxxxxxxx($xxxx['xxxx'], '/xxxxx.xxxxxxx');\n"
"xxxx \"<xx>\\x\";\n"
"xxxxxxx('/');\n"
"xxxx \"</xx>\\x\";\n";

static const char* pattern = "%^((?:<\\?(?:php)?\\s(?:[^?]|\\?(?!>))*\\?>\\n?)*)((?Us).*)((?:<\\?(?:php)?\\s(?:[^?]|\\?(?!>))*\\?>\\n?)*)$%";

int main() {
  const char* errPtr = 0;
  int errOff = 0;
  int ovector[100];
  unsigned const char *tables = pcre_maketables();
  pcre* re = pcre_compile(pattern, 0/*options*/, &errPtr, &errOff, tables);
  if (re == NULL) printf("compile failed: %s\n", errPtr);
  pcre_exec(re, NULL, subject, strlen(subject), 0/*off*/, 0/*opt*/, ovector,
            100);

  printf("OK\n");
  return 0;
}

Reply via email to