sqc 0.4.13

Software Code Quality - CERT C compliance checker
[metadata]
id = "CON41-C"
type = "rule"
category = "CON"
number = 41
title = "Wrap functions that can fail spuriously in a loop"
description = """
Functions that can fail spuriously should be wrapped in a loop. Theatomic_compar
e_exchange_weak()andatomic_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 functionsatomic_compare_exchange
_strong()andatomic_compare_exchange_strong_explicit(), these functions are
permitted tofail spuriously. This makes these functions faster on some
platforms—for example, on architectures that implement compare-and-exchange
using load-linked/store-conditional instructions, such as Alpha, ARM, MIPS, and
PowerPC. The C Standard, 7.17.7.4, paragraph 5 [ISO/IEC 9899:2024], describes
this behavior:A weak compare-and-exchange operation may fail spuriously. That
is, even when the contents of memory referred to byexpectedandobjectare equal,
it may return zero and store back toexpectedthe same memory contents that were
originally there.Noncompliant Code ExampleIn this noncompliant code
example,reorganize_data_structure()is to be used as an argument tothrd_create().
After reorganizing, the function attempts to replace the head pointer so that it
points to the new version. If no other thread has changed the head pointer since
it was originally loaded,reorganize_data_structure()is intended to exit the
thread with a result oftrue, indicating success. Otherwise, the new
reorganization attempt is discarded and the thread is exited with a result
offalse. However,atomic_compare_exchange_weak()may fail even when the head
pointer has not changed. Therefore,reorganize_data_structure()may perform the
work and then discard it unnecessarily.#include <stdatomic.h> #include
<stdbool.h> struct data { struct data *next; /* ... */ }; extern void
cleanup_data_structure(struct data *head); int reorganize_data_structure(void
*thread_arg) { struct data *_Atomic *ptr_to_head = thread_arg; struct data
*old_head = atomic_load(ptr_to_head); struct data *new_head; bool success; /*
... Reorganize the data structure ... */ success =
atomic_compare_exchange_weak(ptr_to_head, &old_head, new_head); if (!success) {
cleanup_data_structure(new_head); } return success; /* Exit the thread */
}Compliant Solution (atomic_compare_exchange_weak())To recover from spurious
failures, a loop must be used. However,atomic_compare_exchange_weak()might fail
because the head pointer changed, or the failure may be spurious. In either
case, the thread must perform the work repeatedly until the compare-and-exchange
succeeds, as shown in this compliant solution:#include <stdatomic.h> #include
<stdbool.h> #include <stddef.h> struct data { struct data *next; /* ... */ };
extern void cleanup_data_structure(struct data *head); int
reorganize_data_structure(void *thread_arg) { struct data *_Atomic *ptr_to_head
= thread_arg; struct data *old_head = atomic_load(ptr_to_head); struct data
*new_head = NULL; struct data *saved_old_head; bool success; do { if (new_head
!= NULL) { cleanup_data_structure(new_head); } saved_old_head = old_head; /* ...
Reorganize the data structure ... */ } while (!(success =
atomic_compare_exchange_weak( ptr_to_head, &old_head, new_head )) && old_head ==
saved_old_head); return success; /* Exit the thread */ }This loop could also be
part of a larger control flow; for example, the thread from the noncompliant
code example could be retried if it returnsfalse.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, as in this
compliant solution:#include <stdatomic.h> #include <stdbool.h> struct data {
struct data *next; /* ... */ }; extern void cleanup_data_structure(struct data
*head); int reorganize_data_structure(void *thread_arg) { struct data *_Atomic
*ptr_to_head = thread_arg; struct data *old_head = atomic_load(ptr_to_head);
struct data *new_head; bool success; /* ... Reorganize the data structure ... */
success = atomic_compare_exchange_strong( ptr_to_head, &old_head, new_head ); if
(!success) { cleanup_data_structure(new_head); } return success; /* Exit the
thread */ }Risk AssessmentFailing to wrap theatomic_compare_exchange_weak()andat
omic_compare_exchange_weak_explicit()functions in a loop can result in incorrect
values and control flow.RuleSeverityLikelihoodDetectableRepairablePriorityLevelC
ON41-CLowUnlikelyYesNoP2L3Automated DetectionToolVersionCheckerDescriptionCodeSo
nar9.1p0LANG.STRUCT.ICOLInappropriate Call Outside
LoopCoverity2017.07BAD_CHECK_OF_WAIT_CONDImplementedCppcheck
Premium24.11.0premium-cert-con41-cHelix
QAC2025.2C2026C++5023Klocwork2025.2CERT.CONC.ATOMIC_COMP_FAIL_IN_LOOPParasoft
C/C++test2024.2CERT_C-CON41-aWrap functions that can fail spuriously in a
loopPolyspace Bug FinderR2025bCERT C: Rule CON41-CChecks for situations where
functions that can spuriously fail are not wrapped in loop (rule fully
covered)Related VulnerabilitiesSearch forvulnerabilitiesresulting from the
violation of this rule on theCERT website.Related GuidelinesKey here(explains
table format and definitions)TaxonomyTaxonomy itemRelationshipCERT Oracle Secure
Coding Standard for JavaTHI03-J. Always invoke wait() and await() methods inside
a loopPrior to 2018-01-12: CERT: Unspecified RelationshipBibliography[ISO/IEC
9899:2024]7.17.7.4, "Theatomic_compare_exchangeGeneric Functions"[Lea
2000]1.3.2, "Liveness"3.2.2, "Monitor Mechanics"
"""
severity = "Low"
likelihood = "Unlikely"
priority = "P2"
level = "L3"
cert_version = "2016 Edition (Wiki)"
last_modified = "Unknown"

[rules.cert_c.CON41-C]
enabled = true

[references]
wiki = "https://wiki.sei.cmu.edu/confluence/display/c/CON41-C.+Wrap+functions+that+can+fail+spuriously+in+a+loop"