Main Content

CERT C: Rule POS49-C

When data must be accessed by multiple threads, provide a mutex and guarantee no adjacent data is also accessed

Description

This checker is deactivated in a default Polyspace® as You Code analysis. See Checkers Deactivated in Polyspace as You Code Analysis (Polyspace Access).

Rule Definition

When data must be accessed by multiple threads, provide a mutex and guarantee no adjacent data is also accessed.1

Polyspace Implementation

The rule checker checks for Data race on adjacent bit fields.

Examples

expand all

Issue

Data race on adjacent bit fields occurs when:

  • Multiple tasks perform unprotected operations on bit fields that are part of the same structure.

    For instance, a task operates on field errorFlag1 and another task on field errorFlag2 in a variable of this type:

    struct errorFlags {
       unsigned int errorFlag1 : 1;
       unsigned int errorFlag2 : 1;
       ...
    }
    Suppose that the operations are not atomic with respect to each other. In other words, you have not implemented protection mechanisms to ensure that one operation completes before another begins.

  • At least one of the unprotected operations is a write operation.

Risk

Adjacent bit fields that are part of the same structure might be stored in one byte in the same memory location. Read or write operations on all variables including bit fields happen one byte or word at a time. To modify only specific bits in a byte, steps similar to this happen in sequence:

  1. The byte is loaded into RAM.

  2. A mask is created so that only specific bits would be modified to the intended value and the remaining bits remain unchanged.

  3. A bitwise OR operation is performed between the copy of the byte in RAM and the mask.

  4. The byte with specific bits modified is copied back from RAM.

If two different bit fields are accessed, these four steps have to be performed for each bit field. If the accesses are not protected, all four steps for one bit field might not complete before the four steps for the other begin. As a result, the modification of one bit field might undo the modification of an adjacent bit field. For instance, the modification of errorFlag1 and errorFlag2 can happen in the following sequence. Steps marked 1 relate to modification of errorFlag1 and steps marked 2 relate to that of errorFlag2.

1a. The byte with both errorFlag1 and errorFlag2 unmodified is copied into RAM, for purposes of modifying errorFlag1.

1b. A mask that modifies only errorFlag1 is bitwise OR-ed with this copy.

2a. The byte containing both errorFlag1 and errorFlag2 unmodified is copied into RAM a second time, for purposes of modifying errorFlag2.

2b. A mask that modifies only errorFlag2 is bitwise OR-ed with this second copy.

1c. The version with errorFlag1 modified is copied back. This version has errorFlag2 unmodified.

2c The version with errorFlag2 modified is copied back. This version has errorFlag1 unmodified and overwrites the previous modification.

Fix

To fix this defect, protect the operations on bit fields that are part of the same structure using critical sections, temporal exclusion or another means. See Protections for Shared Variables in Multitasking Code.

To identify existing protections that you can reuse, see the table and graphs associated with the result. The table shows each pair of conflicting calls. The Access Protections column shows existing protections on the calls. To see the function call sequence leading to the conflicts, click the icon. For an example, see below.

Example - Unprotected Operation on Global Variable from Multiple POSIX Threads
#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; //Noncompliant

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

In this example, the threads with id thread1 and thread2 access different bit fields IOFlag and SetupFlag, which belong to the same structured variable InterruptConfigbitsProc12.

Correction - Use Critical Sections

One possible correction is to wrap the bit field accesses in a critical section. A critical section lies between a call to a lock function and an unlock function. In this correction, the critical section lies between the calls to functions pthread_mutex_lock and 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
    }
}
Correction – Insert Bit Field of Size 0

You can enter a non bit field member or an unnamed bit field member of size 0 in between two adjacent bit fields that might be accessed concurrently. A non bit field member or size 0 bit field member ensures that the subsequent bit field starts from a new memory location. In this corrected example, the size 0 bit field member ensures that IOFlag and SetupFlag are stored in distinct memory locations.

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

Check Information

Group: Rule 50. POSIX (POS)

Version History

Introduced in R2019a


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.