Описание
Гонка данных через стандартный вызов библиотечной функции происходит когда:
Несколько задач вызывают ту же стандартную библиотечную функцию.
Например, несколько задач вызывают strerror
функция.
Вызовы не защищены с помощью общей защиты.
Например, вызовы не защищены тем же критическим разделом.
Функции, отмеченные этим дефектом, как гарантируют, не будут повторно используемы. Функция повторно используема, если она может быть прервана и безопасно названа снова, прежде чем его предыдущий вызов завершает выполнение. Если функция не повторно используема, несколько задач, вызывающих функцию без защиты, могут вызвать проблемы параллелизма. Для списка функций, которые отмечаются, см. 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();
}
В этом примере, чтобы эмулировать многозадачное поведение, задают следующие опции:
На командной строке можно использовать следующее:
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 задайте следующие дополнительные опции:
На командной строке можно использовать следующее:
polyspace-bug-finder
-temporal-exclusions-file "C:\exclusions_file.txt"
где файл
C:\exclusions_file.txt
имеет следующую линию: