php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #63455 SIGSEGV with preg_match
Submitted: 2012-11-07 14:13 UTC Modified: 2012-11-07 23:09 UTC
From: jakub dot galczyk at gmail dot com Assigned:
Status: Not a bug Package: PCRE related
PHP Version: 5.4.8 OS: Ubuntu
Private report: No CVE-ID: None
Welcome back! If you're the original bug submitter, here's where you can edit the bug or add additional notes.
If you forgot your password, you can retrieve your password here.
Password:
Status:
Package:
Bug Type:
Summary:
From: jakub dot galczyk at gmail dot com
New email:
PHP Version: OS:

 

 [2012-11-07 14:13 UTC] jakub dot galczyk at gmail dot com
Description:
------------
I was checking one bug in CMS (found by someone else) and accidently there was a SIGSEGV ;]



Test script:
---------------
Exploit code ('script to test') is here:
http://www.exploit-db.com/exploits/15369/

CMS (I saw that we need to have this CMS in /wwwroot) to test:
http://www.geardownload.com/webdevelopment/auto-cms-download.html

(Below I added a little description grepped from .c file, gdb and valgrind.

Expected result:
----------------
No sigsegv? ;)

(and shell output from this sploit for autocms wroted by giudinvx)


Actual result:
--------------
kuba@box:~/src/php-5.4.8$ /usr/local/bin/php -v
PHP 5.4.8 (cli) (built: Nov  7 2012 13:36:10)
Copyright (c) 1997-2012 The PHP Group
Zend Engine v2.4.0, Copyright (c) 1998-2012 Zend Technologies
kuba@box:~/src/php-5.4.8$ /usr/local/bin/php ../../public_html/spl.php localhost /

Auto CMS <= 1.8 Remote Code Execution
Exploit by giudinvx
ShellCMD
WHATEVERGOESHERE:*:*:*
Segmentation fault (core dumped)
kuba@box:~/src/php-5.4.8$

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-11-07 14:26 UTC] pajoye@php.net
Increase the stack and the problem should go away.

Actually the reproduce script could be reduce to:

preg_match("/(\n|.)*/i", $res, $match);

with the content of $res and $match being previously set.
 [2012-11-07 14:34 UTC] jakub dot galczyk at gmail dot com
More info:
                                       
(gdb) bt
(...)
#21790 0x080b8f3b in php_pcre_exec (argument_re=0x4657380, extra_data=0xbe9e6ed4,
    subject=0x43c3abc "HTTP/1.1 302 Found\r\nDate: Wed, 07 Nov 2012 13:14:24 GMT\r\nServer: Apache/2.2.22 (Ubuntu)\r\nX-Powered-By: PHP/5.3.10-1ubuntu3.4\r\nCache-Control: no-cache, must-revalidate\r\nExpires: Sat, 26 Jul 1997 05:00:"..., length=11527, start_offset=0, options=0, offsets=0x43bad0c, offsetcount=6)
    at /home/kuba/src/php-5.4.8/ext/pcre/pcrelib/pcre_exec.c:6098
#21791 0x080bd6cd in php_pcre_match_impl (pce=0x46574d8,
    subject=0x43c3abc "HTTP/1.1 302 Found\r\nDate: Wed, 07 Nov 2012 13:14:24 GMT\r\nServer: Apache/2.2.22 (Ubuntu)\r\nX-Powered-By: PHP/5.3.10-1ubuntu3.4\r\nCache-Control: ---Type <return> to continue, or q <return> to quit---
no-cache, must-revalidate\r\nExpires: Sat, 26 Jul 1997 05:00:"..., subject_len=11527, return_value=0x43c20a4, subpats=0x43bd784, global=0, use_flags=0, flags=0,
    start_offset=0) at /home/kuba/src/php-5.4.8/ext/pcre/php_pcre.c:634

#21792 0x080be13d in php_do_pcre_match (ht=3, return_value=0x43c20a4, global=0, return_value_ptr=<optimized out>, this_ptr=<optimized out>,
    return_value_used=<optimized out>) at /home/kuba/src/php-5.4.8/ext/pcre/php_pcre.c:528
#21793 0x08374d58 in zend_do_fcall_common_helper_SPEC (execute_data=<optimized out>) at /home/kuba/src/php-5.4.8/Zend/zend_vm_execute.h:642
#21794 0x08337ced in execute (op_array=<optimized out>) at /home/kuba/src/php-5.4.8/Zend/zend_vm_execute.h:410
#21795 0x082d9a93 in zend_execute_scripts (type=8, retval=0x0, file_count=3) at /home/kuba/src/php-5.4.8/Zend/zend.c:1309
#21796 0x0827a6b2 in php_execute_script (primary_file=0xbe9e93dc) at /home/kuba/src/php-5.4.8/main/main.c:2482
#21797 0x0837749e in do_cli (argc=4, argv=0xbe9ea664) at /home/kuba/src/php-5.4.8/sapi/cli/php_cli.c:988
#21798 0x08067254 in main (argc=4, argv=0xbe9ea664) at /home/kuba/src/php-5.4.8/sapi/cli/php_cli.c:1364
(gdb)
(gdb)
(gdb) x/10i 0x80a99bb
=> 0x80a99bb <match+5787>:      mov    %edi,0x8(%esp)
   0x80a99bf <match+5791>:      mov    %ebp,0x14(%esp)
   0x80a99c3 <match+5795>:      mov    0x40(%esp),%ebp
   0x80a99c7 <match+5799>:      movzbl 0x83e2dc0(%eax),%eax
   0x80a99ce <match+5806>:      mov    %ebp,0x10(%esp)
   0x80a99d2 <match+5810>:      mov    0x3c(%esp),%ebp
   0x80a99d6 <match+5814>:      lea    (%esi,%eax,1),%edx
   0x80a99d9 <match+5817>:      mov    0x30(%esp),%eax
   0x80a99dd <match+5821>:      mov    %ebp,0xc(%esp)
   0x80a99e1 <match+5825>:      mov    0x184(%esp),%ebp
