Implement RF bug #72777 - ensure stack limits on mbstring functions.

The patch creates new config: mbstring.regex_stack_limit, which
defaults to 100000.
This commit is contained in:
Yasuo Ohgaki 2016-09-01 19:15:32 +09:00 committed by Stanislav Malyshev
parent 218154e695
commit 738016bd88
5 changed files with 94 additions and 8 deletions

View file

@ -1027,9 +1027,18 @@ static void *_php_mb_compile_regex(const char *pattern)
/* {{{ _php_mb_match_regex */
static int _php_mb_match_regex(void *opaque, const char *str, size_t str_len)
{
return onig_search((php_mb_regex_t *)opaque, (const OnigUChar *)str,
OnigMatchParam *mp = onig_new_match_param();
int err;
onig_initialize_match_param(mp);
if(MBSTRG(regex_stack_limit) > 0 && MBSTRG(regex_stack_limit) < UINT_MAX) {
onig_set_match_stack_limit_size_of_match_param(mp, (unsigned int)MBSTRG(regex_stack_limit));
}
/* search */
err = onig_search_with_param((php_mb_regex_t *)opaque, (const OnigUChar *)str,
(const OnigUChar*)str + str_len, (const OnigUChar *)str,
(const OnigUChar*)str + str_len, NULL, ONIG_OPTION_NONE) >= 0;
(const OnigUChar*)str + str_len, NULL, ONIG_OPTION_NONE, mp);
onig_free_match_param(mp);
return err >= 0;
}
/* }}} */
@ -1502,6 +1511,9 @@ PHP_INI_BEGIN()
PHP_INI_ALL,
OnUpdateBool,
strict_detection, zend_mbstring_globals, mbstring_globals)
#if HAVE_MBREGEX
STD_PHP_INI_ENTRY("mbstring.regex_stack_limit", "100000",PHP_INI_ALL, OnUpdateLong, regex_stack_limit, zend_mbstring_globals, mbstring_globals)
#endif
PHP_INI_END()
/* }}} */

View file

@ -166,6 +166,7 @@ ZEND_BEGIN_MODULE_GLOBALS(mbstring)
void *http_output_conv_mimetypes;
#if HAVE_MBREGEX
struct _zend_mb_regex_globals *mb_regex_globals;
zend_long regex_stack_limit;
#endif
char *last_used_encoding_name;
const mbfl_encoding *last_used_encoding;

View file

