Описание
Гонка данных через стандартный вызов библиотечной функции происходит когда:
Несколько задач вызывают ту же стандартную библиотечную функцию.
Например, несколько задач вызывают функцию 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
имеет следующую строку: