Bug #77193 [Asn]: Infinite loop in preg_replace_callback

From: Date: Fri, 30 Nov 2018 16:41:16 +0000
Subject: Bug #77193 [Asn]: Infinite loop in preg_replace_callback
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-218230@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=77193&edit=1

 ID:                 77193
 User updated by:    mlocati at gmail dot com
 Reported by:        mlocati at gmail dot com
 Summary:            Infinite loop in preg_replace_callback
 Status:             Assigned
 Type:               Bug
 Package:            PCRE related
 PHP Version:        7.3.0RC6
 Assigned To:        ab
 Block user comment: N
 Private report:     N

 New Comment:

It took me quite some time to keep removing stuff to get to a test script as simple as possible, and
here it is:

<?php
$text = '{CCM:CID_2}';
echo '1';
preg_replace_callback(
    '/([0-9]+)/i',
    function ($matches) {
        echo $matches[1];
        filter_var('http', FILTER_VALIDATE_REGEXP, ['options' =>
['regexp' => '/^http$/i']]);
    },
    $text
);
echo '3';


It should print '123', but it prints '1' and an infinite number of
'2'.

If you set $text to just a digit (eg '2'), just '12' is printed out, and PHP
quits with an error level -1073741819 (I'm using a 32-bit PHP).


Previous Comments:
------------------------------------------------------------------------
[2018-11-30 15:07:00] ab@php.net

Thanks for testing, Michele.

Chistoph, I'll be preparing a patch then. Still lack on a simpler repro case :/

Thanks

------------------------------------------------------------------------
[2018-11-29 22:54:34] cmb@php.net

Thanks for testing, Michele!  Then we should go with Anatol's
patch.  Not sure if it should go into PHP-7.3.0.

------------------------------------------------------------------------
[2018-11-29 13:39:06] mlocati at gmail dot com

Just for the records, I'm compiling a 32-bit PHP under Windows with

configure --disable-all --with-all-shared --enable-cli --enable-phar --enable-json --enable-filter
--with-iconv --enable-pdo --with-mysqli --with-mysqlnd --with-pdo-mysql --with-openssl
--with-simplexml --with-libxml --with-gd --enable-mbstring --enable-tokenizer --with-dom --with-xml
--enable-xmlreader --enable-xmlwriter --enable-hash  --enable-fileinfo --enable-session --enable-zip

nmake

------------------------------------------------------------------------
[2018-11-29 13:33:56] mlocati at gmail dot com

@cmb I just tried your patch instead of the @ab one, but with it we still have the infinite loop...

------------------------------------------------------------------------
[2018-11-29 11:34:17] cmb@php.net

I wonder whether we don't have to call pcre2_get_mark() before
calling pcre2_get_ovector_pointer(), i.e. partly reverting commit
b81d712.

I mean something like this instead the patch above:

 ext/pcre/php_pcre.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/ext/pcre/php_pcre.c b/ext/pcre/php_pcre.c
index 5165209b85..4277ffb6aa 100644
--- a/ext/pcre/php_pcre.c
+++ b/ext/pcre/php_pcre.c
@@ -1792,6 +1792,7 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce,
zend_strin
 	char			*match,				/* The current match */
 					*piece;				/* The current piece of subject */
 	size_t			result_len; 		/* Length of result */
+	PCRE2_SPTR		mark = NULL;		/* Target for MARK name */
 	zend_string		*result;			/* Result of replacement */
 	zend_string     *eval_result;		/* Result of custom function */
 	pcre2_match_data *match_data;
@@ -1852,6 +1853,8 @@ static zend_string *php_pcre_replace_func_impl(pcre_cache_entry *pce,
zend_strin
 	while (1) {
 		piece = subject + start_offset;
 
+		mark = pcre2_get_mark(match_data);
+
 		if (count >= 0 && limit) {
 			/* Check for too many substrings condition. */
 			if (UNEXPECTED(count == 0)) {
@@ -1881,8 +1884,7 @@ matched:
 			new_len = result_len + offsets[0] - start_offset; /* part before the match */
 
 			/* Use custom function to get replacement string and its length. */
-			eval_result = preg_do_repl_func(fci, fcc, subject, offsets, subpat_names, count,
-				pcre2_get_mark(match_data));
+			eval_result = preg_do_repl_func(fci, fcc, subject, offsets, subpat_names, count, mark);
 
 			ZEND_ASSERT(eval_result);
 			new_len = zend_safe_address_guarded(1, ZSTR_LEN(eval_result), new_len);


[1] <http://git.php.net/?p=php-src.git;a=commit;h=b81d712961aa3cbc64dc8bb521f2427cf443e550>

------------------------------------------------------------------------


The remainder of the comments for this report are too long. To view
the rest of the comments, please view the bug report online at

    https://bugs.php.net/bug.php?id=77193


--
Edit this bug report at https://bugs.php.net/bug.php?id=77193&edit=1


Thread (22 messages)

« previous php.bugs (#218230) next »