summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Shulga <Dmitry.Shulga@oracle.com>2011-02-04 10:47:46 +0600
committerDmitry Shulga <Dmitry.Shulga@oracle.com>2011-02-04 10:47:46 +0600
commit378091e434d66dcc7081f991a07db2244dbb8cda (patch)
treed9c23034dd5cc3bd26c4dce059805d050030408d
parenta265fde655316a4c1b1702bb97dd7fdde621f55c (diff)
downloadmariadb-git-378091e434d66dcc7081f991a07db2244dbb8cda.tar.gz
Fixed bug#58026 - massive recursion and crash in regular expression
handling. The problem was that parsing of nested regular expression involved recursive calls. Such recursion didn't take into account the amount of available stack space, which ended up leading to stack overflow crashes. mysql-test/t/not_embedded_server.test: Added test for bug#58026. regex/my_regex.h: added pointer to function as last argument of my_regex_init() for check enough memory in stack. regex/regcomp.c: p_ere() was modified: added call to function for check enough memory in stack. Function for check available stack space specified by global variable my_regex_enough_mem_in_stack. This variable set to NULL for embedded mysqld and to a pointer to function check_enough_stack_size otherwise. regex/reginit.c: my_regex_init was modified: pass a pointer to a function for check enough memory in stack space. Reset this pointer to NULL in my_regex_end. sql/mysqld.cc: Added function check_enough_stack_size() for check enough memory in stack. Passed this function as second argument to my_regex_init. For embedded mysqld passed NULL as second argument.
-rw-r--r--mysql-test/r/not_embedded_server.result4
-rw-r--r--mysql-test/t/not_embedded_server.test10
-rw-r--r--regex/my_regex.h4
-rw-r--r--regex/regcomp.c15
-rw-r--r--regex/reginit.c6
-rw-r--r--sql/mysqld.cc19
6 files changed, 53 insertions, 5 deletions
diff --git a/mysql-test/r/not_embedded_server.result b/mysql-test/r/not_embedded_server.result
index 60c92bd0196..7219e29b2a6 100644
--- a/mysql-test/r/not_embedded_server.result
+++ b/mysql-test/r/not_embedded_server.result
@@ -4,3 +4,7 @@ select 1;
SHOW VARIABLES like 'slave_skip_errors';
Variable_name Value
slave_skip_errors OFF
+#
+# Bug#58026: massive recursion and crash in regular expression handling
+#
+SELECT '1' RLIKE RPAD('1', 10000, '(');
diff --git a/mysql-test/t/not_embedded_server.test b/mysql-test/t/not_embedded_server.test
index fa2b659ec57..43cdb977386 100644
--- a/mysql-test/t/not_embedded_server.test
+++ b/mysql-test/t/not_embedded_server.test
@@ -42,4 +42,14 @@ select 1;
SHOW VARIABLES like 'slave_skip_errors';
+--echo #
+--echo # Bug#58026: massive recursion and crash in regular expression handling
+--echo #
+
+--disable_result_log
+--error ER_STACK_OVERRUN_NEED_MORE
+SELECT '1' RLIKE RPAD('1', 10000, '(');
+--enable_result_log
+
+
# End of 5.1 tests
diff --git a/regex/my_regex.h b/regex/my_regex.h
index 0d1cedf5430..30896e29b91 100644
--- a/regex/my_regex.h
+++ b/regex/my_regex.h
@@ -28,6 +28,7 @@ typedef struct {
/* === regcomp.c === */
+typedef int (*my_regex_stack_check_t)();
extern int my_regcomp(my_regex_t *, const char *, int, CHARSET_INFO *charset);
#define REG_BASIC 0000
#define REG_EXTENDED 0001
@@ -76,7 +77,8 @@ extern void my_regfree(my_regex_t *);
/* === reginit.c === */
-extern void my_regex_init(CHARSET_INFO *cs); /* Should be called for multithread progs */
+/* Should be called for multithread progs */
+extern void my_regex_init(CHARSET_INFO *cs, my_regex_stack_check_t func);
extern void my_regex_end(void); /* If one wants a clean end */
#ifdef __cplusplus
diff --git a/regex/regcomp.c b/regex/regcomp.c
index 81c435ed552..e163a9ba7f4 100644
--- a/regex/regcomp.c
+++ b/regex/regcomp.c
@@ -31,6 +31,9 @@ struct parse {
CHARSET_INFO *charset; /* for ctype things */
};
+/* Check if there is enough stack space for recursion. */
+my_regex_stack_check_t my_regex_enough_mem_in_stack= NULL;
+
#include "regcomp.ih"
static char nuls[10]; /* place to point scanner in event of error */
@@ -117,7 +120,7 @@ CHARSET_INFO *charset;
# define GOODFLAGS(f) ((f)&~REG_DUMP)
#endif
- my_regex_init(charset); /* Init cclass if neaded */
+ my_regex_init(charset, NULL); /* Init cclass if neaded */
preg->charset=charset;
cflags = GOODFLAGS(cflags);
if ((cflags&REG_EXTENDED) && (cflags&REG_NOSPEC))
@@ -222,7 +225,15 @@ int stop; /* character this ERE should end at */
/* do a bunch of concatenated expressions */
conc = HERE();
while (MORE() && (c = PEEK()) != '|' && c != stop)
- p_ere_exp(p);
+ {
+ if (my_regex_enough_mem_in_stack &&
+ my_regex_enough_mem_in_stack())
+ {
+ SETERROR(REG_ESPACE);
+ return;
+ }
+ p_ere_exp(p);
+ }
if(REQUIRE(HERE() != conc, REG_EMPTY)) {}/* require nonempty */
if (!EAT('|'))
diff --git a/regex/reginit.c b/regex/reginit.c
index 5980de24030..3d2cd64d1e7 100644
--- a/regex/reginit.c
+++ b/regex/reginit.c
@@ -4,10 +4,12 @@
#include <m_ctype.h>
#include <m_string.h>
#include "cclass.h"
+#include "my_regex.h"
static my_bool regex_inited=0;
+extern my_regex_stack_check_t my_regex_enough_mem_in_stack;
-void my_regex_init(CHARSET_INFO *cs)
+void my_regex_init(CHARSET_INFO *cs, my_regex_stack_check_t func)
{
char buff[CCLASS_LAST][256];
int count[CCLASS_LAST];
@@ -16,6 +18,7 @@ void my_regex_init(CHARSET_INFO *cs)
if (!regex_inited)
{
regex_inited=1;
+ my_regex_enough_mem_in_stack= func;
bzero((uchar*) &count,sizeof(count));
for (i=1 ; i<= 255; i++)
@@ -74,6 +77,7 @@ void my_regex_end()
int i;
for (i=0; i < CCLASS_LAST ; i++)
free((char*) cclasses[i].chars);
+ my_regex_enough_mem_in_stack= NULL;
regex_inited=0;
}
}
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 54f93cccd5d..694d64c81df 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -3034,6 +3034,19 @@ sizeof(load_default_groups)/sizeof(load_default_groups[0]);
#endif
+#ifndef EMBEDDED_LIBRARY
+static
+int
+check_enough_stack_size()
+{
+ uchar stack_top;
+
+ return check_stack_overrun(current_thd, STACK_MIN_SIZE,
+ &stack_top);
+}
+#endif
+
+
/**
Initialize one of the global date/time format variables.
@@ -3415,7 +3428,11 @@ static int init_common_variables(const char *conf_file_name, int argc,
#endif
mysys_uses_curses=0;
#ifdef USE_REGEX
- my_regex_init(&my_charset_latin1);
+#ifndef EMBEDDED_LIBRARY
+ my_regex_init(&my_charset_latin1, check_enough_stack_size);
+#else
+ my_regex_init(&my_charset_latin1, NULL);
+#endif
#endif
/*
Process a comma-separated character set list and choose