CertC-CON30ΒΆ
Clean up thread-specific storage
Required inputs: IR
The
tss_create() 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.
#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_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 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
In this compliant solution, each thread explicitly frees the thread-specific
storage returned by the
tss_get() function before terminating:
#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));
return 0;
}
/* ... 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:
#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(data);
}
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 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 | Remediation Cost | Priority | Level |
|---|---|---|---|---|---|
| CON30-C | Medium | Unlikely | Medium | P4 | L3 |
Possible Messages
Key |
Text |
Severity |
Disabled |
|---|---|---|---|
cleanup_thread_specific_storage |
Clean up thread-specific storage. |
None |
False |
Options
This rule shares the following common options: exclude_in_macros, exclude_messages_in_system_headers, excludes, extend_exclude_to_macro_invocations, includes, justification_checker, languages, post_processing, provider, report_at, severity
The following places define options that affect this rule: Stylechecks, Analysis-GlobalOptions
This rule has no individual options.