CERT C: Rule POS49-C

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

Описание

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

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

Реализация Polyspace

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

Примеры

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

Проблема

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

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

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

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

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

Риск

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

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

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

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

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

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

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

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

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

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

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

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

Фиксация

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

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

В этом примере, потоках с ID 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 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". УНИВЕРСИТЕТ КАРНЕГИ-МЕЛЛОН НЕ ДАЕТ ГАРАНТИЙ НИКАКОГО ВИДА, ИЛИ ОПИСАЛ ИЛИ ПОДРАЗУМЕВАЛ, ОТНОСИТЕЛЬНО ЛЮБОГО ВОПРОСА ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИЛ, ГАРАНТИЯ ПРИГОДНОСТИ ДЛЯ ЦЕЛИ ИЛИ ВЫСОКОГО СПРОСА, ИСКЛЮЧИТЕЛЬНОСТИ, ИЛИ ЗАКАНЧИВАЕТСЯ ПОЛУЧЕННЫЙ ИЗ ИСПОЛЬЗОВАНИЯ МАТЕРИАЛА. УНИВЕРСИТЕТ КАРНЕГИ-МЕЛЛОН НЕ ДАЕТ ГАРАНТИИ НИКАКОГО ВИДА ОТНОСИТЕЛЬНО СВОБОДЫ ОТ ПАТЕНТА, ТОВАРНОГО ЗНАКА ИЛИ НАРУШЕНИЯ АВТОРСКОГО ПРАВА.

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