exponenta event banner

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

Избегайте расовых условий при использовании библиотечных функций

Описание

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

Избегайте расовых условий при использовании библиотечных функций. [1 ]

Внедрение Polyspace

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

Примеры

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

Проблема

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

  1. Несколько задач вызывают одну и ту же стандартную библиотечную функцию.

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

  2. Вызовы не резервируются с помощью общей защиты.

    Например, вызовы не защищены одним и тем же критическим участком.

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

Чтобы найти этот дефект, перед анализом необходимо указать параметры многозадачности. Чтобы задать эти параметры, на панели Конфигурация выберите Многозадачность. Дополнительные сведения см. в разделе Настройка многозадачного анализа в многозадачном пространстве вручную.

Риск

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

Вызовы также могут вызывать более серьезные уязвимости безопасности, такие как ненормальное завершение, атака типа «отказ в обслуживании» и нарушения целостности данных.

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

Чтобы устранить этот дефект, выполните одно из следующих действий.

  • Используйте исходную версию стандартной библиотечной функции, если она существует.

    Например, вместо strerror(), использовать strerror_r() или strerror_s(). Альтернативы функциям, отмеченным этим дефектом, см. в документации для CON33-C.

  • Защита вызовов функций с помощью общих критических разделов или временного исключения.

    Посмотрите Critical section details (-critical-section-begin -critical-section-end) и Temporally exclusive tasks (-temporal-exclusions-file).

    Чтобы определить существующие защиты, которые можно использовать повторно, см. таблицу и графики, связанные с результатом. В таблице показаны все пары конфликтующих вызовов. В столбце Access Protections отображаются существующие защиты для вызовов. Для просмотра последовательности вызовов функции, приводящей к конфликтам, щелкните по пиктограмме. Пример см. ниже.

Пример - Функция вызова стандартной библиотеки без резервирования из нескольких задач
#include <errno.h>
#include <stdio.h>
#include <string.h>

void begin_critical_section(void);
void end_critical_section(void);

FILE *getFilePointer(void);

void func(FILE *fp) {
  fpos_t pos;
  errno = 0;
  if (0 != fgetpos(fp, &pos)) {
    char *errmsg = strerror(errno);
    printf("Could not get the file position: %s\n", errmsg);
  }
}

void task1(void) {
    FILE* fptr1 = getFilePointer();
    func(fptr1);
}

void task2(void) {
     FILE* fptr2 = getFilePointer();
     func(fptr2);
}

void task3(void) {
     FILE* fptr3 = getFilePointer();
     begin_critical_section();
     func(fptr3);
     end_critical_section();
}

В этом примере для эмуляции многозадачности задайте следующие параметры.

ВыборСпецификация
Configure multitasking manually
Tasks (-entry-points)

task1

task2

task3

Critical section details (-critical-section-begin -critical-section-end)Запуск подпрограммыЗавершение процедуры
begin_critical_sectionend_critical_section

В командной строке можно использовать следующее:

 polyspace-bug-finder
   -entry-points task1,task2,task3
   -critical-section-begin begin_critical_section:cs1
   -critical-section-end end_critical_section:cs1

В этом примере задачи, task1, task2 и task3, вызовите функцию func. func вызывает неназначенную стандартную библиотечную функцию, strerror.

Хотя task3 требования func внутри критической секции другие задачи не используют ту же критическую секцию. Операции в критическом разделе task3 не являются взаимоисключающими с операциями в других задачах.

Эти три задачи вызывают функцию стандартной библиотеки без общей защиты. В подробных данных результата отображается каждая пара конфликтующих вызовов функций.

При щелчке по пиктограмме отображается последовательность вызовов функции, начиная с точки входа и заканчивая вызовом стандартной функции библиотеки. Вы также видите, что вызов начинается с task3 находится в критическом разделе. Запись Access Protections показывает функцию блокировки и разблокировки, которая начинает и заканчивает критический раздел. В этом примере рассматриваются функции. begin_critical_section и end_critical_section.

Исправление - использование новой версии стандартной библиотечной функции

Одной из возможных корректировок является использование новой версии стандартной библиотечной функции. strerror. Можно использовать версию POSIX ®strerror_r который обладает той же функциональностью, но также гарантирует безопасность резьбы.

#include <errno.h>
#include <stdio.h>
#include <string.h>

void begin_critical_section(void);
void end_critical_section(void);

FILE *getFilePointer(void);
enum { BUFFERSIZE = 64 };

void func(FILE *fp) {
  fpos_t pos;
  errno = 0;
  if (0 != fgetpos(fp, &pos)) {
    char errmsg[BUFFERSIZE];
    if (strerror_r(errno, errmsg, BUFFERSIZE) != 0) {
      /* Handle error */
    }
    printf("Could not get the file position: %s\n", errmsg);
  }
}

void task1(void) {
    FILE* fptr1 = getFilePointer();
    func(fptr1);
}

void task2(void) {
     FILE* fptr2 = getFilePointer();
     func(fptr2);
}

void task3(void) {
     FILE* fptr3 = getFilePointer();
     begin_critical_section();
     func(fptr3);
     end_critical_section();
}
Коррекция - размещение вызова функции в критическом разделе

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

Например, можно выполнить вызов промежуточной функции. func в одном и том же критическом разделе в трех задачах. Когда task1 входит в критический раздел, остальные задачи не могут войти в критический раздел, пока task1 покидает свой критический участок. Вызовы для func и, следовательно, вызовы strerror из трех задач не могут мешать друг другу.

Для реализации критического раздела в каждой из трех задач вызовите func между вызовами begin_critical_section и end_critical_section.

#include <errno.h>
#include <stdio.h>
#include <string.h>

void begin_critical_section(void);
void end_critical_section(void);

FILE *getFilePointer(void);

void func(FILE *fp) {
  fpos_t pos;
  errno = 0;
  if (0 != fgetpos(fp, &pos)) {
    char *errmsg = strerror(errno);
    printf("Could not get the file position: %s\n", errmsg);
  }
}

void task1(void) {
    FILE* fptr1 = getFilePointer();
    begin_critical_section();
    func(fptr1);
    end_critical_section();
}

void task2(void) {
     FILE* fptr2 = getFilePointer();
     begin_critical_section();
     func(fptr2);
     end_critical_section();
}

void task3(void) {
     FILE* fptr3 = getFilePointer();
     begin_critical_section();
     func(fptr3);
     end_critical_section();
}

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

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

На панели Конфигурация (Configuration) укажите следующие дополнительные параметры.

ВыборСтоимость
Temporally exclusive tasks (-temporal-exclusions-file)

task1 task2 task3

В командной строке можно использовать следующее:

 polyspace-bug-finder
     -temporal-exclusions-file "C:\exclusions_file.txt"
где файл C:\exclusions_file.txt имеет следующую строку:
task1 task2 task3

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

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

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

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