List:Commits« Previous MessageNext Message »
From:Dmitry Shulga Date:January 18 2011 7:17am
Subject:bzr commit into mysql-5.1-bugteam branch (Dmitry.Shulga:3514) Bug#58026
View as plain text  
#At file:///Users/shulga/projects/mysql/5.1-bugteam-bug58026/ based on revid:davi.arnaut@stripped

 3514 Dmitry Shulga	2011-01-18
      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.
     @ client/mysqltest.cc
        Passed NULL pointer to function as last actual argument in call
        to my_regcomp. In this case check for enough memory in stack
        doesn't occur. Such checking is not necessary in mysql clients.
     @ regex/main.c
        Passed NULL pointer to function as last actual argument in call
        to my_regcomp. In this case check for enough memory
        in stack doesn't occur. Such checking is not necessary in regex's 
        regression tests.
     @ regex/my_regex.h
        added pointer to function as last argument of my_regcomp() for check
        enough memory in stack.
     @ regex/regcomp.c
        Added pointer to function as last argument of p_ere()/p_ere_exp()
        that called in order to check that there are enough memory in stack
        for next recursion call.
     @ regex/regcomp.ih
        added pointer to function as last argument of my_regcomp() for check
        enough memory in stack.
     @ sql/item_cmpfunc.cc
        Passed pointer to function check_enough_stack_size() in call to
        my_regcomp(). The function check_enough_stack_size() will be
        called in during recursive descendant for regular expression parsing.

    modified:
      client/mysqltest.cc
      regex/main.c
      regex/my_regex.h
      regex/regcomp.c
      regex/regcomp.ih
      sql/item_cmpfunc.cc
