Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
bgColor#ffcccc
volatile int a, b;

/* Lock to enable threads to access a and b safely */
pthread_mutex_t global_mutlock = PTHREAD_MUTEX_INITIALIZER;

/* Lock to enable worker thread to initialize safely */
pthread_mutex_t init_mutlock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t init_mutlock_sig = PTHREAD_COND_INITIALIZER;

void cleanup_worker_thread(void* dummy) {
  int result;
  if ((result = pthread_mutex_unlock(&global_mut);lock)) != 0) {
    /* handle error */
  }
}

void* worker_thread(void* dummy) {
  int i, c;
  int result;

  /* set the cancelability flag during mutex. */
  /* this guarantees this block calls after pthread_cond_wait() and, more importantly, before pthread_cancel() */
  if ((result = pthread_mutex_lock(&init_mut);
 lock)) != 0) {
    /* handle error */
  }
  if ((result = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&i);)) != 0) {
    /* handle error */
  }
  pthread_cleanup_push( cleanup_worker_thread, NULL);
  if ((result = pthread_cond_signal(&init_mutlock_sig);) != 0) {
    /* handle error */
  }
  if ((result = pthread_mutex_unlock(&init_mut);lock)) != 0) {
    /* handle error */
  }

  while (1) {
    if ((result = pthread_mutex_lock(&global_mut);lock)) != 0) {
      /* handle error */
    }
    c = b;
    b = a;
    a = c;
    if ((result = pthread_mutex_unlock(&global_mut);lock)) != 0) {
      /* handle error */
    }

    /* now we're safe to cancel, creating cancel point */
    pthread_testcancel();
  }
  pthread_cleanup_pop( 1);
  return NULL;
}


int main(void) {
  int jresult;
  a = 5;
  b = 10;
  pthread_t worker;

  if ((result = pthread_mutex_lock(&init_mut);
 lock)) != 0) {
    /* handle error */
  }
  if ((result = pthread_create( &worker, NULL,(void*) worker_thread, NULL);) != 0) {
    /* handle error */
  }

  /* wait until canceltype is set */
  if ((result = pthread_cond_wait( &init_mutlock_sig, &init_mut);
 lock)) != 0) {
    /* handle error */
  }
  if ((result = pthread_mutex_unlock(&init_mut);lock)) != 0) {
    /* handle error */
  }

  /* do stuff, like data input */
  j = getc(stdin);
  /* since we don't know when the character is read in, the program could continue at any time */
  if ((result = pthread_cancel(worker);)) != 0) {
    /* handle error */
  }

  /* pthread_join waits for the thread to finish up before continuing */
  if ((result = pthread_join(worker, 0);) != 0) {
    /* handle error */
  }

  if ((result = pthread_mutex_lock(&global_mut);lock)) != 0) {
    /* handle error */
  }
  printf("a: %i | b: %i", a, b);
  if ((result = pthread_mutex_unlock(&global_mut);lock)) != 0) {
    /* handle error */
  }
  /* this will always print either "a: 5 | b: 10" or "a: 10 | b: 5" */

  /* clean up */
  if ((result = pthread_mutex_destroy(&init_mut);
 lock)) != 0) {
    /* handle error */
  }
  if ((result = pthread_cond_destroy(&init_mutlock_sig);) != 0) {
    /* handle error */
  }
  return 0;
}

This code is thread-safe in that it invokes no undefined behavior. However, this program can still create a race condition, because an asynchronous cancel can happen at any time. For instance, the worker thread could be cancelled right before the last line (a = c) and thereby lose the old value of b. Consequently the main thread might print that a and b have the same value.

...

Code Block
bgColor#ccccff
void* worker_thread(void* dummy) {
  /* ... */
  if ((result = pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,&i);)) != 0) {
    /* handle error */
  }
  /* ... */
}

Since this code limits cancellation of the worker thread to the end of the while loop, the worker thread can preserve the data invariant that a == b. Consequently, the program might print that a and b are both 5, or they are both 10, but they will always be revealed to have the same value when the worker thread is cancelled.

...

Incorrectly using threads that asynchronously cancel may result in silent corruption, resource leaks and, in the worst case, unpredictable interactions.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

POS47-C

medium

probable

low

P12

L1

Automated Detection

TODO

Related Vulnerabilities

...