chanWhile using The tss_create() to prepare a key for use by various threads to maintain thread-specific data, ensure that the thread-specific data stored for a key is cleaned up when the thread exits; otherwise, potential memory leaks or misuse of data can occur.
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 enabling access to the one thread's specific data even after it exits from a memory leak.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <threads.h> #include <stdlib.h> /* globalGlobal key to the thread-specific datastorage */ tss_t key; enum {max MAX_threadsTHREADS = 3 }; int *get_data(void) { int *arr = (int *)malloc(2 * sizeof(int)); if (arr == NULL) { return arr; /* HandleReport Errorerror */ } arr[0] = rand()10; arr[1] = rand()42; return arr; } voidint add_data(void) { int *data = get_data(); if (data == NULL) { int result; return -1; /* Report error */ } if ((resultthrd_success != tss_set( key, (void *)data)) != thrd_success) { /* Handle Errorerror */ } return 0; } void print_data(void) { /* getGet this thread's global data from key */ int *data = tss_get(key); if (data != NULL) { /* printPrint data */ } } voidint *function(void * dummy) { if (add_data(); print_data(); thrd_exit(0) != 0) { return -1; /* Report error */ } print_data(); return NULL0; } int main(void) { int i,result; thrd_t thread_id[maxMAX_threadsTHREADS]; /* createCreate the key before creating the threads */ if ((resultthrd_success != tss_create( &key, NULL )) != thrd_success) { /* Handle Errorerror */ } /* createCreate threads that would store specific datastorage */ for (size_t i = 0; i < maxMAX_threadsTHREADS; i++) { if ((resultthrd_success != thrd_create( &thread_id[i], function, NULL )) != thrd_success) { /* Handle Errorerror */ } } for (size_t i = 0; i < maxMAX_threadsTHREADS; i++) { if ((resultthrd_success != thrd_join(thread_id[i], NULL)) != thrd_success) { /* Handle Errorerror */ } } if ((result = tss_delete(key)) != thrd_success) { /* Handle Error */; } return 0; } |
Noncompliant Code Example
Compliant Solution
In this compliant solution, each thread explicitly frees the thread-specific storage returned by the This 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).) function before terminating:
| Code Block | ||||
|---|---|---|---|---|
| ||||
void *#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(); free( tss_get(key)); thrd_exit(0); return NULL0; } /* ... Other functions are unchanged */ int main(void) { /* ... */ if ((result = tss_delete(key)) != thrd_success) { /* Handle Error */ } 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.
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 * | ||||
| Code Block | ||||
| ||||
void* destructor(void* data) { free(data); return NULL; } int main(void) { int i,result; thrd_t thread_id[maxMAX_threadsTHREADS]; /* createCreate the key before creating the threads */ if ((resultthrd_success != tss_create( &key, destructor)) != thrd_success) { /* Handle Errorerror */ } /* ... Create threads that would store specific storage */ for (size_t i = 0; i < MAX_THREADS; i++) { if ((resultthrd_success != tssthrd_delete(key)) create(&thread_id[i], function, NULL)) { /* Handle error */ } } for (size_t i = 0; i < MAX_THREADS; i++) { if (thrd_success != thrd_success_join(thread_id[i], NULL)) { /* Handle Errorerror */ } } 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 | Repairable | Priority | Level |
|---|---|---|---|
CON30-C | Medium |
Unlikely |
No |
No |
P4
L3
...
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.
...