]> granicus.if.org Git - php/commitdiff
Implement RF bug #72777 - ensure stack limits on mbstring functions.
authorYasuo Ohgaki <yohgaki@php.net>
Thu, 1 Sep 2016 10:15:32 +0000 (19:15 +0900)
committerStanislav Malyshev <stas@php.net>
Thu, 28 Mar 2019 07:31:57 +0000 (00:31 -0700)
The patch creates new config: mbstring.regex_stack_limit, which
defaults to 100000.

ext/mbstring/mbstring.c
ext/mbstring/mbstring.h
ext/mbstring/php_mbregex.c
ext/mbstring/tests/mbregex_stack_limit.phpt [new file with mode: 0644]
ext/mbstring/tests/mbregex_stack_limit2.phpt [new file with mode: 0644]

index 0e255e98d2718fad5d2d0c74c34729d8ef6c1cc3..36b6c478b84c81533861b97eb9c7a97c55cb97be 100644 (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,
-                       (const OnigUChar*)str + str_len, (const OnigUChar *)str,
-                       (const OnigUChar*)str + str_len, NULL, ONIG_OPTION_NONE) >= 0;
+       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, 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()
 /* }}} */
 
index ede7b17b2478f688eb0dbfc4e1dbbad6c878e256..29fe1e97e2ce22299ea464a897b6f600bd1fa17a 100644 (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;
index 319ee567c631f32daea065233c48c7dd602ba4ca..10c7f3e272ca0448117c7b526f5d00f9983e4146 100644 (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;
diff --git a/ext/mbstring/tests/mbregex_stack_limit.phpt b/ext/mbstring/tests/mbregex_stack_limit.phpt
new file mode 100644 (file)
index 0000000..9d0f3ac
--- /dev/null
@@ -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
diff --git a/ext/mbstring/tests/mbregex_stack_limit2.phpt b/ext/mbstring/tests/mbregex_stack_limit2.phpt
new file mode 100644 (file)
index 0000000..12c8c8e
--- /dev/null
@@ -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