(gdb) p $1
History has not yet reached $1.
(gdb) p $eip
$1 = (void (*)()) 0x80a99bb <match+5787>
(gdb) i r
eax            0x43bad20        71019808
ecx            0x43c3abc        71056060
edx            0x2a8e   10894
ebx            0x43c654a        71066954
esp            0xbe1ebfd0       0xbe1ebfd0
ebp            0x0      0x0
esi            0x46573ac        73757612
edi            0xbe9e6d80       -1096913536
eip            0x80a99bb        0x80a99bb <match+5787>
eflags         0x4      [ PF ]
cs             0x73     115
ss             0x7b     123
ds             0x7b     123
es             0x0      0
fs             0x0      0
gs             0xb      11
(gdb) list
817             (int)(eptr - md->start_subject);
818
819           flags = (op == OP_SCBRA)? match_cbegroup : 0;
820           do
821             {
822             RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md,
823               ims, eptrb, flags, RM1);
824             if (rrc != MATCH_NOMATCH &&
825                 (rrc != MATCH_THEN || md->start_match_ptr != ecode))
826               RRETURN(rrc);
(gdb)
827             md->capture_last = save_capture_last;
828             ecode += GET(ecode, 1);
829             }
830           while (*ecode == OP_ALT);
831
832           DPRINTF(("bracket %d failed\n", number));
833
834           md->offset_vector[offset] = save_offset1;
835           md->offset_vector[offset+1] = save_offset2;
836           md->offset_vector[md->offset_end - number] = save_offset3;
(gdb) \q
kuba@box:~/src/php-5.4.8$ grep -n -r -e RMATCH ./
(...)
kuba@box:~/src/php-5.4.8$ vim ext/pcre/pcrelib/pcre_exec.c
/RMATCH
(...)
line 235:
"The original heap-recursive code used longjmp(). However, it seems that this
can be very slow on some operating systems. Following a suggestion from Stan
Switzer, the use of longjmp() has been abolished, at the cost of having to
provide a unique number for each call to RMATCH. There is no way of generating
a sequence of numbers at compile time in C. I have given them names, to make
them stand out more clearly."
(...)
"/* These versions of the macros use the stack, as normal. There are debugging
versions and production versions. Note that the "rw" argument of RMATCH isn't
actually used in this definition. */

#ifndef NO_RECURSE
#define REGISTER register

#ifdef PCRE_DEBUG
#define RMATCH(ra,rb,rc,rd,re,rf,rg,rw) \
" 
(...)
"
/* These versions of the macros manage a private stack on the heap. Note that
the "rd" argument of RMATCH isn't actually used in this definition. It's the md
argument of match(), which never changes. */"

No to ja go chyba troche zmieniƂem ;]

"register int  i;           /* Used for loops not involving calls to RMATCH() */"
"
/* OK, now we can get on with the real code of the function. Recursive calls
are specified by the macro RMATCH and RRETURN is used to return. When
NO_RECURSE is *not* defined, these just turn into a recursive call to match()
and a "return", respectively (possibly with some debugging if PCRE_DEBUG is
defined). However, RMATCH isn't like a function call because it's quite a
complicated macro. It has to be used in one particular way. This shouldn't,
however, impact performance when true recursion is being used. */"

818:833:
(...)
      flags = (op == OP_SCBRA)? match_cbegroup : 0;
      do
        {
==>        RMATCH(eptr, ecode + _pcre_OP_lengths[*ecode], offset_top, md, ims, eptrb, flags, RM1);
        if (rrc != MATCH_NOMATCH &&
            (rrc != MATCH_THEN || md->start_match_ptr != ecode))
          RRETURN(rrc);
        md->capture_last = save_capture_last;
        ecode += GET(ecode, 1);
        }
      while (*ecode == OP_ALT);

      DPRINTF(("bracket %d failed\n", number));
(...)
 [2012-11-07 14:37 UTC] tony2001@php.net
-Status: Open +Status: Not a bug -Package: *Math Functions +Package: PCRE related
 [2012-11-07 14:37 UTC] tony2001@php.net
This is PCRE overflowing the stack due to an (endless?) recursion in your regular 
expression.
It's a well known PCRE problem and solutions to it are described here:
http://manpages.courier-mta.org/htmlman3/pcrestack.3.html
and here:
http://pcre.org/pcre.txt (look for "STACK USAGE")
 [2012-11-07 23:09 UTC] sixd@php.net
-Summary: SIGSEGV +Summary: SIGSEGV with preg_match
 
PHP Copyright © 2001-2025 The PHP Group
All rights reserved.
Last updated: Thu Jul 03 15:01:34 2025 UTC