exponenta event banner

CERT C: Rec. CON05-C

Не выполнять операции, которые могут блокировать при удержании блокировки

Описание

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

Не выполняйте операции, которые могут блокировать при удержании блокировки. [1 ]

Внедрение Polyspace

Эта проверка проверяет блокировку при удержании блокировки.

Примеры

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

Проблема

Операция блокировки при удержании блокировки происходит, когда задача (поток) выполняет потенциально длительную операцию при удержании блокировки.

Средство проверки рассматривает вызовы этих функций как потенциально длительные:

  • Функции, получающие доступ к сети, такие как recv

  • Функции вызова системы, такие как fork, pipe и system

  • Функции для операций ввода-вывода, такие как getchar и scanf

  • Функции обработки файлов, такие как fopen, remove и lstat

  • Функции управления каталогами, такие как mkdir и rmdir

Средство проверки автоматически обнаруживает определенные примитивы, которые удерживают и снимают блокировку, например, pthread_mutex_lock и pthread_mutex_unlock. Полный список автоматически обнаруживаемых примитивов см. в разделе Автоматическое обнаружение создания потоков и критического сечения в Polyspace.

Риск

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

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

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

Некоторые функции, обнаруженные этой проверкой, могут вызываться таким образом, чтобы не делать их потенциально длинными. Например, функция recv может вызываться с параметром O_NONBLOCK что приводит к сбою вызова, если сообщение недоступно. При вызове с этим параметром recv не ожидает, пока сообщение станет доступным.

Пример - Операции сетевого ввода-вывода с recv При удержании блокировки
#include <pthread.h>
#include <sys/socket.h>

pthread_mutexattr_t attr;
pthread_mutex_t mutex;
 
void thread_foo(void *ptr) {
  unsigned int num;
  int result;
  int sock;
 
  /* sock is a connected TCP socket */
 
  if ((result = pthread_mutex_lock(&mutex)) != 0) {
    /* Handle Error */
  }
 
  if ((result = recv(sock, (void *)&num, sizeof(unsigned int), 0)) < 0) {
    /* Handle Error */
  }
 
  /* ... */
 
  if ((result = pthread_mutex_unlock(&mutex)) != 0) {
    /* Handle Error */
  }
}
 
int main() {
  pthread_t thread;
  int result;
 
  if ((result = pthread_mutexattr_settype(
      &attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
    /* Handle Error */
  }
 
  if ((result = pthread_mutex_init(&mutex, &attr)) != 0) {
    /* Handle Error */
  }
 
  if (pthread_create(&thread, NULL,(void*(*)(void*))& thread_foo, NULL) != 0) {
    /* Handle Error */
  }
 
  /* ... */
 
  pthread_join(thread, NULL);
 
  if ((result = pthread_mutex_destroy(&mutex)) != 0) {
    /* Handle Error */
  }
 
  return 0;
}

В этом примере в каждом потоке, созданном с помощью pthread_create, функция thread_foo выполняет операцию сетевого ввода-вывода с помощью recv после получения блокировки с pthread_mutex_lock. Другие потоки, использующие ту же переменную блокировки mutex необходимо дождаться завершения операции, а блокировка станет доступной.

Исправление - Выполнение операции блокирования перед получением блокировки

Одной из возможных корректировок является вызов recv перед захватом замка.

#include <pthread.h>
#include <sys/socket.h>

pthread_mutexattr_t attr;
pthread_mutex_t mutex;
 
void thread_foo(void *ptr) {
  unsigned int num;
  int result;
  int sock;
 
  /* sock is a connected TCP socket */
  if ((result = recv(sock, (void *)&num, sizeof(unsigned int), 0)) < 0) {
    /* Handle Error */
  }
  
  if ((result = pthread_mutex_lock(&mutex)) != 0) {
    /* Handle Error */
  }
 
    /* ... */
 
  if ((result = pthread_mutex_unlock(&mutex)) != 0) {
    /* Handle Error */
  }
}
 
int main() {
  pthread_t thread;
  int result;
 
  if ((result = pthread_mutexattr_settype(
      &attr, PTHREAD_MUTEX_ERRORCHECK)) != 0) {
    /* Handle Error */
  }
 
  if ((result = pthread_mutex_init(&mutex, &attr)) != 0) {
    /* Handle Error */
  }
 
  if (pthread_create(&thread, NULL,(void*(*)(void*))& thread_foo, NULL) != 0) {
    /* Handle Error */
  }
 
  /* ... */
 
  pthread_join(thread, NULL);
 
  if ((result = pthread_mutex_destroy(&mutex)) != 0) {
    /* Handle Error */
  }
 
  return 0;
}

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

Группа: Rec. 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 Университет Карнеги-Меллон, со специальным разрешением от его Института программного обеспечения.

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

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