=== modified file 'client/mysqltest.cc'
--- a/client/mysqltest.cc	2010-11-19 09:29:08 +0000
+++ b/client/mysqltest.cc	2011-01-18 07:17:19 +0000
@@ -7593,7 +7593,7 @@ char *re_eprint(int err)
 void init_re_comp(my_regex_t *re, const char* str)
 {
   int err= my_regcomp(re, str, (REG_EXTENDED | REG_ICASE | REG_NOSUB),
-                      &my_charset_latin1);
+                      &my_charset_latin1, NULL);
   if (err)
   {
     char erbuf[100];
@@ -9032,7 +9032,7 @@ int reg_replace(char** buf_p, int* buf_l
   if (icase)
     cflags|= REG_ICASE;
 
-  if ((err_code= my_regcomp(&r,pattern,cflags,&my_charset_latin1)))
+  if ((err_code= my_regcomp(&r,pattern,cflags,&my_charset_latin1, NULL)))
   {
     check_regerr(&r,err_code);
     return 1;

=== modified file 'regex/main.c'
--- a/regex/main.c	2010-10-19 22:36:59 +0000
+++ b/regex/main.c	2011-01-18 07:17:19 +0000
@@ -74,7 +74,8 @@ char *argv[];
 		exit(status);
 	}
 
-	err = my_regcomp(&re, argv[optind++], copts, &my_charset_latin1);
+	err = my_regcomp(&re, argv[optind++], copts, &my_charset_latin1,
+	                 NULL);
 	if (err) {
 		len = my_regerror(err, &re, erbuf, sizeof(erbuf));
 		fprintf(stderr, "error %s, %d/%d `%s'\n",
@@ -226,7 +227,7 @@ int opts;			/* may not match f1 */
 	strcpy(f0copy, f0);
 	re.re_endp = (opts&REG_PEND) ? f0copy + strlen(f0copy) : NULL;
 	fixstr(f0copy);
-	err = my_regcomp(&re, f0copy, opts, &my_charset_latin1);
+	err = my_regcomp(&re, f0copy, opts, &my_charset_latin1, NULL);
 	if (err != 0 && (!opt('C', f1) || err != efind(f2))) {
 		/* unexpected error or wrong error */
 		len = my_regerror(err, &re, erbuf, sizeof(erbuf));

=== modified file 'regex/my_regex.h'
--- a/regex/my_regex.h	2005-09-29 00:08:24 +0000
+++ b/regex/my_regex.h	2011-01-18 07:17:19 +0000
@@ -28,7 +28,9 @@ typedef struct {
 
 
 /* === regcomp.c === */
-extern int my_regcomp(my_regex_t *, const char *, int, CHARSET_INFO *charset);
+typedef int (*my_regex_stack_check_t)();
+extern int my_regcomp(my_regex_t *, const char *, int, CHARSET_INFO *charset,
+                      my_regex_stack_check_t check_enough_mem_in_stack);
 #define	REG_BASIC	0000
 #define	REG_EXTENDED	0001
 #define	REG_ICASE	0002

=== modified file 'regex/regcomp.c'
--- a/regex/regcomp.c	2010-07-09 19:37:52 +0000
+++ b/regex/regcomp.c	2011-01-18 07:17:19 +0000
@@ -100,11 +100,12 @@ static int never = 0;		/* for use in ass
  = #define	REG_DUMP	0200
  */
 int				/* 0 success, otherwise REG_something */
-my_regcomp(preg, pattern, cflags, charset)
+my_regcomp(preg, pattern, cflags, charset, check_enough_mem_in_stack)
 my_regex_t *preg;
 const char *pattern;
 int cflags;
 CHARSET_INFO *charset;
+my_regex_stack_check_t check_enough_mem_in_stack;
 {
 	struct parse pa;
 	register struct re_guts *g;
@@ -174,7 +175,7 @@ CHARSET_INFO *charset;
 	EMIT(OEND, 0);
 	g->firststate = THERE();
 	if (cflags&REG_EXTENDED)
-		p_ere(p, OUT);
+		p_ere(p, OUT, check_enough_mem_in_stack);
 	else if (cflags&REG_NOSPEC)
 		p_str(p);
 	else
@@ -205,12 +206,14 @@ CHARSET_INFO *charset;
 
 /*
  - p_ere - ERE parser top level, concatenation and alternation
- == static void p_ere(register struct parse *p, int stop);
+ == static void p_ere(register struct parse *p, int stop,
+                      my_regex_stack_check_t check_enough_mem_in_stack);
  */
 static void
-p_ere(p, stop)
+p_ere(p, stop, check_enough_mem_in_stack)
 register struct parse *p;
 int stop;			/* character this ERE should end at */
+my_regex_stack_check_t check_enough_mem_in_stack;
 {
 	register char c;
 	register sopno UNINIT_VAR(prevback);
@@ -221,8 +224,14 @@ int stop;			/* character this ERE should
 	for (;;) {
 		/* do a bunch of concatenated expressions */
 		conc = HERE();
-		while (MORE() && (c = PEEK()) != '|' && c != stop)
-			p_ere_exp(p);
+		while (MORE() && (c = PEEK()) != '|' && c != stop) {
+		  if (check_enough_mem_in_stack &&
+		      check_enough_mem_in_stack()) {
+		    SETERROR(REG_ESPACE);
+		    return;
+		  }
+		  p_ere_exp(p, check_enough_mem_in_stack);
+		}
 		if(REQUIRE(HERE() != conc, REG_EMPTY)) {}/* require nonempty */
 
 		if (!EAT('|'))
@@ -254,8 +263,9 @@ int stop;			/* character this ERE should
  == static void p_ere_exp(register struct parse *p);
  */
 static void
-p_ere_exp(p)
+p_ere_exp(p,check_enough_mem_in_stack)
 register struct parse *p;
+my_regex_stack_check_t check_enough_mem_in_stack;
 {
 	register char c;
 	register sopno pos;
@@ -277,7 +287,7 @@ register struct parse *p;
 		        p->pbegin[subno] = HERE();
 		EMIT(OLPAREN, subno);
 		if (!SEE(')'))
-			p_ere(p, ')');
+			p_ere(p, ')', check_enough_mem_in_stack);
 		if (subno < NPAREN) {
 			p->pend[subno] = HERE();
 			assert(p->pend[subno] != 0);

=== modified file 'regex/regcomp.ih'
--- a/regex/regcomp.ih	2004-10-19 22:28:42 +0000
+++ b/regex/regcomp.ih	2011-01-18 07:17:19 +0000
@@ -4,8 +4,8 @@ extern "C" {
 #endif
 
 /* === regcomp.c === */
-static void p_ere(register struct parse *p, int stop);
-static void p_ere_exp(register struct parse *p);
+static void p_ere(register struct parse *p, int stop, my_regex_stack_check_t check_enough_mem_in_stack);
+static void p_ere_exp(register struct parse *p, my_regex_stack_check_t check_enough_mem_in_stack);
 static void p_str(register struct parse *p);
 static void p_bre(register struct parse *p, register int end1, register int end2);
 static int p_simp_re(register struct parse *p, int starordinary);

=== modified file 'sql/item_cmpfunc.cc'
--- a/sql/item_cmpfunc.cc	2010-09-09 12:48:06 +0000
+++ b/sql/item_cmpfunc.cc	2011-01-18 07:17:19 +0000
@@ -4791,6 +4791,18 @@ void Item_func_like::cleanup()
 
 #ifdef USE_REGEX
 
+extern "C"
+int
+check_enough_stack_size()
+{
+#ifndef EMBEDDED_LIBRARY    // Avoid compiler warning
+  uchar stack_top;
+#endif
+  return check_stack_overrun(current_thd, STACK_MIN_SIZE,
+                             &stack_top);
+}
+
+
 /**
   @brief Compile regular expression.
 
@@ -4834,7 +4846,8 @@ int Item_func_regex::regcomp(bool send_e
   }
 
   if ((error= my_regcomp(&preg, res->c_ptr_safe(),
-                         regex_lib_flags, regex_lib_charset)))
+                         regex_lib_flags, regex_lib_charset,
+                         check_enough_stack_size)))
   {
     if (send_error)
     {


Attachment: [text/bzr-bundle] bzr/dmitry.shulga@oracle.com-20110118071719-upb85w50xcvdm0p0.bundle
Thread
bzr commit into mysql-5.1-bugteam branch (Dmitry.Shulga:3514) Bug#58026Dmitry Shulga18 Jan
  • Re: bzr commit into mysql-5.1-bugteam branch (Dmitry.Shulga:3514)Bug#58026Davi Arnaut18 Jan
    • Re: bzr commit into mysql-5.1-bugteam branch (Dmitry.Shulga:3514)Bug#58026Joerg Bruehe19 Jan
      • Re: bzr commit into mysql-5.1-bugteam branch (Dmitry.Shulga:3514)Bug#58026Davi Arnaut19 Jan