Thread-specific memory leak

Динамически выделенная специфичная память не освобождается до конца потока

Описание

Этот дефект возникает, когда вы не освобождаете определенную потоком динамически выделенную память до конца потока.

Чтобы создать специфичное для потока хранилище, вы обычно делаете следующие шаги:

  1. Вы создаете ключ для специфичного для потока хранилища.

  2. Вы создаете потоки.

  3. В каждом потоке вы динамически выделяете устройство хранения данных, а затем связываете ключ с этим устройством хранения.

    После ассоциации можно считать сохраненные данные позже с помощью ключа.

  4. Перед завершением потока вы освобождаете определенную для потока память с помощью ключа.

Шашка помечает пути выполнения в потоке, где отсутствует последний шаг.

Шашка работает с этими семействами функций:

  • tss_get и tss_set (C11)

  • pthread_getspecific и pthread_setspecific (POSIX)

Риск

Данные, хранящиеся в памяти, доступны другим процессам даже после окончания потоков (утечка памяти). Помимо уязвимостей безопасности, утечки памяти могут уменьшить объем доступной памяти и снизить эффективность.

Зафиксировать

Освободите динамически выделенную память перед концом потока.

Вы можете явно освободить динамически выделенную память с такими функциями, как free.

Кроме того, когда вы создаете ключ, можно связать функцию деструктора с клавишей. Функция деструктора вызывается со значением ключа в качестве аргумента в конце потока. В теле функции деструктора можно освободить любую память, связанную с клавишей. Если вы используете этот метод, Bug Finder все еще помечает дефект. Игнорируйте этот дефект с соответствующими комментариями. Смотрите Адрес Результаты Polyspace через исправления ошибок или обоснования.

Примеры

расширить все

#include <threads.h>
#include <stdlib.h>
 
/* Global key to the thread-specific storage */
tss_t key;
enum { MAX_THREADS = 3 };
 

int add_data(void) {
  int *data = (int *)malloc(2 * sizeof(int));
  if (data == NULL) {
    return -1;  /* Report error  */
  }
  data[0] = 0;
  data[1] = 1;
 
  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 func(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], func, 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;
}

В этом примере стартовая функция каждого потока func вызывает две функции:

  • add_data: Эта функция динамически выделяет хранилище и связывает хранилище с ключом с помощью tss_set функция.

  • print_data: Эта функция считывает сохраненные данные с помощью tss_get функция.

В точках, где func возвращает, динамически выделенное хранилище не освобождено.

Коррекция - свободная динамически выделенная память явным образом

Одной из возможных коррекций является явное освобождение динамически выделенной памяти перед оставлением начальной функции потока. См. выделенное изменение в исправленной версии.

В этой исправленной версии дефект все еще появляется на return оператор в разделе обработки ошибок func. Дефект не может возникнуть на практике, потому что раздел обработки ошибок вводится только в случае сбоя динамического выделения памяти. Проигнорируйте этот оставшийся дефект с соответствующими комментариями. Смотрите Адрес Результаты Polyspace через исправления ошибок или обоснования.

#include <threads.h>
#include <stdlib.h>
 
/* Global key to the thread-specific storage */
tss_t key;
enum { MAX_THREADS = 3 };
 

int add_data(void) {
  int *data = (int *)malloc(2 * sizeof(int));
  if (data == NULL) {
    return -1;  /* Report error  */
  }
  data[0] = 0;
  data[1] = 1;
 
  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 func(void *dummy) {
  if (add_data() != 0) {
    return -1;  /* Report error */
  }
  print_data();
  free(tss_get(key));
  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], func, 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;
}

Информация о результатах

Группа: Параллелизм
Язык: C | C++
По умолчанию: Off
Синтаксис командной строки : THREAD_MEM_LEAK
Влияние: Средний
ИДЕНТИФИКАТОР CWE: 401, 404
Введенный в R2018b