Problem of l64a(3) on multithread application

Shunichi Sagawa s-sagawa@jp.fujitsu.com
Tue Feb 22 09:51:00 GMT 2005


Hello, 

In multithreaded application, the result of this function is likely
to be overwritten with the result of another thread.
l64a() returns the result with the pointer to a static buffer and
the static buffer is not protected for the multithreaded access.
I think that the static buffer should be protected.


<test program start>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#define MAX 10000

unsigned long int number_one = 1000000000;
unsigned long int number_two = 2000000000;
//the result of l64a(number_one) should be string_one
char *string_one = ".cgav";
//the result of l64a(number_two) should be string_two
char *string_two = ".ENBr/";
int find_error = 0;

void *thread_l64a()
{
	char *str_one;
	char *str_two;

	while (!find_error) {
		str_one = l64a(number_one);
		if(strcmp(string_one, str_one)) {
			find_error = 1;
		}
		str_two = l64a(number_two);
		if(strcmp(string_two, str_two)) {
			find_error = 1;
		}
	}
}

int main()
{
	int thread_create_ret;
	pthread_t l64a_one_id, l64a_two_id;

	thread_create_ret = 
		pthread_create(&l64a_one_id, NULL, (void *)thread_l64a, NULL);
	if(thread_create_ret != 0) {
		printf("thread create error\n");
		exit(1);
	}

	thread_create_ret = 
		pthread_create(&l64a_two_id, NULL, (void *)thread_l64a, NULL);
	if(thread_create_ret != 0) {
		printf("thread create error\n");
		exit(1);
	}
	
	pthread_join(l64a_one_id, NULL);
	pthread_join(l64a_two_id, NULL);
	
	if(find_error)
		printf("The return value of function l64a() is error!\n");

	return 0;
}
<test program end>


# ./tst-l64a 
The return value of function l64a() is error!
# 



<patch start>
diff -Nur glibc-2.3.4/stdlib/l64a.c glibc-2.3.4.new/stdlib/l64a.c
--- glibc-2.3.4/stdlib/l64a.c   2005-02-22 09:57:00.000000000 +0900
+++ glibc-2.3.4.new/stdlib/l64a.c       2005-02-22 09:57:49.000000000 +0900
@@ -18,6 +18,21 @@
    02111-1307 USA.  */
 
 #include <stdlib.h>
+#include <bits/libc-lock.h>
+
+/* This is the key for the thread specific memory.  */
+static __libc_key_t key;
+__libc_once_define (static, once);
+
+/* If nonzero the key allocation failed and we should better use a
+   static buffer than fail.  */
+static char local_buf[7];
+static char *static_buf;
+
+/* Destructor for the thread-specific data.  */
+static void init (void);
+static void free_key_mem (void *mem);
+static char *getbuffer (void);
 
 /* Conversion table.  */
 static const char conv_table[64] =
@@ -37,8 +52,11 @@
      long int n;
 {
   unsigned long int m = (unsigned long int) n;
-  static char result[7];
+  char *buffer;
   int cnt;
+  __libc_once (once, init);
+
+  buffer = getbuffer ();
 
   /* The standard says that only 32 bits are used.  */
   m &= 0xffffffff;
@@ -49,10 +67,61 @@
 
   for (cnt = 0; m > 0ul; ++cnt)
     {
-      result[cnt] = conv_table[m & 0x3f];
+      buffer[cnt] = conv_table[m & 0x3f];
       m >>= 6;
     }
-  result[cnt] = '\0';
+  buffer[cnt] = '\0';
+
+  return buffer;
+}
+
+
+/* Initialize buffer.  */
+static void
+init (void)
+{
+  if (__libc_key_create (&key, free_key_mem))
+    /* Creating the key failed.  This means something really went
+       wrong.  In any case use a static buffer which is better than
+       nothing.  */
+    static_buf = local_buf;
+}
+
+
+/* Free the thread specific data, this is done if a thread terminates.  */
+static void
+free_key_mem (void *mem)
+{
+  free (mem);
+  __libc_setspecific (key, NULL);
+}
+
+
+/* Return the buffer to be used.  */
+static char *
+getbuffer (void)
+{
+  char *result;
+
+  if (static_buf != NULL)
+    result = static_buf;
+  else
+    {
+      /* We don't use the static buffer and so we have a key.  Use it
+         to get the thread-specific buffer.  */
+      result = __libc_getspecific (key);
+      if (result == NULL)
+        {
+          /* No buffer allocated so far.  */
+          result = malloc (7);
+          if (result == NULL)
+            /* No more memory available.  We use the static buffer.  */
+            result = local_buf;
+          else
+            /* Set the tsd.  */
+            __libc_setspecific (key, result);
+        }
+    }
 
   return result;
 }
<patch end>


Best Regards,
Shunichi Sagawa



More information about the Libc-alpha mailing list