Add support for mbstring.regex_retry_limit

This is very similar to the existing mbstring.regex_stack_limit,
but for backtracking. The default value matches pcre.backtrack_limit.
Only used on libonig >= 2.8.0.
This commit is contained in:
Nikita Popov 2019-10-02 12:42:46 +02:00
parent b078ae6c01
commit 6623e7ac51
7 changed files with 53 additions and 1 deletions

View File

@ -268,6 +268,10 @@ PHP 7.4 UPGRADE NOTES
. Added mb_str_split() function, which provides the same functionality as
str_split(), but operating on code points rather than bytes.
RFC: https://wiki.php.net/rfc/mb_str_split
. Added mbstring.regex_retry_limit ini setting defaulting to 1000000. It
limits the amount of backtracking that may be performed during one mbregex
match and thus protects against exponential backtracking attacks (ReDOS).
This setting only takes effect when linking against oniguruma >= 2.8.0.
- OPcache:
. Support for preloading code has been added.

View File

@ -65,6 +65,7 @@ typedef void OnigMatchParam;
#define onig_new_match_param() (NULL)
#define onig_initialize_match_param(x) (void)(x)
#define onig_set_match_stack_limit_size_of_match_param(x, y)
#define onig_set_retry_limit_in_match_of_match_param(x, y)
#define onig_free_match_param(x)
#define onig_search_with_param(reg, str, end, start, range, region, option, mp) \
onig_search(reg, str, end, start, range, region, option)
@ -1029,6 +1030,9 @@ static int _php_mb_match_regex(void *opaque, const char *str, size_t str_len)
if (!ZEND_LONG_UINT_OVFL(MBSTRG(regex_stack_limit))) {
onig_set_match_stack_limit_size_of_match_param(mp, (unsigned int)MBSTRG(regex_stack_limit));
}
if (!ZEND_LONG_UINT_OVFL(MBSTRG(regex_retry_limit))) {
onig_set_retry_limit_in_match_of_match_param(mp, (unsigned int)MBSTRG(regex_retry_limit));
}
/* search */
err = onig_search_with_param((php_mb_regex_t *)opaque, (const OnigUChar *)str,
(const OnigUChar*)str + str_len, (const OnigUChar *)str,
@ -1495,6 +1499,7 @@ PHP_INI_BEGIN()
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)
STD_PHP_INI_ENTRY("mbstring.regex_retry_limit", "1000000",PHP_INI_ALL, OnUpdateLong, regex_retry_limit, zend_mbstring_globals, mbstring_globals)
#endif
PHP_INI_END()
/* }}} */

View File

@ -170,6 +170,9 @@ ZEND_BEGIN_MODULE_GLOBALS(mbstring)
zend_bool internal_encoding_set;
zend_bool http_output_set;
zend_bool http_input_set;
#if HAVE_MBREGEX
zend_long regex_retry_limit;
#endif
ZEND_END_MODULE_GLOBALS(mbstring)
#define MB_OVERLOAD_MAIL 1

View File

@ -39,6 +39,7 @@ typedef void OnigMatchParam;
#define onig_new_match_param() (NULL)
#define onig_initialize_match_param(x) (void)(x)
#define onig_set_match_stack_limit_size_of_match_param(x, y)
#define onig_set_retry_limit_in_match_of_match_param(x, y)
#define onig_free_match_param(x)
#define onig_search_with_param(reg, str, end, start, range, region, option, mp) \
onig_search(reg, str, end, start, range, region, option)
@ -874,6 +875,9 @@ static int _php_mb_onig_search(regex_t* reg, const OnigUChar* str, const OnigUCh
if (!ZEND_LONG_UINT_OVFL(MBSTRG(regex_stack_limit))) {
onig_set_match_stack_limit_size_of_match_param(mp, (unsigned int)MBSTRG(regex_stack_limit));
}
if (!ZEND_LONG_UINT_OVFL(MBSTRG(regex_retry_limit))) {
onig_set_retry_limit_in_match_of_match_param(mp, (unsigned int)MBSTRG(regex_retry_limit));
}
/* search */
err = onig_search_with_param(reg, str, end, start, range, region, option, mp);
onig_free_match_param(mp);
@ -1395,9 +1399,12 @@ PHP_FUNCTION(mb_ereg_match)
mp = onig_new_match_param();
onig_initialize_match_param(mp);
if(MBSTRG(regex_stack_limit) > 0 && MBSTRG(regex_stack_limit) < UINT_MAX) {
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));
}
if (MBSTRG(regex_retry_limit) > 0 && MBSTRG(regex_retry_limit) < UINT_MAX) {
onig_set_retry_limit_in_match_of_match_param(mp, (unsigned int)MBSTRG(regex_retry_limit));
}
/* match */
err = onig_match_with_param(re, (OnigUChar *)string, (OnigUChar *)(string + string_len), (OnigUChar *)string, NULL, 0, mp);
onig_free_match_param(mp);

View File

@ -0,0 +1,23 @@
--TEST--
Oniguruma retry limit
--SKIPIF--
<?php
extension_loaded('mbstring') or die('skip mbstring not available');
if (!function_exists('mb_ereg')) die('skip mb_ereg not available');
if (version_compare(MB_ONIGURUMA_VERSION, '6.8.0') < 0) {
die('skip requires Oniguruma 6.8.0');
}
?>
--FILE--
<?php
$regex = 'A(B|C+)+D|AC+X';
$str = 'ACCCCCCCCCCCCCCCCCCCX';
var_dump(mb_ereg($regex, $str));
ini_set('mbstring.regex_retry_limit', '100000');
var_dump(mb_ereg($regex, $str));
?>
--EXPECT--
int(1)
bool(false)

View File

@ -1692,6 +1692,11 @@ zend.assertions = 1
; Default: 100000
;mbstring.regex_stack_limit=100000
; This directive specifies maximum retry count for mbstring regular expressions. It is similar
; to the pcre.backtrack_limit for PCRE.
; Default: 1000000
;mbstring.regex_retry_limit=1000000
[gd]
; Tell the jpeg decode to ignore warnings and try to create
; a gd image. The warning will then be displayed as notices

View File

@ -1694,6 +1694,11 @@ zend.assertions = -1
; Default: 100000
;mbstring.regex_stack_limit=100000
; This directive specifies maximum retry count for mbstring regular expressions. It is similar
; to the pcre.backtrack_limit for PCRE.
; Default: 1000000
;mbstring.regex_retry_limit=1000000
[gd]
; Tell the jpeg decode to ignore warnings and try to create
; a gd image. The warning will then be displayed as notices