exponenta event banner

CERT C: CON30-C правил

Очистка хранилища для конкретных потоков

Описание

Определение правила

Очистка зависящего от потока хранилища. [1 ]

Внедрение Polyspace

Эта проверка проверяет наличие утечки памяти для конкретного потока.

Примеры

развернуть все

Проблема

Утечка памяти для конкретного потока происходит, когда динамически выделенная память для конкретного потока не освобождается до конца потока.

Для создания системы хранения данных, зависящей от потока, обычно выполняются следующие действия:

  1. Создается ключ для хранения данных, специфичных для потоков.

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

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

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

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

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

Средство проверки работает с этими семействами функций:

  • tss_get и tss_set (C11)

  • pthread_getspecific и pthread_setspecific (POSIX)

Риск

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

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

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

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

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

Пример - Память не освобождается в конце потока
#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. Дефект не может возникнуть на практике, поскольку раздел обработки ошибок вводится только в случае сбоя динамического выделения памяти. Проигнорируйте этот оставшийся дефект с соответствующими комментариями. См. раздел Результаты анализа пространства адресов с помощью исправлений ошибок или обоснований.

#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;
}

Проверить информацию

Группа: Правило 14. Параллелизм (CON)
Представлен в R2019a

[1] Данное программное обеспечение было создано компанией MathWorks и включает в себя следующие компоненты: «Веб-сайт SEI CERT-C», © 2017 Университет Карнеги-Меллон, веб-сайт SEI CERT-C + + © 2017 Университет Карнеги-Меллон, "Стандарт кодирования SEI CERT C - Правила разработки безопасных, Надежные и безопасные системы - 2016 Edition ", © 2016 Университет Карнеги-Меллон, и "Стандарт кодирования SEI CERT C++ - Правила разработки безопасных, Надежные и безопасные системы в C++ - 2016 Edition "© 2016 Университет Карнеги-Меллон, со специальным разрешением от его Института программного обеспечения.

ЛЮБОЙ МАТЕРИАЛ УНИВЕРСИТЕТА КАРНЕГИ МЕЛЛОНА И/ИЛИ ЕГО ПРОГРАММНОГО ИНЖЕНЕРНОГО ИНСТИТУТА, СОДЕРЖАЩИЙСЯ В НАСТОЯЩЕМ ДОКУМЕНТЕ, ПОСТАВЛЯЕТСЯ КАК ЕСТЬ. УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, В ОТНОШЕНИИ ЛЮБЫХ ВОПРОСОВ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИИ ПРИГОДНОСТИ ДЛЯ ЦЕЛЕЙ ИЛИ ТОВАРНОЙ ПРИГОДНОСТИ, ИСКЛЮЧИТЕЛЬНОСТИ ИЛИ РЕЗУЛЬТАТОВ, ПОЛУЧЕННЫХ ОТ ИСПОЛЬЗОВАНИЯ УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ В ОТНОШЕНИИ СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКИХ ПРАВ.

Данное программное обеспечение и связанная с ним документация не были рассмотрены и не одобрены Университетом Карнеги-Меллона или его Институтом разработки программного обеспечения.