exponenta event banner

CERT C: CON32-C правил

Предотвращение скачков данных при доступе к битовым полям из нескольких потоков

Описание

Определение правила

Запретить гонки данных при доступе к битовым полям из нескольких потоков [1 ].

Внедрение Polyspace

Эта проверка проверяет наличие гонки данных в соседних битовых полях.

Примеры

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

Проблема

Гонка данных в соседних битовых полях происходит, когда:

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

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

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

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

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

Риск

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

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

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

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

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

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

Шаги 1,2 и 5 относятся к модификации errorFlag1 и в то время как шаги 3,4 и 6 относятся к шагам errorFlag2.

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

  2. Маска, изменяющая только errorFlag1 побитовый ИЛИ с этой копией.

  3. Байт, содержащий оба errorFlag1 и errorFlag2 немодифицированная копируется в ОЗУ второй раз, для целей модификации 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.

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

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

 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();
}

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

ВыборСпецификация
Настройка многозадачности вручную
Задачи

task1

task2

Детали критического разделаЗапуск подпрограммыЗавершение процедуры
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 года и позднее мандат Стандарта С отличается 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;
}

Проверить информацию

Группа: Правило 14. Параллелизм (CON)
Представлен в R2019a

[1] Данное программное обеспечение было создано компанией MathWorks и включает в себя следующие компоненты: «Веб-сайт SEI CERT-C», © 2017 Университет Карнеги-Меллон, веб-сайт SEI CERT-C + + © 2017 Университет Карнеги-Меллон, "Стандарт кодирования SEI CERT C - Правила разработки безопасных, Надежные и безопасные системы - 2016 Edition ", © 2016 Университет Карнеги-Меллон, и "Стандарт кодирования SEI CERT C++ - Правила разработки безопасных, Надежные и безопасные системы в C++ - 2016 Edition "© 2016 Университет Карнеги-Меллон, со специальным разрешением от его Института программного обеспечения.

ЛЮБОЙ МАТЕРИАЛ УНИВЕРСИТЕТА КАРНЕГИ МЕЛЛОНА И/ИЛИ ЕГО ПРОГРАММНОГО ИНЖЕНЕРНОГО ИНСТИТУТА, СОДЕРЖАЩИЙСЯ В НАСТОЯЩЕМ ДОКУМЕНТЕ, ПОСТАВЛЯЕТСЯ КАК ЕСТЬ. УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ, ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, В ОТНОШЕНИИ ЛЮБЫХ ВОПРОСОВ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИИ ПРИГОДНОСТИ ДЛЯ ЦЕЛЕЙ ИЛИ ТОВАРНОЙ ПРИГОДНОСТИ, ИСКЛЮЧИТЕЛЬНОСТИ ИЛИ РЕЗУЛЬТАТОВ, ПОЛУЧЕННЫХ ОТ ИСПОЛЬЗОВАНИЯ УНИВЕРСИТЕТ КАРНЕГИ МЕЛЛОН НЕ ДАЕТ НИКАКИХ ГАРАНТИЙ В ОТНОШЕНИИ СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКИХ ПРАВ.

Данное программное обеспечение и связанная с ним документация не были рассмотрены и не одобрены Университетом Карнеги-Меллона или его Институтом разработки программного обеспечения.