ПроблемаГонка данных через стандартный вызов функции библиотеки происходит, когда:
Несколько задач вызывают одну и ту же стандартную функцию библиотеки.
Например, несколько задач вызывают 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
.
Коррекция - Используйте 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 задайте следующие дополнительные опции:
В командной строке можно использовать следующее:
polyspace-bug-finder
-temporal-exclusions-file "C:\exclusions_file.txt"
где файл
C:\exclusions_file.txt
имеет следующую линию: