CERT C++: CON33-C

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

Описание

Управляйте определением

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

Примеры

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

Описание

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

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

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

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

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

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

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

Риск

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

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

Фиксация

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

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

    Например, вместо 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)Starting routineEnding routine
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

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

Группа: 10. Параллелизм (CON)

Введенный в R2019a


[1]  Это программное обеспечение было создано MathWorks, включающим фрагменты: “Веб-сайт SEI CERT-C”, © 2017 Carnegie Mellon University, веб-сайт SEI CERT-C © 2017 Carnegie Mellon University”, CERT SEI C Кодирование Стандарта – Правил для Разработки безопасных, Надежных и Защищенных систем – 2 016 Выпусков”, © 2016 Carnegie Mellon University, and “CERT SEI Стандарт Кодирования C++ – Правил для Разработки безопасных, Надежных и Защищенных систем на C++ – 2 016 Выпусков” © 2016 Carnegie Mellon University, со специальным разрешением от его Института программной инженерии.

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

Это программное обеспечение и сопоставленная документация не были рассмотрены, ни являются подтвержденным Университетом Карнеги-Меллон или его Институтом программной инженерии.