Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Wiki Markup
In threading, pthreads have the option of being set to cancel immediately or defer until a specific cancellation point. Canceling asynchronously (immediately) is dangerous, however, because most threads are in fact not safe to cancel immediately.

...



The [IEEE standards page|AA. References#Open Group 04] states that:

...


{quote}only functions that are cancel-safe may be called from a thread that is asynchronously cancelable.

...

{quote}

Canceling asynchronously would follow the same route as passing a signal in to the thread to kill it, thus posing similarities to [POS44-C. Do not use signals to terminate threads|POS44-C. Do not use signals to terminate threads], which is strongly related to [SIG02-C. Avoid using signals to implement normal functionality|SIG02-C. Avoid using signals to implement normal functionality]. These expand on the dangers of canceling a thread suddenly as this can create a [data race condition

...

Noncompliant Code Example

In this noncompliant code example the worker thread is doing something as simple as swapping a and b repeatedly.

This code uses two locks. The init_mut mutex is used to ensure that the worker thread is initialized safely, and it notifies the main thread once it has set its cancellation policy. And the global_mut mutex is used by both threads to guarantee exclusive access to the global variables a and b. Furthermore, the worker thread uses the cleanup_worker function to clear the global_mut mutex, in case it gets cancelled while the lock is held.

Code Block
bgColor#ffcccc

volatile int a, b;

/* Lock to enable threads to access a and b safely */
pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER|BB. Definitions#data race].


h2. Noncompliant Code Example

In this noncompliant code example the {{worker}} thread is doing something as simple as swapping {{a}} and {{b}} repeatedly.

This code uses one lock. The {{init_lock}} mutex is used to ensure that the worker thread is initialized safely, because the worker thread notifies the main thread once it has set its cancellation policy. 

{code:bgColor=#ffcccc}
/* We need no mutex on these variables. They are initiailized by the main thread,
   then managed exclusively by the worker thread. Finally after the worker thread
   is cancelled, they are once again accessed by the main thread. */
volatile int a, b;

/* Lock to enable worker thread to initialize safely */
pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t init_lock_sig = PTHREAD_COND_INITIALIZER;

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

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

  /* setSet the cancelability flag duringwhile mutex. */
  /* this guarantees this block calls after pthread_cond_wait() and, more importantly, before pthread_cancel() *holding init lock. */
  if ((result = pthread_mutex_lock(&init_lock)) != 0) {
    /* handleHandle error */
  }
  if ((result = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,&i)) != 0) {
    /* handleHandle error */
  }
  pthread_cleanup_push( cleanup_worker_thread, NULL);
  if ((result = pthread_cond_signal(&init_lock_sig)) != 0) {
    /* handleHandle error */
  }
  if ((result = pthread_mutex_unlock(&init_lock)) != 0) {
    /* handleHandle error */
  }

  while (1) {
    if ((result = pthread_mutex_lock(&global_lock)) != 0) {
      /* handle error */
    }
    c = b;
    b = a;
    a = c;
    if ((result = pthread_mutex_unlock(&global_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 result;
  a = 5;
  b = 10;
  pthread_t worker;

  /* Creates thread while init_lock is held. This guarantees that we
     don't continue until worker thread is safely initialized. */
  if ((result = pthread_mutex_lock(&init_lock)) != 0) {
    /* handleHandle error */
  }
  if ((result = pthread_create( &worker, NULL, worker_thread, NULL)) != 0) {
    /* handleHandle error */
  }

  /* wait until canceltype is set */
  if ((result = pthread_cond_wait( &init_lock_sig, &init_lock)) != 0) {
    /* handleHandle error */
  }
  if ((result = pthread_mutex_unlock(&init_lock)) != 0) {
    /* handleHandle error */
  }

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

  /* pthread_join waits for the thread worker thread, and wait for it to finish up before continuing */
  if ((result = pthread_joincancel(worker, 0)) != 0) {
    /* handleHandle error */
  }

  if ((result = pthread_mutex_lock(&global_lockjoin(worker, 0)) != 0) {
    /* handleHandle error */
  }

  printf("a: %i | b: %i\n", a, b);
  if ((result = pthread_mutex_unlock(&global_lock)) != 0) {
    /* handle error */
  }
  /* this willa should always printbe either "a: 5 | b: 10" or "a: 10 | b: 5" different from b */

  /* cleanClean up */
  if ((result = pthread_mutex_destroy(&init_lock)) != 0) {
    /* handleHandle error */
  }
  if ((result = pthread_cond_destroy(&init_lock_sig)) != 0) {
    /* handleHandle error */
  }
  return 0;
}

...

{code}

{mc}
For test purposes, the 'Do stuff' section in {{main()}} can just contain {{getc(stdin);}}
{mc}

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.

...

Compliant Solution

From IEEE standards page:

...




h2. Compliant Solution

From [IEEE standards page|AA. References#Open Group 04]:
{quote}
The cancelability state and type of any newly created threads, including the thread in which main() was first invoked, shall be PTHREAD_CANCEL_ENABLE and PTHREAD_CANCEL_DEFERRED respectively.

...


{quote}
Since the default condition according to the IEEE standards for POSIX is {{PTHREAD_CANCEL_DEFERRED}}, one would simply not set cancel type for the compliant solution.

...



However, since not all compilers are necessarily guaranteed to follow standards, you should also explicitly call {{pthread_setcanceltype()}} with {{PTHREAD_CANCEL_DEFERRED}}. All remaining code is identical to the noncompliant example.

...



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

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.

...

Risk Assessment

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

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Other Languages

In Java, similar reasoning resulted in the deprecation of Thread.stop() and appears in the Java Secure Coding Standard as CON24-J. Do not use Thread.stop() to terminate threads .

References

...




h2. Risk Assessment

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 | {color:#ff0000}{*}P12{*}{color} | {color:#ff0000}{*}L1{*}{color} |

h3. Automated  Detection

TODO

h3. Related  Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule  on the [CERT website|https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+POS47-C].

h3. Other Languages

In Java, similar reasoning resulted in the deprecation of {{Thread.stop()}} and appears in the Java Secure Coding Standard as [CON24-J. Do not use Thread.stop() to terminate threads|java:CON24-J. Do not use Thread.stop() to terminate threads] .

h2. References

\[[MKS|AA. References#MKS]\] [{{pthread_cancel()}} Man Page|http://www.mkssoftware.com/docs/man3/pthread_cancel.3.asp]
\[[Open Group 04|AA. References#Open Group 04]\] [Threads Overview|http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html]