CERT C: Rule CON32-C

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

Описание

Управляйте определением

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

Реализация Polyspace

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

Примеры

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

Проблема

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

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

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

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

[1]  Это программное обеспечение было создано MathWorks, включающим фрагменты: “Веб-сайт SEI CERT-C”, © 2017 Carnegie Mellon University, веб-сайт SEI CERT-C © 2017 Carnegie Mellon University”, CERT SEI C Кодирование Стандарта – Правил для Разработки безопасных, Надежных и Защищенных систем – 2 016 Выпусков”, © 2016 Carnegie Mellon University, and “CERT SEI Стандарт Кодирования C++ – Правил для Разработки безопасных, Надежных и Защищенных систем на C++ – 2 016 Выпусков” © 2016 Carnegie Mellon University, со специальным разрешением от его Института программной инженерии.

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

Это программное обеспечение и сопоставленная документация не были рассмотрены, ни являются подтвержденным Университетом Карнеги-Меллон или его Институтом программной инженерии.