]> granicus.if.org Git - php/commitdiff
Add support for mbstring.regex_retry_limit
authorNikita Popov <nikita.ppv@gmail.com>
Wed, 2 Oct 2019 10:42:46 +0000 (12:42 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Sun, 6 Oct 2019 08:06:33 +0000 (10:06 +0200)
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.

UPGRADING
ext/mbstring/mbstring.c
ext/mbstring/mbstring.h
ext/mbstring/php_mbregex.c
ext/mbstring/tests/retry_limit.phpt [new file with mode: 0644]
php.ini-development
php.ini-production

index 79a69df045d214a2fe83c32fac4dcf63707ae01e..a7c70c269008bce1166f0e57f00b92d9c1998bdd 100644 (file)
--- 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.
index f1a617810a9d1887c8f017e6cf8f8c39d5174969..a18d237df29a35c44827e63d88f17ff1fa6ed8cd 100644 (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()
 /* }}} */
index 5a713e549693c6e0eae4b8a4d50c95c13a5d8e62..eb2d0bce3f18b312cae69d1c9e8f62db03d0d3d0 100644 (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
index d3157127819b93342dff0b4fa3df17be676af7bd..366aad23cfa2f8416413432650497989021e79aa 100644 (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);
diff --git a/ext/mbstring/tests/retry_limit.phpt b/ext/mbstring/tests/retry_limit.phpt
new file mode 100644 (file)
index 0000000..2d9d42a
--- /dev/null
@@ -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)
index 4ac6c44b1e9d98d84db9564ddf6b8830db5bd0a0..7c8649d4c72354fba2dcc56653e3bb6d15b36481 100644 (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
index d47cf161e3f55997ffba89649adb57ba0a7bf35e..8dc9a32e0026905ac5a881b0a93bdb9d8274953a 100644 (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