Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: REM Cost Reform

The tss_create() function creates a thread-specific storage pointed to pointer identified by a key.   Threads can allocated thread allocate thread-specific memory and associated it storage and associate the storage with a key that uniquely identifies the storage by calling the tss_set() function.   If not properly freed, this memory may be leaked or misused.   Ensure that thread specicy memory -specific storage 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 This memory is subsequently leaked when the threads returnterminate.

Code Block
bgColor#ffcccc
langc
#include <threads.h>
#include <stdlib.h>

/* Global key to the thread-specific data.storage */
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 Errorerror */
  }
  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.storage */
  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;
}

Compliant Solution

This In this compliant solution has , each thread explicitly free its memory before it is complete.frees the thread-specific storage returned by the tss_get() function before terminating:

Code Block
bgColor#ffcccc#ccccff
langc
#include <threads.h>
#include <stdlib.h>
  
/* Global key to the thread-specific data.storage */
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 also avoids the memory leak and frees 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 solution invokes a destructor function registered during the call to tss_get() returns NULL), it also ensures that the destructor function is not executed unnecessarily when the thread exits.create() to automatically free any thread-specific storage:

Code Block
bgColor#ccccff
langc
#include <threads.h>
#include <stdlib.h>

/* Global key to the thread-specific data.storage */
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.storage */
  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 free thread-specific objects could lead to results in memory leaks and misuse of datacould result in a denial-of-service attack.

Rule

Severity

Likelihood

Detectable

Remediation Cost

Repairable

Priority

Level

CON30-C

medium

Medium

Unlikely

unlikely

No

medium

No

P4

L3

P2

L3

Automated Detection

ToolVersionCheckerDescription
Astrée
Include Page
Astrée_V
Astrée_V

Supported, but no explicit checker
CodeSonar
Include Page
CodeSonar_V
CodeSonar_V

ALLOC.LEAK

Leak

Coverity
Include Page
Coverity_V
Coverity_V
ALLOC_FREE_MISMATCHPartially implemented, correct implementation is more involved
Cppcheck Premium

Include Page
Cppcheck Premium_V
Cppcheck Premium_V

premium-cert-con30-c
Helix QAC

Include Page
Helix QAC_V
Helix QAC_V

C1780, C1781, C1782, C1783, C1784


Parasoft C/C++test
Include Page
Parasoft_V
Parasoft_V

CERT_C-CON30-a

Ensure resources are freed

Polyspace Bug Finder

Include Page
Polyspace Bug Finder_V
Polyspace Bug Finder_V

CERT C: Rule CON30-CChecks for thread-specific memory leak (rule fully covered)

Related Vulnerabilities

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


...

Image Modified Image Modified Image Modified