1. Summary: strerror_r() can use in multi-threaded applications, but rarely SIGSEGV occurs in strerror_r() with using setlocale() in other threads. 2. hardware dependency: none. 3. Description of Problem: SIGSEGV occurs in strerror_r(). 4. How reproducible: rarely, but it happen. 5. Step to Reproduce: The following test program can reproduce the problems. tst-strerror.tar.gz To make reproduction easy, this program executes strerror_r(), printf(), setlocale() at the same time by a lot of threads. 1) Extract the archive file. The following files are extracted. Makefile tst-strerror.c 2) Compile the test program. # make 3) Run the test program. # ./tst-strerror Run the test programs in several times, SIGSEGV occurs. 6. Actual Result: SIGSEGV occurs in strerror_r(). 7. Expected Result: strerror_r() execute normally. ------------------------------ Makefile: CC=gcc CFLAGS=-g -Wall -lpthread PROGS= tst-strerror all: $(PROGS) .c: $(CC) $(CFLAGS) -o $* $*.c $(PROGS): clean: rm -f $(PROGS) ------------------------------- tst-strerror.c: #include <stdlib.h> #include <stddef.h> #include <stdio.h> #include <unistd.h> #include <limits.h> #include <locale.h> #include <wordexp.h> #include "pthread.h" #include <langinfo.h> #include <string.h> #include <monetary.h> #include <string.h> #include <errno.h> #define ENVCOUNT_MAX 100 #define MAXTHREAD 100 static int start; static void * process_setlocale_1(void *arg) { int i,s; char *result; while(!start) sched_yield(); s = 0; for (i = 0; ; i++) { switch (s) { case 0: result = setlocale ( LC_MESSAGES, "el_GR.utf8" ); s++; break; case 1: result = setlocale ( LC_MESSAGES, "en_CA.iso88591" ); s++; break; case 2: result = setlocale ( LC_MESSAGES, "es_DO" ); s++; break; case 3: result = setlocale ( LC_MESSAGES, "et_EE.utf8" ); s++; break; default : s = 0; } } return NULL; } static void * taikou_strerror(void *arg) { int i; char *buf; buf = malloc ( 1024 ); extern int errno; while(!start) sched_yield(); for (i = 0; ; i++) { strerror_r ( errno, buf, 1024 ); printf ( " errno = %d : %s \n ", errno, buf); } return NULL; } int main (void) { int retcode; pthread_t th[MAXTHREAD]; pthread_attr_t attr[MAXTHREAD]; char s[MAXTHREAD][7]; int i; void *result; start = 0; i = 0; pthread_attr_init(&attr[i]); pthread_attr_setstacksize(&attr[i],PTHREAD_STACK_MIN); sprintf(s[i], "A%05d", i); retcode = pthread_create (&th[i], &attr[i], process_setlocale_1, (void *) s[i]); if (retcode != 0) { fprintf (stderr, "create a failed (wordexp) %d\n", retcode); } for (i = 1; i < MAXTHREAD; i++) { pthread_attr_init(&attr[i]); pthread_attr_setstacksize(&attr[i],PTHREAD_STACK_MIN); sprintf(s[i], "A%05d", i); retcode = pthread_create (&th[i], &attr[i], taikou_strerror, (void *) s[i]); if (retcode != 0) { fprintf (stderr, "create a failed (getenv) %d\n", retcode); } } sleep(0); start = 1; sleep(0); for (i = 0; i < MAXTHREAD ; i++) { pthread_join(th[i],&result); } printf("end\n"); return 0; } --------------------------------------------------------------------
Can't confirm with glibc 2.7 on x86 32bit.
*** Bug 5489 has been marked as a duplicate of this bug. ***
I confirmed it with Fedora8 again. In the uniprocessor environment, it was confirmed that the test program operated normally. However, the test program fails in SIGSEGV in the multiprocessor environment. Best regards,
Created attachment 2163 [details] patch to fix this problem I propose a following patch. This patch adds lock between _dcigettext() and setlocale() using lock variables __libc_setlocale_lock . So _dcigettext() and setlocale() runs excluisively, and avoid segmentation fault. I comfirmed that problem is resolved with this patch. best regards, Ryo Hirukawa
The patch is in principal OK but since all gettext users only read the data it is overly restrictive. I've checked in an expanded version which avoids the problem.
Fix: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=9a69db292ac This went into glibc 2.8.