When multiple threads must access or make modifications to a common variable, they may also inadvertently access other variables in adjacent memory. This is an artifact of variables being stored compactly, with 1 one byte possibly holding multiple variables, and is a common optimization on word-addressed machines. Bit-fields are especially prone to this behavior because compilers are allowed to store multiple bit-fields in 1 one addressable byte or word. Consequently, data races may exist not just on a variable accessed by multiple threads but also on other variables sharing the same byte or word address.
...
The same code is compliant when run on a C11-compliant platform. Unlike C99, C11 explicitly defines a memory location and provides the following note , in subclause 3.14.2 [ISO/IEC 9899:2011]:
NOTE 1 Two threads of execution can update and access separate memory locations without interfering with each other.
...
Code Block | ||||
---|---|---|---|---|
| ||||
struct multi_threaded_flags { unsigned int flag1 : 2; unsigned int flag2 : 2; }; struct multi_threaded_flags flags; int thread1(void *arg) { flags.flag1 = 1; return 0; } int thread2(void *arg) { flags.flag2 = 2; return 0; } |
The C Standard, Section subclause 3.14.3 [ISO/IEC 9899:2011], states:
NOTE 2 A bit-field and an adjacent non-bit-field member are in separate memory locations. The same applies to two bit-fields, if one is declared inside a nested structure declaration and the other is not, or if the two are separated by a zero-length bit-field declaration, or if they are separated by a non-bit-field member declaration. It is not safe to concurrently update two non-atomic bit-fields in the same structure if all members declared between them are also (non-zero-length) bit-fields, no matter what the sizes of those intervening bit-fields happen to be.
...
Code Block |
---|
Thread 1: register 0 = flags Thread 1: register 0 &= ~mask(flag1) Thread 2: register 0 = flags Thread 2: register 0 &= ~mask(flag2) Thread 1: register 0 |= 1 << shift(flag1) Thread 1: flags = register 0 Thread 2: register 0 |= 2 << shift(flag2) Thread 2: flags = register 0 |
Compliant Solution (Bit-field, C11,
...
Mutex)
This compliant solution protects all accesses of the flags with a mutex, thereby preventing any data races. Finally, the flags are embedded in a union alongside a long
, and a static assertion guarantees that the flags do not occupy more space than the long
. This technique prevents any data not checked by the mutex from being accessed or modified with the bit-fields on platforms that do not comply with C11.
...
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
CON32-C | mediumMedium | probableProbable | mediumMedium | P8 | L2 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Coverity | 6.5 | RACE_CONDITION | Fully implemented |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Bibliography
[ISO/IEC 9899:2011] | Subclause 3.14, "Memory Location" |
...