Functions that can fail spuriously should be wrapped in a loop. The atomic_compare_exchange_weak()
and atomic_compare_exchange_weak_explicit()
functions both attempt to set an atomic variable to a new value but only if it currently possesses a known old value. Unlike the related functions atomic_compare_exchange_strong()
and atomic_compare_exchange_strong_explicit()
, these functions are permitted to "fail spuriously," which makes them faster on some platforms. The C Standard describes this behavior in subclause 7.17.7.4, paragraph 4:
A weak compare-and-exchange operation may fail spuriously. That is, even when the contents of memory referred to by
expected
andobject
are equal, it may return zero and store back toexpected
the same memory contents that were originally there.
Noncompliant Code Example
This noncompliant code example calls the atomic_compare_exchange_weak()
outside of a loop . Consequently it is possible for func()
to complete without actually toggleing the value of cur
.
#include <stdbool.h> #include <stdatomic.h> static atomic_bool cur; void init_flag(void) { atomic_init(&cur, false); } void func(void) { init_flag(); bool exp = atomic_load(&cur); bool des = !exp; if (!atomic_compare_exchange_weak(&cur, &exp, des)) { /* Handle error */ } }
Compliant Solution (atomic_compare_exchange_weak()
)
A consequence of spurious failure is that nearly all uses of weak compare-and-exchange will be in a loop. This compliant solution calls the atomic_compare_exchange_weak()
function from within a while
loop to ensure the function does not fail spuriously. When a compare-and-exchange is in a loop, the weak version can yield better performance on some platforms.
#include <stdbool.h> #include <stdatomic.h> static atomic_bool cur; void init_flag(void) { atomic_init(&cur, false); } void func(void) { init_flag(); bool exp = atomic_load(&cur); bool des = !exp; while (!atomic_compare_exchange_weak(&cur, &exp, des)) { /* do nothing */ } }
Compliant Solution (atomic_compare_exchange_strong()
)
When a weak compare-and-exchange would require a loop and a strong one would not, the strong one is preferable.
#include <stdbool.h> #include <stdatomic.h> static atomic_bool cur; void init_flag(void) { atomic_init(&cur, false); } void func(void) { init_flag(); bool exp = atomic_load(&cur); bool des = !exp; if (!atomic_compare_exchange_strong(&cur, &exp, des) { /* Handle error */ } }
Risk Assessment
Failing to wrap the atomic_compare_exchange_weak()
and atomic_compare_exchange_weak_explicit()
functions in a loop can result in incorrect values and control flow.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
CON44-C | Low | Unlikely | Medium | P2 | L3 |
Related Guidelines
CERT Oracle Secure Coding Standard for Java | THI03-J. Always invoke wait() and await() methods inside a loop |
Bibliography
[ISO/IEC 9899:2011] | Subclause 7.17.7.4, "The atomic_compare_exchange Generic Functions" |
[Lea 2000] | 1.3.2, "Liveness" |