Bug 5443 - SIGSEGV occurs in strerror_r()
Summary: SIGSEGV occurs in strerror_r()
Status: RESOLVED FIXED
Alias: None
Product: glibc
Classification: Unclassified
Component: libc (show other bugs)
Version: unspecified
: P2 normal
Target Milestone: ---
Assignee: Ulrich Drepper
URL:
Keywords:
: 5489 (view as bug list)
Depends on:
Blocks:
 
Reported: 2007-12-04 01:41 UTC by ryo
Modified: 2014-06-16 10:47 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:
fweimer: security+


Attachments
patch to fix this problem (738 bytes, patch)
2007-12-28 04:28 UTC, ryo
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description ryo 2007-12-04 01:41:46 UTC
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;
}
--------------------------------------------------------------------
Comment 1 Ismail "cartman" Donmez 2007-12-15 09:51:36 UTC
Can't confirm with glibc 2.7 on x86 32bit.
Comment 2 Ulrich Drepper 2007-12-16 21:54:29 UTC
*** Bug 5489 has been marked as a duplicate of this bug. ***
Comment 3 ryo 2007-12-17 09:20:12 UTC
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,
Comment 4 ryo 2007-12-28 04:28:45 UTC
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
Comment 5 Ulrich Drepper 2008-03-31 00:38:49 UTC
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.
Comment 6 Florian Weimer 2014-06-16 10:47:55 UTC
Fix: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=9a69db292ac

This went into glibc 2.8.