Data race through standard library function call

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

Описание

Этот дефект происходит когда:

  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, вызовите функциональный funcfunc вызывает неповторно используемую стандартную библиотечную функцию, 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

Информация о результате

Группа: параллелизм
Язык: C | C++
Значение по умолчанию: On
Синтаксис командной строки: DATA_RACE_STD_LIB
Удар: высоко
ID CWE: 366, 413
Введенный в R2017b