Data race on adjacent bit fields

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

Описание

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

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

    Например, задача работает с полем errorFlag1 и другая задача на поле errorFlag2 в переменной этого типа:

    struct errorFlags {
       unsigned int errorFlag1 : 1;
       unsigned int errorFlag2 : 1;
       ...
    }
    Предположим, что операции не являются атомарными друг относительно друга. Другими словами, вы не реализовали механизмы защиты, чтобы гарантировать, что одна операция завершается, прежде чем другая операция начинается.

  2. По крайней мере одна из незащищенных операций является операцией записи.

Чтобы найти этот дефект, перед анализом, необходимо задать многозадачные опции. Чтобы задать эти опции, на панели Configuration, выбирают Multitasking. Для получения дополнительной информации смотрите Анализ Многозадачности Polyspace Конфигурирования Вручную.

Риск

Смежные битовые поля, которые являются частью той же структуры, могут храниться в одном байте в той же ячейке памяти. Операции чтения или операции записи на всех переменных включая битовые поля происходят один байт или слово за один раз. Чтобы изменить только определенные биты в байте, шаги, похожие на эти шаги, происходят в последовательности:

  1. Байт загружается в RAM.

  2. Маска создается так, чтобы только определенные биты были изменены к намеченному значению, и остающиеся биты остаются неизменными.

  3. Операция битового "ИЛИ" выполняется между копией байта в RAM и маской.

  4. Байт с определенными измененными битами копируется назад с RAM.

Когда вы получаете доступ к двум различным битовым полям, эти четыре шага должны быть выполнены для каждого битового поля. Если доступы не защищены, все четыре шага для одного битового поля не могут быть завершены, прежде чем четыре шага для другого битового поля начинаются. В результате модификация одного битового поля может отменить модификацию смежного битового поля. Например, в предыдущем примере, модификации errorFlag1 и errorFlag2 может произойти в следующей последовательности. Шаги 1,2 и 5 относятся к модификации errorFlag1 и в то время как шаги 3,4 и 6 относятся к шагам 3,4 и 6 errorFlag2.

  1. Байт с обоими errorFlag1 и errorFlag2 немодифицированный копируется в RAM, в целях изменить errorFlag1.

  2. Маска, которая изменяет только errorFlag1 редактор битового "ИЛИ" с этой копией.

  3. Байт, содержащий оба errorFlag1 и errorFlag2 немодифицированный копируется в RAM во второй раз, в целях изменить errorFlag2.

  4. Маска, которая изменяет только errorFlag2 редактор битового "ИЛИ" с этой второй копией.

  5. Версия с errorFlag1 измененный копируется назад. Эта версия имеет errorFlag2 немодифицированный.

  6. Версия с errorFlag2 измененный копируется назад. Эта версия имеет errorFlag1 немодифицированный и перезаписи предыдущая модификация.

Фиксация

Чтобы зафиксировать этот дефект, защитите операции на битовых полях, которые являются частью той же структуры при помощи критических разделов, временного исключения, или другой означает. Смотрите Меры защиты для Совместно используемых переменных в Многозадачном Коде.

Чтобы идентифицировать существующие меры защиты, которые можно снова использовать, см. таблицу и графики, сопоставленные результатом. Таблица показывает каждую пару конфликтных вызовов. Столбец Access Protections показывает существующие меры защиты на вызовах. Чтобы видеть, что последовательность вызова функции ведет к конфликтам, кликните по значку.

Примеры

развернуть все

typedef struct
{
   unsigned int IOFlag :1;
   unsigned int InterruptFlag :1;
   unsigned int Register1Flag :1;
   unsigned int SignFlag :1;
   unsigned int SetupFlag :1;
   unsigned int Register2Flag :1;
   unsigned int ProcessorFlag :1;
   unsigned int GeneralFlag :1;
} InterruptConfigbits_t;

InterruptConfigbits_t InterruptConfigbitsProc12;

void task1 (void) {
    InterruptConfigbitsProc12.IOFlag = 0;
}

void task2 (void) {
    InterruptConfigbitsProc12.SetupFlag = 0;
}

В этом примере, task1 и task2 доступ различные битовые поля IOFlag и SetupFlag, которые принадлежат той же структурированной переменной InterruptConfigbitsProc12.

