exponenta event banner

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

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

Описание

Этот дефект возникает в следующих случаях:

  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

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

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