IssueThis defect 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
is completed before another operation begins.At least one of the unprotected operations is a
write operation.
To find this defect, before analysis, you must specify the
multitasking options. To specify these options, on the
Configuration pane, select
Multitasking. For more information, see
Configuring Polyspace Multitasking Analysis Manually.
RiskAdjacent 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 occur one byte or
word at a time. To modify only specific bits in a byte, steps similar
to these steps occur in sequence:
The byte is loaded into RAM.
A mask is created so that only specific bits are
modified to the intended value and the remaining
bits remain unchanged.
A bitwise OR operation is performed between the copy
of the byte in RAM and the mask.
The byte with specific bits modified is copied back
from RAM.
When you access two different bit fields, 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 be completed before the four
steps for the other bit field begin. As a result, the modification of
one bit field might undo the modification of an adjacent bit field.
For instance, in the preceding example, the modification of
errorFlag1
and errorFlag2
can occur in the following sequence. Steps 1,2 and 5 relate to
modification of errorFlag1
and while steps 3,4 and
6 relate to that of errorFlag2
.
The byte with both errorFlag1
and
errorFlag2
unmodified is copied
into RAM, for purposes of modifying
errorFlag1
.
A mask that modifies only errorFlag1
is
bitwise OR-ed with this copy.
The byte containing both errorFlag1
and
errorFlag2
unmodified is copied
into RAM a second time, for purposes of modifying
errorFlag2
.
A mask that modifies only errorFlag2
is
bitwise OR-ed with this second copy.
The version with errorFlag1
modified is
copied back. This version has
errorFlag2
unmodified.
The version with errorFlag2
modified is
copied back. This version has
errorFlag1
unmodified and
overwrites the previous modification.
FixTo fix this defect, protect the operations on bit fields that are part
of the same structure by 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.
Example - Unprotected Operation on Global Variable from Multiple Taskstypedef 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) {
InterruptConfigbitsProc12.IOFlag = 0;
}
void task2 (void) {
InterruptConfigbitsProc12.SetupFlag = 0;
}
In this example, task1
and task2
access different bit fields IOFlag
and
SetupFlag
, which belong to the same
structured variable
InterruptConfigbitsProc12
.
To emulate multitasking behavior, specify the options listed in this
table.
At the command-line,
use:
polyspace-bug-finder
-entry-points task1,task2
Correction – Use Critical SectionsOne possible correction is to wrap the bit field access 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
begin_critical_section
and
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();
}
In this example, to emulate multitasking behavior, specify options
listed in this table.
At the command-line,
use:
polyspace-bug-finder
-entry-points task1,task2
-critical-section-begin begin_critical_section:cs1
-critical-section-end end_critical_section:cs1
Correction – Avoid Bit FieldsIf you do not have memory constraints, use the char
data type instead of bit fields. The char
variables
in a structure occupy at least one byte and do not have the
thread-safety issues that come from bit manipulations in a byte-sized
operation. Data races do not result from unprotected operations on
different char
variables that are part of the same
structure.
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;
}
Though the checker does not flag this correction, do not use this
correction for C99 or earlier. Only from C11 and later does the C
Standard mandate that distinct char
variables
cannot be accessed using the same word.
Correction – Insert Bit Field of Size 0You can enter a non-bit field member or an unnamed bit field member of
size 0 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.
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;
}