summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikita Popov <nikita.ppv@gmail.com>2019-10-02 12:42:46 +0200
committerNikita Popov <nikita.ppv@gmail.com>2019-10-06 10:06:33 +0200
commit6623e7ac51f0bafe85e013f9aed5340199e80c10 (patch)
tree88a884f764f5a532996802ccbb74365a598884d5
parentb078ae6c01101f2f69cd5fb4c9b80c65a5164e04 (diff)
downloadphp-git-6623e7ac51f0bafe85e013f9aed5340199e80c10.tar.gz
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.
-rw-r--r--UPGRADING4
-rw-r--r--ext/mbstring/mbstring.c5
-rw-r--r--ext/mbstring/mbstring.h3
-rw-r--r--ext/mbstring/php_mbregex.c9
-rw-r--r--ext/mbstring/tests/retry_limit.phpt23
-rw-r--r--php.ini-development5
-rw-r--r--php.ini-production5
7 files changed, 53 insertions, 1 deletions
diff --git a/UPGRADING b/UPGRADING
index 79a69df045..a7c70c2690 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -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.
diff --git a/ext/mbstring/mbstring.c b/ext/mbstring/mbstring.c
index f1a617810a..a18d237df2 100644
--- a/ext/mbstring/mbstring.c
+++ b/ext/mbstring/mbstring.c
@@ -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()
/* }}} */
diff --git a/ext/mbstring/mbstring.h b/ext/mbstring/mbstring.h
index 5a713e5496..eb2d0bce3f 100644
--- a/ext/mbstring/mbstring.h
+++ b/ext/mbstring/mbstring.h
@@ -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
diff --git a/ext/mbstring/php_mbregex.c b/ext/mbstring/php_mbregex.c
index d315712781..366aad23cf 100644
--- a/ext/mbstring/php_mbregex.c
+++ b/ext/mbstring/php_mbregex.c
@@ -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);
diff --git a/ext/mbstring/tests/retry_limit.phpt b/ext/mbstring/tests/retry_limit.phpt
new file mode 100644
index 0000000000..2d9d42a36f
--- /dev/null
+++ b/ext/mbstring/tests/retry_limit.phpt
@@ -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)
diff --git a/php.ini-development b/php.ini-development
index 4ac6c44b1e..7c8649d4c7 100644
--- a/php.ini-development
+++ b/php.ini-development
@@ -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
diff --git a/php.ini-production b/php.ini-production
index d47cf161e3..8dc9a32e00 100644
--- a/php.ini-production
+++ b/php.ini-production
@@ -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