CERT C: Rule CON33-C

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

Описание

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

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

Реализация Polyspace

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

Примеры

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

Проблема

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

  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.

Коррекция - Используйте Reentrant Version функции стандартной библиотеки

Одной из возможных коррекций является использование перевходной версии стандартной функции библиотеки 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 Университет Карнеги Меллон, с специального разрешения от его Института программной инженерии.

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

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