[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#400960: libc6-i686: Thread cancelation while in pthread_cond_wait corrupts condition variable



Package: libc6-i686
Version: 2.3.6.ds1-8
Severity: normal


If a thread is canceled while waiting for a condition in
pthread_cond_wait(), the used condition variable is left in a
corrupted state. For example calling pthread_cond_destroy() on it will
fail. Moreover, the canceled thread does not properly lock the
associated mutex before running any cleanup handlers.

See the attached example program.

The specific combination that shows the problem is:
libc6-i686 and linux-image-2.6.18-3-686 (either the debian kernel package or a vanilla kernel)

If either libc6-i686 is removed or a 2.6.17 kernel is used the problem
goes away. Another way to make it go away is to set
LD_ASSUME_KERNEL=2.4.19. It appears that the broken combination is
NPTL with kernel 2.6.18 or later.

-- System Information:
Debian Release: 4.0
  APT prefers unstable
  APT policy: (800, 'unstable')
Architecture: i386 (i686)
Shell:  /bin/sh linked to /bin/bash
Kernel: Linux 2.6.18-3-686
Locale: LANG=C, LC_CTYPE=C (charmap=ANSI_X3.4-1968)

Versions of packages libc6-i686 depends on:
ii  libc6                        2.3.6.ds1-8 GNU C Library: Shared libraries

libc6-i686 recommends no packages.

-- debconf-show failed
/* Test program for pthread cancelation while waiting on a condition variable.

Expected output:
action: locking mutex
action: got mutex
Condition false, going to sleep.
cleanup_hook for action: unlocking  mutex
action: unlocked  mutex

Output in the case of kernel 2.6.18-3 and libc6-i686:
action: locking mutex
action: got mutex
Condition false, going to sleep.
cleanup_hook for action: unlocking  mutex
cleanup_hook: error unlocking mutex: Operation not permitted
action: unlocked  mutex
main: Wave Condition error Device or resource busy

*/

#include <pthread.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#include <error.h>
#include <string.h>
#include <assert.h>

static int resource_ready = 0;

static  pthread_t thread;
static pthread_attr_t ta;
static pthread_mutex_t mutex;
static pthread_mutexattr_t mt_attr;
static pthread_cond_t condition;

#define DEBUG 1
#define es_log(level, args ... ) printf(args); printf("\n");

/* <Mutex helpers */

/* Helper functions and macros for managing mutexes */
#ifndef DEBUG
#define MUTEX_LOCK(mutex) pthread_mutex_lock(&mutex);
#define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(&mutex);
#else
#define MUTEX_LOCK(mutex) \
  if (pthread_mutex_lock(&mutex) != 0) \
{\
  es_log(2, "%s: Error locking mutex.", __func__); \
  exit(3); \
}

#define MUTEX_UNLOCK(mutex) \
  { \
int ret_;\
  if ((ret_ = pthread_mutex_unlock(&mutex)) != 0) \
{\
  es_log(2, "%s: error unlocking mutex: %s", __func__, strerror(ret_)); \
  } \
}
#endif

static void cleanup_hook(void *function_name) {
  es_log(2, "cleanup_hook for %s: unlocking  mutex", function_name);
  MUTEX_UNLOCK(mutex);
  es_log(2, "%s: unlocked  mutex", (char *) function_name);
}

#define DO_UNLOCK \
  es_log(2, "%s: unlocking mutex", __func__); \
  MUTEX_UNLOCK(mutex); \
  pthread_cleanup_pop(0); \
  es_log(2, "%s: unlocked mutex", __func__);

#define DO_LOCK \
  es_log(2, "%s: locking mutex", __func__); \
  pthread_cleanup_push(cleanup_hook, (void *) __func__); \
  MUTEX_LOCK(mutex); \
  es_log(2, "%s: got mutex", __func__); \
  pthread_testcancel();

/* > */





static void *action(void* arg) {
  DO_LOCK;
  while (!resource_ready) {
	printf("Condition false, going to sleep.\n");
	pthread_cond_wait(&condition, &mutex);
	printf("Woke up, going to test condition.\n");
  }
  printf("Condition passed..\n");
  DO_UNLOCK;
  return 0;
}

int main() {
  int ret; 
  /* Initialize thread, mutex and condition */
	pthread_mutexattr_init(&mt_attr);
#ifdef DEBUG
	pthread_mutexattr_settype(&mt_attr, PTHREAD_MUTEX_ERRORCHECK_NP);
#endif
	pthread_mutex_init(&mutex, &mt_attr);

	pthread_cond_init(&condition, NULL);
	
  /* Start thread*/
	pthread_attr_init(&ta);
	pthread_create(&thread, &ta, action, NULL);

  /* Wait for thread to go to sleep on condition */
	sleep(3);

	/*Cancel thread */
	
	pthread_cancel(thread);

	/* Join thread */
	ret = pthread_join(thread, NULL);
	assert(ret == 0);
	/* At this point, no other thread is running */

	ret = pthread_mutex_trylock(&mutex);
	if (ret != 0) {
	  printf("main: trylock error: %s", strerror(ret));
	}

ret = pthread_cond_destroy(&condition);
  if (ret)
  {
	es_log(2, "main: Wave Condition error %s", strerror(ret));
  }
	
	return 0;
}

Reply to: