While using pthread_keyThe tss_create() to prepare a key to be used by various threads to maintain thread specific data, ensure that the data stored for a key is cleaned up after the key is no longer used, otherwise there could be potential memory leaks or misuse of data at the memory locations pointed to by the value.
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 terminate.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <threads.h>
#include <stdlib.h>
/* Global key to the thread-specific 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 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 | ||||
| Code Block | ||||
| ||||
#include <pthread.h> #include <stdio.h> #include<malloc.h> #define MAX_NUMBER_OF_THREADS 3 /* 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; int main( void ) { Â Â Â int i; Â Â Â pthread_t thread_id[MAX_NUMBER_OF_THREADS]; Â Â Â /* createCreate the thread specific data key before creating the threads */ Â Â Â pthread_key if (thrd_success != tss_create( &key, NULL ); Â Â Â )) { /* Handle error */ } /* createCreate threads that willwould usestore thespecific keystorage */ Â Â Â for (size_t i = 0; i < MAX_NUMBER_OF_THREADS; i++) { Â Â Â Â Â Â Â pthread if (thrd_success != thrd_create( &thread_id[i], NULL, function, NULL ); Â Â Â )) { /* Handle error */ } } Â Â Â for (size_t i = 0; i < MAX_NUMBER_OF_THREADS; i++) { Â Â Â Â Â Â Â int ret if (thrd_success != pthreadthrd_join(thread_id[i], NULL); Â Â Â Â Â Â Â printf("Join return value for thread[%d] = %d",i,ret); Â Â Â ) { /* Handle error */ } } Â Â Â pthread_keytss_delete(key); 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 Gettingto somethe randomthread-specific datastorage */ tss_t key; int *getfunction(void *dummy) { if (add_data() ) != 0) { Â Â Â intreturn *arr = malloc(2*sizeof(int)); Â Â Â *arr = rand-1; /* Report error */ } print_data(); Â Â Â *(arr+1) = rand( free(tss_get(key)); Â Â Â return arr0; } /* ... Other functions are unchanged */ |
Compliant Solution
This compliant solution invokes a destructor function registered during the call to tss_create() to automatically free any thread-specific storage:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <threads.h> #include <stdlib.h> /* Global key to the thread-specific storage */ tss_t key; enum { MAX_THREADS = 3 }; /* ... Other functions are unchanged */ void destructor(void *data) { free(datavoid *function(void) {    add_data();    print_data();    pthread_exit(NULL); } voidint add_datamain(void) {    int *data = get_data();    printf("some data = %p\n",data);    pthread_setspecific( key,    (void *) data); } void print_data() {    /* get this thread's global data from    * priority_key */    int *data = pthread_getspecific(    key );    printf("thread id = %u, data[0] = %d, data[1] = %d \n",(size_t)pthread_self(),*data,*(data+1)); }  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 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 free thread-specific objects results in memory leaks and could result in a denial-of-service attack.
Rule | Severity | Likelihood | Detectable | Repairable | Priority | Level |
|---|---|---|---|---|---|---|
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.
...