You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 45 Next »

The tss_create() function creates a thread-specific storage pointed to by a key.  Threads can allocated thread specific memory and associated it with a key by calling the tss_set() function.  If not properly freed, this memory may be leaked or misused.  Ensure that thread specicy memory is freed by using a destructor.

Noncompliant Code Example

In this noncompliant code example, each thread dynamically allocates storage in the get_data() function which is then associated with the global key by the call to tss_set() in the add_data() function.  This memory is subsequently leaked when the threads return.

#include <threads.h>
#include <stdlib.h>

/* Global key to the thread-specific data. */
tss_t key;
enum { MAX_THREADS = 3 };

int *get_data(void) {
  int *arr = (int *)malloc(2 * sizeof(int));
  if (arr == NULL) {
    return arr;  /* Report error  */
  }
  arr[0] = 10;
  arr[1] = 42;
  return arr;
}

int add_data(void) {
  int *data = get_data();
  if (data == NULL) {
    return -1;	/* Report error */
  }

  if (thrd_success != tss_set(key, (void *)data)) {
    /* Handle Error */
  }
  return 0;
}

void print_data(void) {
  /* Get this thread's global data from key. */
  int *data = tss_get(key);

  if (data != NULL) {
    /* Print data */
  } 
}

int function(void *dummy) {
  if (add_data() != 0) {
    return -1;	/* Report error */
  }
  print_data();
  return 0;
}

int main(void) {
  thrd_t thread_id[MAX_THREADS];

  /* Create the key before creating the threads. */
  if (thrd_success != tss_create(&key, NULL)) {
    /* Handle error */
  }

  /* Create threads that would store specific data. */
  for (size_t i = 0; i < MAX_THREADS; i++) {
    if (thrd_success != thrd_create(&thread_id[i], function, NULL)) {
      /* Handle error */
    }
  }

  for (size_t i = 0; i < MAX_THREADS; i++) {
    if (thrd_success != thrd_join(thread_id[i], NULL)) {
      /* Handle error */
    }
  }

  tss_delete(key);
  return 0;
}

Noncompliant Code Example

This noncompliant code example is an improvement over the previous example. However, even if the address of the data is known, it still presents a potential vulnerability. If, for any reason, the data received from an arbitrary function, get_data(), is NULL, a call to free(tss_get(key)) would be equivalent to free(NULL), for which the compiler takes no action, according to the standard. This solution, however, does not avoid a call to free() when there is no thread-specific data, which is not unsafe but can be avoided (as shown in the subsequent compliant solution).

#include <threads.h>
#include <stdlib.h>
 
/* Global key to the thread-specific data. */
tss_t key;
 
int function(void *dummy) {
  if (add_data() != 0) {
    return -1;	/* Report error */
  }
  print_data();
  free(tss_get(key));
  return 0;
}

/* ... Other functions are unchanged. */

int main(void) {
  /* ... */
  tss_delete(key);
  return 0;
}

Compliant Solution

This compliant solution avoids the memory leak and destroys the value associated to a key for the thread-specific data. A destructor function is a clean approach because it automatically sets the specific value associated to the key to NULL when the thread exits. In case any particular thread does not maintain specific data (a call to tss_get() returns NULL), it also ensures that the destructor function is not executed unnecessarily when the thread exits.

#include <threads.h>
#include <stdlib.h>

/* Global key to the thread-specific data. */
tss_t key;
enum { MAX_THREADS = 3 };

/* ... Other functions are unchanged. */
void destructor(void *data) {
  free(data);
  return 0;
}
 
int main(void) {
  thrd_t thread_id[MAX_THREADS];

  /* Create the key before creating the threads. */
  if (thrd_success != tss_create(&key, destructor)) {
    /* Handle error */
  }

  /* Create threads that would store specific data. */
  for (size_t i = 0; i < MAX_THREADS; i++) {
    if (thrd_success != thrd_create(&thread_id[i], function, NULL)) {
      /* Handle error */
    }
  }

  for (size_t i = 0; i < MAX_THREADS; i++) {
    if (thrd_success != thrd_join(thread_id[i], NULL)) {
      /* Handle error */
    }
  }

  tss_delete(key);
  return 0;
}

Risk Assessment

Failing to destroy thread-specific objects could lead to memory leaks and misuse of data.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CON30-C

medium

unlikely

medium

P4

L3

Related Vulnerabilities

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


 

  • No labels