While using pthread_key_The tss_create() to prepare a key to be used by various threads to maintain thread specific data, ensure that the thread specific data stored for a key is cleaned up while the thread finishes otherwise there could be potential memory leaks or misuse of data.
Noncompliant Code Example
function creates a thread-specific storage pointer identified by a key. Threads can allocate thread-specific 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. Ensure that thread-specific storage is freed.
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 terminateThe dynamically allocated block of memory for every thread results in a memory leak and is not destroyed in the noncompliant code example. This also leads to a potential vulnerability to access the one thread's specific data even after it exits apart from a memory leak.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <pthread<threads.h> #include <stdio<stdlib.h> #include<malloc.h> /* function prototypes */ void destructor(void * data); void* function(); void add_data(); void print_data(); /* global Global key to the thread -specific datastorage */ pthreadtss_key_t key; enum {max MAX_threadsTHREADS = 3 }; int main*get_data( void) ) { Â Â Â int i,ret; Â Â Â pthread_t thread_id[max_threads]; Â Â Â /* create the key before creating the threads */ Â Â Â pthread_key_create( &key, NULL ); Â Â Â /* create threads that would store specific data*/ Â Â Â for(i =0; i < max_threads; i++){ Â Â Â Â Â Â Â pthread_create( &thread_id[i], NULL, function, NULL ); Â Â Â } Â Â Â for(i=0;i < max_threads; i++){ Â Â Â Â Â Â Â ret = pthread_join(thread_id[i], NULL); if(ret !=0)*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) { /*Handle Errorreturn -1; /* Report error */ } Â Â Â } Â Â Â pthread_key_delete(key); } void *function(void) { Â Â Â add_data(); Â Â Â print_data(); Â Â Â pthread_exit(NULL); } void add_data(void) { Â Â Â int *data = get_data(); Â Â Â pthread_setspecific( key, if (thrd_success != tss_set(key, (void *)data); } int *get_data() ) { Â Â Â int *arr = malloc(2*sizeof(int)); Â Â Â *arr = rand(); Â Â Â *(arr+1) = rand(); Â Â Â return arr; }/* Handle error */ } return 0; } void print_data(void) { Â Â Â /* getGet this thread's global data from key */ Â Â Â int *data = pthreadtss_getspecificget(key); if (data != NULL) { /* Print data */ } |
Noncompliant Code Example
This code example is an improvement over the above sample where we try to avoid a memory leak. However if the address of the data is known it could still lead to a potential vulnerability. If for any reason the data received from an arbitrary function get_data() is NULL, a call to free(pthread_getspecific(key) would be equivalent to free(NULL) which most compilers might ignore but is not a good practice.
| Code Block | ||
|---|---|---|
| ||
#include <pthread.h> #include <stdio.h> #include<malloc.h> /* function prototypes */ void destructor(void * data); void* function(); void add_data(); void print_data(); /* global key to the thread specific data */ pthread_key_t key; enum {max_threads = 3}; } } int function(void *dummy) { if (add_data() != 0) { return -1; /* Report error */ } print_data(); return 0; } int main( void) ) { Â Â Â int i,ret; Â Â Â pthread thrd_t thread_id[maxMAX_threadsTHREADS]; Â Â Â /* createCreate the key before creating the threads */ Â Â Â pthread_key if (thrd_success != tss_create( &key, NULL ); Â Â Â )) { /* Handle error */ } /* createCreate threads that would store specific datastorage */ Â Â Â for (size_t i = 0; i < maxMAX_threadsTHREADS; i++) { Â Â Â Â Â Â Â pthread if (thrd_success != thrd_create( &thread_id[i], NULL, function, NULL ); Â Â Â )) { /* Handle error */ } } Â Â Â for (size_t i = 0; i < maxMAX_threadsTHREADS; i++) { Â Â Â Â Â Â Â Â ret = pthread if (thrd_success != thrd_join(thread_id[i], NULL)); if(ret !=0){ /* Handle Errorerror */ } } Â Â Â } Â Â Â pthreadtss_key_delete(key); } void *function(void) { Â Â Â add_data(); Â Â Â print_data(); free(pthread_getspecific(key)); Â Â Â pthread_exit(NULL); } void add_data(void) { Â Â Â int *data = get_data(); Â Â Â pthread_setspecific( key,(void *)data); } int *get_data() { Â Â Â int *arr = malloc(2*sizeof(int)); Â Â Â *arr = rand(); Â Â Â *(arr+1) = rand(); Â Â Â return arr; } void return 0; } |
Compliant Solution
In this compliant solution, each thread explicitly frees the thread-specific storage returned by the tss_get() function before terminating:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <threads.h> #include <stdlib.h> /* Global key to the thread-specific storage */ tss_t key; int function(void *dummy) { if (add_data() != 0) { return -1; /* Report error */ } print_data(); { Â Â Â /* get this thread's global data from key */ Â Â Â int *data = pthread_getspecific(key); /* Print data */ }free(tss_get(key)); return 0; } /* ... Other functions are unchanged */ |
Compliant Solution
The compliant solution attempts to avoid the memory leak and sets the thread specific data to NULL. An explicit destructor should be used to free memory resources and clean thread specific data. In case a particular thread does not maintain specific data the destructor would not be executed for that thread which eliminates the case of free(NULL) in case of second noncompliant code sample.This compliant solution invokes a destructor function registered during the call to tss_create() to automatically free any thread-specific storage:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <pthread<threads.h> #include <stdio<stdlib.h> #include<malloc.h> /* function prototypes */ void destructor(void * data); void* function(); void add_data(); void print_data(); /* global Global key to the thread -specific datastorage */ pthreadtss_key_t key; enum {max MAX_threadsTHREADS = 3 }; /* ... Other functions are unchanged */ void destructor(void * data) { Â Â Â pthread_setspecific(key, NULL); // thread specific should be cleared (could be sensitive) Â Â Â free(data); } int main( void) ) { Â Â Â int i; Â Â Â pthreadthrd_t thread_id[maxMAX_threadsTHREADS]; Â Â Â /* createCreate the thread specific data key before creating the threads */ Â Â Â pthread_key if (thrd_success != tss_create( &key, destructor ); Â Â Â )) { /* Handle error */ } /* createCreate threadthreads that willwould usestore thespecific keystorage */ Â Â Â for (size_t i = 0; i < maxMAX_threadsTHREADS; i++) { Â Â Â Â Â Â Â pthread if (thrd_success != thrd_create( &thread_id[i], NULL, function, NULL )); Â Â Â } Â Â Â Â for(i=0;i < max_threads; i++){ Â Â Â Â Â Â Â int ret = pthread_join(thread_id[i], NULL); Â Â Â Â Â Â Â if(ret !=0){ Â Â Â Â Â Â Â Â Â Â Â /* Handle Errorerror */ Â Â Â Â Â Â Â } Â Â Â } Â Â Â pthread_key_delete(key); } void *function() { Â Â Â add_data(); Â Â Â print_data(); Â Â Â pthread_exit(NULL); } void add_data() { Â Â Â int *data = get_data(); /* set current thread's data */ Â Â Â pthread_setspecific( key,(void *) data); } int *get_data(){ Â Â Â int *arr = malloc(2*sizeof(int)); /* Not handling the integer overflow */ Â Â Â *arr = rand(); Â Â Â *(arr+1) = rand(); Â Â Â return arr; } void print_data() { Â Â Â /* retrieve current thread's data from key */ Â Â Â int *data = pthread_getspecific for (size_t i = 0; i < MAX_THREADS; i++) { if (thrd_success != thrd_join(thread_id[i], NULL)) { /* Handle error */ } } tss_delete(key); /* Print Data */ } return 0; } |
Risk Assessment
Failing to destroy the objects could lead to free thread-specific objects results in memory leaks and misuse of datacould result in a denial-of-service attack.
Rule | Severity | Likelihood |
|---|
Detectable | Repairable | Priority | Level |
|---|
POS45-C
medium
probable
medium
CON30-C | Medium | Unlikely | No | No | P2 | L3 |
Automated Detection
| Tool | Version | Checker | Description | ||||||
|---|---|---|---|---|---|---|---|---|---|
| Astrée |
| Supported, but no explicit checker | |||||||
| CodeSonar |
| ALLOC.LEAK | Leak | ||||||
| Coverity |
| ALLOC_FREE_MISMATCH | Partially implemented, correct implementation is more involved | ||||||
| Cppcheck Premium |
| premium-cert-con30-c | |||||||
| Helix QAC |
| C1780, C1781, C1782, C1783, C1784 | |||||||
| Parasoft C/C++test |
| CERT_C-CON30-a | Ensure resources are freed | ||||||
| CERT C: Rule CON30-C | Checks for thread-specific memory leak (rule fully covered) |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
...