Skip to content

Commit bc8f292

Browse files
committed
Merge branch 'mb-limit-73' into PHP-7.3
* mb-limit-73: Add fallbacks for older oniguruma versions Add mbstring.regex_stack_limit to php.ini-* Implement RF bug #72777 - ensure stack limits on mbstring functions.
2 parents d2a86e1 + e12c069 commit bc8f292

File tree

7 files changed

+127
-8
lines changed

7 files changed

+127
-8
lines changed

ext/mbstring/mbstring.c

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,17 @@
6565
#include "php_onig_compat.h"
6666
#include <oniguruma.h>
6767
#undef UChar
68+
#if ONIGURUMA_VERSION_INT < 60800
69+
typedef void OnigMatchParam;
70+
#define onig_new_match_param() (NULL)
71+
#define onig_initialize_match_param(x)
72+
#define onig_set_match_stack_limit_size_of_match_param(x, y)
73+
#define onig_free_match_param(x)
74+
#define onig_search_with_param(reg, str, end, start, range, region, option, mp) \
75+
onig_search(reg, str, end, start, range, region, option)
76+
#define onig_match_with_param(re, str, end, at, region, option, mp) \
77+
onig_match(re, str, end, at, region, option)
78+
#endif
6879
#elif HAVE_PCRE || HAVE_BUNDLED_PCRE
6980
#include "ext/pcre/php_pcre.h"
7081
#endif
@@ -1027,9 +1038,18 @@ static void *_php_mb_compile_regex(const char *pattern)
10271038
/* {{{ _php_mb_match_regex */
10281039
static int _php_mb_match_regex(void *opaque, const char *str, size_t str_len)
10291040
{
1030-
return onig_search((php_mb_regex_t *)opaque, (const OnigUChar *)str,
1031-
(const OnigUChar*)str + str_len, (const OnigUChar *)str,
1032-
(const OnigUChar*)str + str_len, NULL, ONIG_OPTION_NONE) >= 0;
1041+
OnigMatchParam *mp = onig_new_match_param();
1042+
int err;
1043+
onig_initialize_match_param(mp);
1044+
if (!ZEND_LONG_UINT_OVFL(MBSTRG(regex_stack_limit))) {
1045+
onig_set_match_stack_limit_size_of_match_param(mp, (unsigned int)MBSTRG(regex_stack_limit));
1046+
}
1047+
/* search */
1048+
err = onig_search_with_param((php_mb_regex_t *)opaque, (const OnigUChar *)str,
1049+
(const OnigUChar*)str + str_len, (const OnigUChar *)str,
1050+
(const OnigUChar*)str + str_len, NULL, ONIG_OPTION_NONE, mp);
1051+
onig_free_match_param(mp);
1052+
return err >= 0;
10331053
}
10341054
/* }}} */
10351055

@@ -1502,6 +1522,9 @@ PHP_INI_BEGIN()
15021522
PHP_INI_ALL,
15031523
OnUpdateBool,
15041524
strict_detection, zend_mbstring_globals, mbstring_globals)
1525+
#if HAVE_MBREGEX
1526+
STD_PHP_INI_ENTRY("mbstring.regex_stack_limit", "100000",PHP_INI_ALL, OnUpdateLong, regex_stack_limit, zend_mbstring_globals, mbstring_globals)
1527+
#endif
15051528
PHP_INI_END()
15061529
/* }}} */
15071530

ext/mbstring/mbstring.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ ZEND_BEGIN_MODULE_GLOBALS(mbstring)
166166
void *http_output_conv_mimetypes;
167167
#if HAVE_MBREGEX
168168
struct _zend_mb_regex_globals *mb_regex_globals;
169+
zend_long regex_stack_limit;
169170
#endif
170171
char *last_used_encoding_name;
171172
const mbfl_encoding *last_used_encoding;

ext/mbstring/php_mbregex.c

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,18 @@
3434
#include <oniguruma.h>
3535
#undef UChar
3636

37+
#if ONIGURUMA_VERSION_INT < 60800
38+
typedef void OnigMatchParam;
39+
#define onig_new_match_param() (NULL)
40+
#define onig_initialize_match_param(x)
41+
#define onig_set_match_stack_limit_size_of_match_param(x, y)
42+
#define onig_free_match_param(x)
43+
#define onig_search_with_param(reg, str, end, start, range, region, option, mp) \
44+
onig_search(reg, str, end, start, range, region, option)
45+
#define onig_match_with_param(re, str, end, at, region, option, mp) \
46+
onig_match(re, str, end, at, region, option)
47+
#endif
48+
3749
ZEND_EXTERN_MODULE_GLOBALS(mbstring)
3850