@ -850,6 +850,23 @@ PHP_FUNCTION(mb_regex_encoding)
}
/* }}} */
/* {{{ _php_mb_onig_search */
static int _php_mb_onig_search(regex_t* reg, const OnigUChar* str, const OnigUChar* end, const OnigUChar* start,
const OnigUChar* range, OnigRegion* region, OnigOptionType option) {
OnigMatchParam *mp = onig_new_match_param();
int err;
onig_initialize_match_param(mp);
if(MBSTRG(regex_stack_limit) > 0 && MBSTRG(regex_stack_limit) < UINT_MAX) {
onig_set_match_stack_limit_size_of_match_param(mp, (unsigned int)MBSTRG(regex_stack_limit));
}
/* search */
err = onig_search_with_param(reg, str, end, start, range, region, option, mp);
onig_free_match_param(mp);
return err;
}
/* }}} */
/* {{{ _php_mb_regex_ereg_exec */
static void _php_mb_regex_ereg_exec(INTERNAL_FUNCTION_PARAMETERS, int icase)
{
@ -909,7 +926,7 @@ static void _php_mb_regex_ereg_exec(INTERNAL_FUNCTION_PARAMETERS, int icase)
regs = onig_region_new();
/* actually execute the regular expression */
if (onig_search(re, (OnigUChar *)string, (OnigUChar *)(string + string_len), (OnigUChar *)string, (OnigUChar *)(string + string_len), regs, 0) < 0) {
if (_php_mb_onig_search(re, (OnigUChar *)string, (OnigUChar *)(string + string_len), (OnigUChar *)string, (OnigUChar *)(string + string_len), regs, 0) < 0) {
RETVAL_FALSE;
goto out;
}
@ -1086,7 +1103,7 @@ static void _php_mb_regex_ereg_replace_exec(INTERNAL_FUNCTION_PARAMETERS, OnigOp
string_lim = (OnigUChar*)(string + string_len);
regs = onig_region_new();
while (err >= 0) {
err = onig_search(re, (OnigUChar *)string, (OnigUChar *)string_lim, pos, (OnigUChar *)string_lim, regs, 0);
err = _php_mb_onig_search(re, (OnigUChar *)string, (OnigUChar *)string_lim, pos, (OnigUChar *)string_lim, regs, 0);
if (err <= -2) {
OnigUChar err_str[ONIG_MAX_ERROR_MESSAGE_LEN];
onig_error_code_to_str(err_str, err);
@ -1262,7 +1279,7 @@ PHP_FUNCTION(mb_split)
/* churn through str, generating array entries as we go */
while (count != 0 && (size_t)(pos - (OnigUChar *)string) < string_len) {
size_t beg, end;
err = onig_search(re, (OnigUChar *)string, (OnigUChar *)(string + string_len), pos, (OnigUChar *)(string + string_len), regs, 0);
err = _php_mb_onig_search(re, (OnigUChar *)string, (OnigUChar *)(string + string_len), pos, (OnigUChar *)(string + string_len), regs, 0);
if (err < 0) {
break;
}
@ -1319,6 +1336,7 @@ PHP_FUNCTION(mb_ereg_match)
OnigSyntaxType *syntax;
OnigOptionType option = 0;
int err;
OnigMatchParam *mp;
{
char *option_str = NULL;
@ -1342,8 +1360,14 @@ PHP_FUNCTION(mb_ereg_match)
RETURN_FALSE;
}
mp = onig_new_match_param();
onig_initialize_match_param(mp);
if(MBSTRG(regex_stack_limit) > 0 && MBSTRG(regex_stack_limit) < UINT_MAX) {
onig_set_match_stack_limit_size_of_match_param(mp, (unsigned int)MBSTRG(regex_stack_limit));
}
/* match */
err = onig_match(re, (OnigUChar *)string, (OnigUChar *)(string + string_len), (OnigUChar *)string, NULL, 0);
err = onig_match_with_param(re, (OnigUChar *)string, (OnigUChar *)(string + string_len), (OnigUChar *)string, NULL, 0, mp);
onig_free_match_param(mp);
if (err >= 0) {
RETVAL_TRUE;
} else {
@ -1406,7 +1430,7 @@ _php_mb_regex_ereg_search_exec(INTERNAL_FUNCTION_PARAMETERS, int mode)
}
MBREX(search_regs) = onig_region_new();
err = onig_search(MBREX(search_re), str, str + len, str + pos, str + len, MBREX(search_regs), 0);
err = _php_mb_onig_search(MBREX(search_re), str, str + len, str + pos, str + len, MBREX(search_regs), 0);
if (err == ONIG_MISMATCH) {
MBREX(search_pos) = len;
RETVAL_FALSE;

View file

@ -0,0 +1,24 @@
--TEST--
Test oniguruma stack limit
--SKIPIF--
<?php extension_loaded('mbstring') or die('skip mbstring not available'); ?>
--FILE--
<?php
$s = str_repeat(' ', 30000);
ini_set('mbstring.regex_stack_limit', 10000);
var_dump(mb_ereg('\\s+$', $s));
ini_set('mbstring.regex_stack_limit', 30000);
var_dump(mb_ereg('\\s+$', $s));
ini_set('mbstring.regex_stack_limit', 30001);
var_dump(mb_ereg('\\s+$', $s));
echo 'OK';
?>
--EXPECT--
bool(false)
bool(false)
int(1)
OK

View file

@ -0,0 +1,25 @@
--TEST--
Test oniguruma stack limit
--SKIPIF--
<?php extension_loaded('mbstring') or die('skip mbstring not available'); ?>
--FILE--
<?php
function mb_trim( $string, $chars = "", $chars_array = array() )
{
for( $x=0; $x<iconv_strlen( $chars ); $x++ ) $chars_array[] = preg_quote( iconv_substr( $chars, $x, 1 ) );
$encoded_char_list = implode( "|", array_merge( array( "\s","\t","\n","\r", "\0", "\x0B" ), $chars_array ) );
$string = mb_ereg_replace( "^($encoded_char_list)*", "", $string );
$string = mb_ereg_replace( "($encoded_char_list)*$", "", $string );
return $string;
}
ini_set('mbstring.regex_stack_limit', 10000);
var_dump(mb_trim(str_repeat(' ', 10000)));
echo 'OK';
?>
--EXPECTF--
Warning: mb_ereg_replace(): mbregex search failure in php_mbereg_replace_exec(): match-stack limit over in %s on line %d
string(0) ""
OK