A consistent locking policy guarantees that multiple threads cannot simultaneously access or modify shared data. Atomic variables eliminate the need for locks by guaranteeing thread safety when certain operations are performed on them. The thread-safe operations on atomic variables are specified in the C Standard, subclauses 7.17.7 and 7.17.8 [ISO/IEC 9899:20112024]. While atomic operations can be combined, combined operations do not provide the thread safety provided by individual atomic operations.
Every time an atomic variable appears on the left -hand side of an assignment operator, including a compound assignment operator such as *=, an atomic write is performed on the variable. The use of the increment (++) or decrement (--) operators on an atomic variable constitutes an atomic read-and-write operation and is consequently thread-safe. Any reference of an atomic variable anywhere else in an expression indicates a distinct atomic read on the variable.
...
Noncompliant Code Example (atomic_bool)
This noncompliant code example declares a shared atomic_bool flag variable and provides a toggle_flag() method that negates the current value of flag:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdatomic.h>
#include <stdbool.h>
static atomic_bool flag = ATOMIC_VAR_INIT(false);
void init_flag(void) {
atomic_init(&flag, false);
}
void toggle_flag(void) {
bool temp_flag = atomic_load(&flag);
temp_flag = !temp_flag;
atomic_store(&flag, temp_flag);
}
bool get_flag(void) {
return atomic_load(&flag);
}
|
Execution of this code may result in a data race because unexpected behavior because the value of flag is read, negated, and written back. This occurs even though the read and write are both atomic.
...
toggle_flag() without Compare-and-Exchange
Time |
| Thread | Action |
|---|---|---|---|
1 |
| t1 | Reads the current value of |
which is | |||
2 |
| t2 | Reads the current value of |
which is still | |||
3 |
| t1 | Toggles the temporary variable in the cache to |
4 |
| t2 | Toggles the temporary variable in the different cache to |
5 |
| t1 | Writes the cache variable's value to |
6 |
| t2 | Writes the different cache variable's value to |
As a result, the effect of the call by t2 is not reflected in flag; the program behaves as if toggle_flag() was called only once, not twice.
Compliant Solution (atomic_compare_exchange_weak())
This compliant solution uses a compare-and-exchange to guarantee that the correct value is stored in flag. All updates are visible to other threads. The call to atomic_compare_exchange_weak() is in a loop in conformance with .
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdatomic.h>
#include <stdbool.h>
static atomic_bool flag = ATOMIC_VAR_INIT(false);
void init_flag(void) {
atomic_init(&flag, false);
}
void toggle_flag(void) {
bool old_flag = atomic_load(&flag);
bool new_flag;
do {
new_flag = !old_flag;
} while (!atomic_compare_exchange_weak(&flag, &old_flag, new_flag));
}
bool get_flag(void) {
return atomic_load(&flag);
} |
An alternative solution is to use the atomic_flag data type for managing Boolean values atomically. However, atomic_flag does not support a toggle operation.
Compliant Solution (Compound Assignment)
This compliant solution uses the ^= assignment operation to toggle flag. This operation is guaranteed to be atomic, according to the C Standard, 6.5.1617.25, paragraph 3 [ISO/IEC 9899:2024]. This operation performs a bitwise-exclusive-or between its arguments, but for Boolean arguments, this is equivalent to negation.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdatomic.h>
#include <stdbool.h>
static atomic_bool flag = ATOMIC_VAR_INIT(false);
void toggle_flag(void) {
flag ^= 1;
}
bool get_flag(void) {
return flag;
} |
Another An alternative solution is to use a mutex to protect the atomic operation, but this solution loses the performance benefits of atomic variables.
Noncompliant Code Example
This noncompliant code example takes an atomic global variable n and computes n + (n - 1) + (n - 2) + ... + 1, using the formula n * (n + 1) / 2:
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdatomic.h> atomic_int n = ATOMIC_VAR_INIT(0); voidint compute_sum(void) { return n * (n + 1) / 2; } |
The value of n may change between the two atomic reads of n in the expression, yielding an incorrect result.
Compliant Solution
This compliant solution passes the atomic variable as a function parameterargument, forcing the variable to be copied and guaranteeing a correct result:. Note that the function's formal parameter need not be atomic, and the atomic variable can still be passed as an actual argument.
| Code Block | ||||
|---|---|---|---|---|
| ||||
#include <stdatomic.h> voidint compute_sum(atomic_int n) { return n * (n + 1) / 2; } |
Risk Assessment
When operations on atomic variables are assumed to be atomic, but are not atomic, surprising data races can occur, leading to corrupted data and invalid control flow.
Rule | Severity | Likelihood | Detectable |
|---|
Repairable | Priority | Level |
|---|---|---|
CON40-C | Medium | Probable |
Yes | No | P8 | L2 |
Automated Detection
Tool | Version | Checker | Description | ||||||
|---|---|---|---|---|---|---|---|---|---|
| Astrée |
| multiple-atomic-accesses | Partially checked | ||||||
| Axivion Bauhaus Suite |
| CertC-CON40 | |||||||
| CodeSonar |
| CONCURRENCY.MAA | Multiple Accesses of Atomic | ||||||
| Coverity |
| EVALUATION_ORDER (partial) MISRA 2012 Rule 13.2 VOLATILE_ATOICITY (possible) | Implemented | ||||||
| Cppcheck Premium |
| premium-cert-con40-c | |||||||
| Helix QAC |
| C1114, C1115, C1116 C++3171, C++4150 | |||||||
| Klocwork |
| CERT.CONC.ATOMIC_TWICE_EXPR | |||||||
| Parasoft C/C++test |
| CERT_C-CON40-a | Do not refer to an atomic variable twice in an expression | ||||||
| Polyspace Bug Finder |
| CERT C: Rule CON40-C | Checks for:
Rule fully covered. | ||||||
| RuleChecker |
| multiple-atomic-accesses | Partially checked | ||||||
| Security Reviewer - Static Reviewer | 6.02 | C122 C123 | Fully Implemented |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
Key here (explains table format and definitions)
Taxonomy | Taxonomy item | Relationship |
|---|---|---|
| CWE 2.11 |
| CWE-366, Race Condition within a Thread | 2017-07-07: CERT: Rule subset of CWE |
CWE-567, Unsynchronized Access to Shared Data in a Multithreaded Context
CWE-667, Improper Locking
CERT-CWE Mapping Notes
Key here for mapping notes
CWE-366 and CON40-C
CON40-C = Subset( CON43-C) Intersection( CON32-C, CON40-C) = Ø
CWE-366 = Union( CON40-C, list) where list =
- C data races that do not involve an atomic variable used twice within an expression