threads/135462: [PATCH] _thread_cleanupspecific() doesn't handle deleted keys

Marc Unangst mju at panasas.com
Thu Jun 11 02:10:02 UTC 2009


>Number:         135462
>Category:       threads
>Synopsis:       [PATCH] _thread_cleanupspecific() doesn't handle deleted keys
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-threads
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Jun 11 02:10:02 UTC 2009
>Closed-Date:
>Last-Modified:
>Originator:     Marc Unangst
>Release:        FreeBSD 7.0-RELEASE amd64
>Organization:
Panasas, Inc.
>Environment:
FreeBSD perf-x4 7.0-RELEASE FreeBSD 7.0-RELEASE #0: Sun Feb 24 10:35:36 UTC 2008     root at driscoll.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC  amd64
>Description:
Programs that use thread-specific data occasionally exit with a warning like this:

Thread 801202c70 has exited with leftover thread-specific data after 4 destructor iterations

This occurs because the thread-specific data cleanup routine that runs when a thread exits (_thread_cleanupspecific()) doesn't handle cleaning up keys that were deleted via pthread_key_delete() while the exiting thread still had a non-NULL value set for that key.  The test is

			if (_thread_keytable[key].allocated &&
			    (curthread->specific[key].data != NULL)) {

i.e., if the key exists globally and the current thread has a non-NULL key.  However, POSIX allows the application to delete the key without clearing the value in all threads; http://www.opengroup.org/onlinepubs/009695399/functions/pthread_key_delete.html says "The thread-specific data values associated with key need not be NULL at the time pthread_key_delete() is called."  POSIX also specifies that in this case, the destructor routine (if one is defined) is not invoked.  In this scenario, the cleanup routine should just set the thread-specific data value for that key to NULL and decrement curthread->specific_data_count.
>How-To-Repeat:
Compile and run the following program:

/*
 * bug_93732.c
 *
 * @author mju
 *
 * Unit test for bug 93732.
 *
 * Compile like this:
 *      gcc -Wall -pthread -o bug_93732 bug_93732.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <string.h>

void *test_thread(void *arg_ignored);

int main(int argc, char **argv)
{
  int ret;
  pthread_t thr;

  ret = pthread_create(&thr, NULL, test_thread, NULL);
  if (ret != 0) {
    fprintf(stderr, "pthread_create failed: %s\n", strerror(errno));
    exit(1);
  }
  printf("Thread created, waiting for exit\n");
  (void) pthread_join(thr, NULL);
  printf("Thread terminated, exiting\n");

  exit(0);
}

void *test_thread(void *arg_ignored)
{
  int ret;
  pthread_key_t key;
  int i = 2;

  ret = pthread_key_create(&key, NULL);
  if (ret != 0) {
    fprintf(stderr, "pthread_key_create failed: %s\n", strerror(errno));
    exit(1);
  }
  printf("Created key %d\n", (int) key);

  ret = pthread_setspecific(key, &i);
  if (ret != 0) {
    fprintf(stderr, "pthread_setspecific failed: %s\n", strerror(errno));
    exit(1);
  }
  printf("Set key to %p\n", &i);

  ret = pthread_key_delete(key);
  if (ret != 0) {
    fprintf(stderr, "pthread_key_delete failed: %s\n", strerror(errno));
    exit(1);
  }
  printf("Deleted key %d\n", (int) key);
  return NULL;
}

>Fix:
See attached patch file.

Patch attached with submission follows:

--- /net/paneast/sb7/mju/freebsd-svn/head/lib/libthr/thread/thr_spec.c	2009-05-26 17:45:27.000000000 -0400
+++ thr_spec.c	2009-06-10 14:57:06.000000000 -0400
@@ -131,9 +131,19 @@
 				curthread->specific[key].data = NULL;
 				curthread->specific_data_count--;
 			}
+			else if (curthread->specific[key].data != NULL) {
+				/* 
+				 * This can happen if the key is deleted via
+				 * pthread_key_delete without first setting the value
+				 * to NULL in all threads.  POSIX says that the
+				 * destructor is not invoked in this case.
+				 */
+				curthread->specific[key].data = NULL;
+				curthread->specific_data_count--;
+			}
 
 			/*
-			 * If there is a destructore, call it
+			 * If there is a destructor, call it
 			 * with the key table entry unlocked:
 			 */
 			if (destructor != NULL) {


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-threads mailing list