changeset 17419:020c917cba9d

regex: fix dfa race in multithreaded uses Problem reported by Ludovic Courtès in <http://lists.gnu.org/archive/html/bug-gnulib/2013-05/msg00058.html>. * lib/regex_internal.h (lock_define, lock_init, lock_fini): New macros. All uses of __libc_lock_define, __libc_lock_init changed to use the first two of these. (__libc_lock_lock, __libc_lock_unlock): New macros, for non-glibc platforms. (struct re_dfa_t): Define the lock unconditionally. * lib/regexec.c (regexec, re_search_stub): Remove some now-incorrect '#ifdef _LIBC"s. * modules/regex (Depends-on): Add pthread, if we use the included regex. * lib/regcomp.c: Do actions that are not needed for glibc, but may be needed elsewhere. (regfree, re_compile_internal): Destroy the lock. (re_compile_internal): Check for lock-initialization failure.
author Paul Eggert <eggert@cs.ucla.edu>
date Sun, 19 May 2013 14:26:05 -0700
parents b876bfd31d0f
children 7266df63bb4d
files ChangeLog lib/regcomp.c lib/regex_internal.h lib/regexec.c modules/regex
diffstat 5 files changed, 45 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,24 @@
 2013-05-19  Paul Eggert  <eggert@cs.ucla.edu>
 
+	regex: fix dfa race in multithreaded uses
+	Problem reported by Ludovic Courtès in
+	<http://lists.gnu.org/archive/html/bug-gnulib/2013-05/msg00058.html>.
+	* lib/regex_internal.h (lock_define, lock_init, lock_fini):
+	New macros.  All uses of __libc_lock_define, __libc_lock_init
+	changed to use the first two of these.
+	(__libc_lock_lock, __libc_lock_unlock): New macros, for
+	non-glibc platforms.
+	(struct re_dfa_t): Define the lock unconditionally.
+	* lib/regexec.c (regexec, re_search_stub): Remove some now-incorrect
+	'#ifdef _LIBC"s.
+	* modules/regex (Depends-on): Add pthread, if we use the
+	included regex.
+
+	* lib/regcomp.c: Do actions that are not needed for glibc,
+	but may be needed elsewhere.
+	(regfree, re_compile_internal): Destroy the lock.
+	(re_compile_internal): Check for lock-initialization failure.
+
 	malloca: port to compilers that reject size-zero arrays
 	This fixes a bug introduced in my previous patch.
 	* lib/malloca.c (struct preliminary_header): Use an int
--- a/lib/regcomp.c
+++ b/lib/regcomp.c
@@ -663,7 +663,10 @@
 {
   re_dfa_t *dfa = preg->buffer;
   if (BE (dfa != NULL, 1))
-    free_dfa_content (dfa);
+    {
+      lock_fini (dfa->lock);
+      free_dfa_content (dfa);
+    }
   preg->buffer = NULL;
   preg->allocated = 0;
 
@@ -784,6 +787,8 @@
   preg->used = sizeof (re_dfa_t);
 
   err = init_dfa (dfa, length);
+  if (BE (err == REG_NOERROR && lock_init (dfa->lock) != 0, 0))
+    err = REG_ESPACE;
   if (BE (err != REG_NOERROR, 0))
     {
       free_dfa_content (dfa);
@@ -797,8 +802,6 @@
   strncpy (dfa->re_str, pattern, length + 1);
 #endif
 
-  __libc_lock_init (dfa->lock);
-
   err = re_string_construct (&regexp, pattern, length, preg->translate,
 			     (syntax & RE_ICASE) != 0, dfa);
   if (BE (err != REG_NOERROR, 0))
@@ -806,6 +809,7 @@
     re_compile_internal_free_return:
       free_workarea_compile (preg);
       re_string_destruct (&regexp);
+      lock_fini (dfa->lock);
       free_dfa_content (dfa);
       preg->buffer = NULL;
       preg->allocated = 0;
@@ -838,6 +842,7 @@
 
   if (BE (err != REG_NOERROR, 0))
     {
+      lock_fini (dfa->lock);
       free_dfa_content (dfa);
       preg->buffer = NULL;
       preg->allocated = 0;
--- a/lib/regex_internal.h
+++ b/lib/regex_internal.h
@@ -32,12 +32,25 @@
 #include <wctype.h>
 #include <stdbool.h>
 #include <stdint.h>
+
 #if defined _LIBC
 # include <bits/libc-lock.h>
+#endif
+/* Use __libc_lock_define (, NAME) if the library defines the macro,
+   and if the compiler is known to support empty macro arguments.  */
+#if (defined __libc_lock_define                                         \
+     && ((defined __GNUC__ && !defined __STRICT_ANSI__)                 \
+         || (defined __STDC_VERSION__ && 199901L <= __STDC_VERSION__)))
+# define lock_define(name) __libc_lock_define (, name)
+# define lock_init(lock) (__libc_lock_init (lock), 0)
+# define lock_fini(lock) 0
 #else
-# define __libc_lock_init(NAME) do { } while (0)
-# define __libc_lock_lock(NAME) do { } while (0)
-# define __libc_lock_unlock(NAME) do { } while (0)
+# include <pthread.h>
+# define lock_define(name) pthread_mutex_t name;
+# define lock_init(lock) pthread_mutex_init (&(lock), 0)
+# define lock_fini(lock) pthread_mutex_destroy (&(lock))
+# define __libc_lock_lock(lock) pthread_mutex_lock (&(lock))
+# define __libc_lock_unlock(lock) pthread_mutex_unlock (&(lock))
 #endif
 
 /* In case that the system doesn't have isblank().  */
@@ -698,9 +711,7 @@
 #ifdef DEBUG
   char* re_str;
 #endif
-#ifdef _LIBC
-  __libc_lock_define (, lock)
-#endif
+  lock_define (lock)
 };
 
 #define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set))
--- a/lib/regexec.c
+++ b/lib/regexec.c
@@ -228,9 +228,7 @@
 {
   reg_errcode_t err;
   Idx start, length;
-#ifdef _LIBC
   re_dfa_t *dfa = preg->buffer;
-#endif
 
   if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND))
     return REG_BADPAT;
@@ -421,9 +419,7 @@
   Idx nregs;
   regoff_t rval;
   int eflags = 0;
-#ifdef _LIBC
   re_dfa_t *dfa = bufp->buffer;
-#endif
   Idx last_start = start + range;
 
   /* Check for out-of-range.  */
--- a/modules/regex
+++ b/modules/regex
@@ -24,6 +24,7 @@
 mbrtowc         [test $ac_use_included_regex = yes]
 mbsinit         [test $ac_use_included_regex = yes]
 nl_langinfo     [test $ac_use_included_regex = yes]
+pthread         [test $ac_use_included_regex = yes]
 stdbool         [test $ac_use_included_regex = yes]
 stdint          [test $ac_use_included_regex = yes]
 wchar           [test $ac_use_included_regex = yes]