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
{
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;
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);
strncpy (dfa->re_str, pattern, length + 1);
#endif
- __libc_lock_init (dfa->lock);
-
err = re_string_construct (®exp, pattern, length, preg->translate,
(syntax & RE_ICASE) != 0, dfa);
if (BE (err != REG_NOERROR, 0))
re_compile_internal_free_return:
free_workarea_compile (preg);
re_string_destruct (®exp);
+ lock_fini (dfa->lock);
free_dfa_content (dfa);
preg->buffer = NULL;
preg->allocated = 0;
if (BE (err != REG_NOERROR, 0))
{
+ lock_fini (dfa->lock);
free_dfa_content (dfa);
preg->buffer = NULL;
preg->allocated = 0;
#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(). */
#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))
{
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;
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. */
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]