Data race on adjacent bit fields

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

Описание

Этот дефект возникает, когда:

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

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

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

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

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

Риск

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

  1. Байт загружается в ОЗУ.

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

  3. Побитовая операция OR выполняется между копией байта в ОЗУ и маской.

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

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

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

  2. Маска, которая изменяет только errorFlag1 bitwise OR-ed с этой копией.

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

  4. Маска, которая изменяет только errorFlag2 bitwise OR-ed с этой второй копией.

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

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

Зафиксировать

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

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