Чтобы эмулировать многозадачное поведение, задайте опции, перечисленные в этой таблице.

ОпцияСпецификация
Configure multitasking manually
Tasks

task1

task2

В командной строке используйте:

 polyspace-bug-finder 
   -entry-points task1,task2

Коррекция – использует критические разделы

Одна возможная коррекция должна перенести доступ к битовому полю в критический раздел. Критический раздел находится между вызовом функции блокировки и разблокировать функцией. В этой коррекции критический раздел находится между вызовами функций begin_critical_section и end_critical_section.

typedef struct
{
   unsigned int IOFlag :1;
   unsigned int InterruptFlag :1;
   unsigned int Register1Flag :1;
   unsigned int SignFlag :1;
   unsigned int SetupFlag :1;
   unsigned int Register2Flag :1;
   unsigned int ProcessorFlag :1;
   unsigned int GeneralFlag :1;
} InterruptConfigbits_t;

InterruptConfigbits_t InterruptConfigbitsProc12;

void begin_critical_section(void);
void end_critical_section(void);

void task1 (void) {
    begin_critical_section();
    InterruptConfigbitsProc12.IOFlag = 0;
    end_critical_section();
}

void task2 (void) {
    begin_critical_section();
    InterruptConfigbitsProc12.SetupFlag = 0;
    end_critical_section();
}

В этом примере, чтобы эмулировать многозадачное поведение, задают опции, перечисленные в этой таблице.

ОпцияСпецификация
Configure multitasking manually
Tasks

task1

task2

Critical section detailsStarting routineEnding routine
begin_critical_sectionend_critical_section

В командной строке используйте:

 polyspace-bug-finder 
   -entry-points task1,task2
   -critical-section-begin begin_critical_section:cs1
   -critical-section-end end_critical_section:cs1

Коррекция – избегает битовых полей

Если у вас нет ограничений памяти, используйте char тип данных вместо битовых полей. char переменные в структуре занимают по крайней мере один байт и не имеют проблем потокобезопасности, которые прибывают из побитовых обработок в операции размера байта. Гонки данных не следуют из незащищенных операций на различном char переменные, которые являются частью той же структуры.

typedef struct
{
   unsigned char IOFlag;
   unsigned char InterruptFlag;
   unsigned char Register1Flag;
   unsigned char SignFlag;
   unsigned char SetupFlag;
   unsigned char Register2Flag;
   unsigned char ProcessorFlag;
   unsigned char GeneralFlag;
} InterruptConfigbits_t;

InterruptConfigbits_t InterruptConfigbitsProc12;

void task1 (void) {
    InterruptConfigbitsProc12.IOFlag = 0;
}

void task2 (void) {
    InterruptConfigbitsProc12.SetupFlag = 0;
}

Хотя средство проверки не отмечает эту коррекцию, не используйте эту коррекцию для C99 или ранее. Только от C11 и позже делает Стандартный мандат C что отличный char к переменным нельзя получить доступ с помощью того же слова.

Коррекция – вставляет битовое поле размера 0

Можно ввести член небитового поля или член битового поля без имени размера 0 между двумя смежными битовыми полями, к которым можно получить доступ одновременно. Член небитового поля или размер, 0 членов битового поля гарантируют, что последующее битовое поле начинает с новой ячейки памяти. В этом откорректированном примере размер 0 членов битового поля гарантируют тот IOFlag и SetupFlag хранятся в отличных ячейках памяти.

typedef struct
{
   unsigned int IOFlag :1;
   unsigned int InterruptFlag :1;
   unsigned int Register1Flag :1;
   unsigned int SignFlag :1;
   unsigned int : 0;
   unsigned int SetupFlag :1;
   unsigned int Register2Flag :1;
   unsigned int ProcessorFlag :1;
   unsigned int GeneralFlag :1;
} InterruptConfigbits_t;

InterruptConfigbits_t InterruptConfigbitsProc12;

void task1 (void) {
    InterruptConfigbitsProc12.IOFlag = 0;
}

void task2 (void) {
    InterruptConfigbitsProc12.SetupFlag = 0;
}

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

Группа: параллелизм
Язык: C | C++
Значение по умолчанию: On
Синтаксис командной строки: DATA_RACE_BIT_FIELDS
Удар: высоко
Введенный в R2020b