3951
struct _zend_mb_regex_globals {
@@ -854,6 +866,23 @@ PHP_FUNCTION(mb_regex_encoding)
854866
}
855867
/* }}} */
856868

869+
/* {{{ _php_mb_onig_search */
870+
static int _php_mb_onig_search(regex_t* reg, const OnigUChar* str, const OnigUChar* end, const OnigUChar* start,
871+
const OnigUChar* range, OnigRegion* region, OnigOptionType option) {
872+
OnigMatchParam *mp = onig_new_match_param();
873+
int err;
874+
onig_initialize_match_param(mp);
875+
if (!ZEND_LONG_UINT_OVFL(MBSTRG(regex_stack_limit))) {
876+
onig_set_match_stack_limit_size_of_match_param(mp, (unsigned int)MBSTRG(regex_stack_limit));
877+
}
878+
/* search */
879+
err = onig_search_with_param(reg, str, end, start, range, region, option, mp);
880+
onig_free_match_param(mp);
881+
return err;
882+
}
883+
/* }}} */
884+
885+
857886
/* {{{ _php_mb_regex_ereg_exec */
858887
static void _php_mb_regex_ereg_exec(INTERNAL_FUNCTION_PARAMETERS, int icase)
859888
{
@@ -913,7 +942,7 @@ static void _php_mb_regex_ereg_exec(INTERNAL_FUNCTION_PARAMETERS, int icase)
913942
regs = onig_region_new();
914943

915944
/* actually execute the regular expression */
916-
if (onig_search(re, (OnigUChar *)string, (OnigUChar *)(string + string_len), (OnigUChar *)string, (OnigUChar *)(string + string_len), regs, 0) < 0) {
945+
if (_php_mb_onig_search(re, (OnigUChar *)string, (OnigUChar *)(string + string_len), (OnigUChar *)string, (OnigUChar *)(string + string_len), regs, 0) < 0) {
917946
RETVAL_FALSE;
918947
goto out;
919948
}
@@ -1090,7 +1119,7 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
10901119
string_lim = (OnigUChar*)(string + string_len);
10911120
regs = onig_region_new();
10921121
while (err >= 0) {
1093-
err = onig_search(re, (OnigUChar *)string, (OnigUChar *)string_lim, pos, (OnigUChar *)string_lim, regs, 0);
1122+
err = _php_mb_onig_search(re, (OnigUChar *)string, (OnigUChar *)string_lim, pos, (OnigUChar *)string_lim, regs, 0);
10941123
if (err <= -2) {
10951124
OnigUChar err_str[ONIG_MAX_ERROR_MESSAGE_LEN];
10961125
onig_error_code_to_str(err_str, err);
@@ -1271,7 +1300,7 @@ PHP_FUNCTION(mb_split)
12711300
/* churn through str, generating array entries as we go */
12721301
while (count != 0 && (size_t)(pos - (OnigUChar *)string) < string_len) {
12731302
size_t beg, end;
1274-
err = onig_search(re, (OnigUChar *)string, (OnigUChar *)(string + string_len), pos, (OnigUChar *)(string + string_len), regs, 0);
1303+
err = _php_mb_onig_search(re, (OnigUChar *)string, (OnigUChar *)(string + string_len), pos, (OnigUChar *)(string + string_len), regs, 0);
12751304
if (err < 0) {
12761305
break;
12771306
}
@@ -1328,6 +1357,7 @@ PHP_FUNCTION(mb_ereg_match)
13281357
OnigSyntaxType *syntax;
13291358
OnigOptionType option = 0;
13301359
int err;
1360+
OnigMatchParam *mp;
13311361

13321362
{
13331363
char *option_str = NULL;
@@ -1356,8 +1386,14 @@ PHP_FUNCTION(mb_ereg_match)
13561386
RETURN_FALSE;
13571387
}
13581388

1389+
mp = onig_new_match_param();
1390+
onig_initialize_match_param(mp);
1391+
if(MBSTRG(regex_stack_limit) > 0 && MBSTRG(regex_stack_limit) < UINT_MAX) {
1392+
onig_set_match_stack_limit_size_of_match_param(mp, (unsigned int)MBSTRG(regex_stack_limit));
1393+
}
13591394
/* match */
1360-
err = onig_match(re, (OnigUChar *)string, (OnigUChar *)(string + string_len), (OnigUChar *)string, NULL, 0);
1395+
err = onig_match_with_param(re, (OnigUChar *)string, (OnigUChar *)(string + string_len), (OnigUChar *)string, NULL, 0, mp);
1396+
onig_free_match_param(mp);
13611397
if (err >= 0) {
13621398
RETVAL_TRUE;
13631399
} else {
@@ -1420,7 +1456,7 @@ _php_mb_regex_ereg_search_exec(INTERNAL_FUNCTION_PARAMETERS, int mode)
14201456
}
14211457
MBREX(search_regs) = onig_region_new();
14221458

1423-
err = onig_search(MBREX(search_re), str, str + len, str + pos, str + len, MBREX(search_regs), 0);
1459+
err = _php_mb_onig_search(MBREX(search_re), str, str + len, str + pos, str + len, MBREX(search_regs), 0);
14241460
if (err == ONIG_MISMATCH) {
14251461
MBREX(search_pos) = len;
14261462
RETVAL_FALSE;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
--TEST--
2+
Test oniguruma stack limit
3+
--SKIPIF--
4+
<?php extension_loaded('mbstring') or die('skip mbstring not available'); ?>
5+
--FILE--
6+
<?php
7+
$s = str_repeat(' ', 30000);
8+
9+
ini_set('mbstring.regex_stack_limit', 10000);
10+
var_dump(mb_ereg('\\s+$', $s));
11+
12+
ini_set('mbstring.regex_stack_limit', 30000);
13+
var_dump(mb_ereg('\\s+$', $s));
14+
15+
ini_set('mbstring.regex_stack_limit', 30001);
16+
var_dump(mb_ereg('\\s+$', $s));
17+
18+
echo 'OK';
19+
?>
20+
--EXPECT--
21+
bool(false)
22+
bool(false)
23+
int(1)
24+
OK
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
--TEST--
2+
Test oniguruma stack limit
3+
--SKIPIF--
4+
<?php extension_loaded('mbstring') or die('skip mbstring not available'); ?>
5+
--FILE--
6+
<?php
7+
function mb_trim( $string, $chars = "", $chars_array = array() )
8+
{
9+
for( $x=0; $x<iconv_strlen( $chars ); $x++ ) $chars_array[] = preg_quote( iconv_substr( $chars, $x, 1 ) );
10+
$encoded_char_list = implode( "|", array_merge( array( "\s","\t","\n","\r", "\0", "\x0B" ), $chars_array ) );
11+
12+
$string = mb_ereg_replace( "^($encoded_char_list)*", "", $string );
13+
$string = mb_ereg_replace( "($encoded_char_list)*$", "", $string );
14+
return $string;
15+
}
16+
17+
ini_set('mbstring.regex_stack_limit', 10000);
18+
var_dump(mb_trim(str_repeat(' ', 10000)));
19+
20+
echo 'OK';
21+
?>
22+
--EXPECTF--
23+
Warning: mb_ereg_replace(): mbregex search failure in php_mbereg_replace_exec(): match-stack limit over in %s on line %d
24+
string(0) ""
25+
OK

php.ini-development

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,6 +1712,11 @@ zend.assertions = 1
17121712
; Default: mbstring.http_output_conv_mimetype=^(text/|application/xhtml\+xml)
17131713
;mbstring.http_output_conv_mimetype=
17141714

1715+
; This directive specifies maximum stack depth for mbstring regular expressions. It is similar
1716+
; to the pcre.recursion_limit for PCRE.
1717+
; Default: 100000
1718+
;mbstring.regex_stack_limit=100000
1719+
17151720
[gd]
17161721
; Tell the jpeg decode to ignore warnings and try to create
17171722
; a gd image. The warning will then be displayed as notices

php.ini-production

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,11 @@ zend.assertions = -1
17191719
; Default: mbstring.http_output_conv_mimetype=^(text/|application/xhtml\+xml)
17201720
;mbstring.http_output_conv_mimetype=
17211721

1722+
; This directive specifies maximum stack depth for mbstring regular expressions. It is similar
1723+
; to the pcre.recursion_limit for PCRE.
1724+
; Default: 100000
1725+
;mbstring.regex_stack_limit=100000
1726+
17221727
[gd]
17231728
; Tell the jpeg decode to ignore warnings and try to create
17241729
; a gd image. The warning will then be displayed as notices

0 commit comments

Comments
 (0)