CERT C: Rule POS49-C

Когда данные должны быть доступны несколькими потоками, предоставьте мьютекс и гарантируйте, что смежные данные также не доступны

Описание

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

Когда данные должны быть доступны несколькими потоками, предоставьте мьютекс и гарантируйте, что смежные данные также не доступны.[1]

Реализация Polyspace

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

Примеры

расширить все

Проблема

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

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

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

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

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

Риск

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Пример - незащищенная операция с глобальной переменной из нескольких потоков POSIX
#include <stdlib.h>
#include <pthread.h>
#define thread_success 0

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* arg) {
    InterruptConfigbitsProc12.IOFlag = 0;
    //Additional code
}

void* task2 (void* arg) {
    InterruptConfigbitsProc12.SetupFlag = 0;
    //Additional code
}

void main() {
    pthread_t thread1, thread2;
    if(thread_success != pthread_create(&thread1, NULL, task1, NULL)){
        //Handle error
    }
    if(thread_success != pthread_create(&thread2, NULL, task2, NULL)){
        //Handle error
    }
}

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

Коррекция - использование критических сечений

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

#include <stdlib.h>
#include <pthread.h>
#define thread_success 0
#define lock_success 0

pthread_mutex_t lock;

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* arg) {
    if( lock_success != pthread_mutex_lock(&lock)) {
        //Handle error
    }
    InterruptConfigbitsProc12.IOFlag = 0;
    if( lock_success != pthread_mutex_unlock(&lock)) {
        //Handle error
    }
    //Additional code
}

void* task2 (void* arg) {
    if( lock_success != pthread_mutex_lock(&lock)) {
        //Handle error
    }
    InterruptConfigbitsProc12.SetupFlag = 0;
    if( lock_success != pthread_mutex_unlock(&lock)) {
        //Handle error
    }
    //Additional code
}

void main() {
    pthread_t thread1, thread2;
    if(thread_success != pthread_create(&thread1, NULL, task1, NULL)){
        //Handle error
    }
    if(thread_success != pthread_create(&thread2, NULL, task2, NULL)){
        //Handle error
    }
}
Коррекция - Вставка битового поля размера 0

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

#include <stdlib.h>
#include <pthread.h>
#define thread_success 0

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* arg) {
    InterruptConfigbitsProc12.IOFlag = 0;
    //Additional code
}

void* task2 (void* arg) {
    InterruptConfigbitsProc12.SetupFlag = 0;
    //Additional code
}

void main() {
    pthread_t thread1, thread2;
    if(thread_success != pthread_create(&thread1, NULL, task1, NULL)){
        //Handle error
    }
    if(thread_success != pthread_create(&thread2, NULL, task2, NULL)){
        //Handle error
    }
}

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

Группа: Правило 50. POSIX (POS)
Введенный в R2019a

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

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

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