Main Content

CERT C: Rule CON40-C

Do not refer to an atomic variable twice in an expression

Description

Rule Definition

Do not refer to an atomic variable twice in an expression.1

Polyspace Implementation

The rule checker checks for these issues:

  • Atomic variable accessed twice in an expression.

  • Atomic load and store sequence not atomic.

Examples

expand all

Issue

Atomic variable accessed twice in an expression occurs when C atomic types or C++ std::atomic class variables appear twice in an expression and there are:

  • Two atomic read operations on the variable.

  • An atomic read and a distinct atomic write operation on the variable.

The C standard defines certain operations on atomic variables that are thread safe and do not cause data race conditions. Unlike individual operations, a pair of operations on the same atomic variable in an expression is not thread safe.

Risk

A thread can modify the atomic variable between the pair of atomic operations, which can result in a data race condition.

Fix

Do not reference an atomic variable twice in the same expression.

Example - Referencing Atomic Variable Twice in an Expression

In this example, the global variable n is referenced twice in the return statement of compute_sum(). The value of n can change between the two distinct read operations. compute_sum() can return an incorrect value.

To see this violation, run Polyspace® Bug Finder™ on this code and spcify gnu4.9 as the compiler. See Compiler (-compiler).

#include <stdatomic.h>

atomic_int n = ATOMIC_VAR_INIT(0);

int compute_sum(void)
{
    return n * (n + 1) / 2; //Noncompliant
}
Correction — Pass Variable as Function Argument

One possible correction is to pass the variable as a function argument n. The variable is copied to memory and the read operations on the copy guarantee that compute_sum() returns a correct result. If you pass a variable of type int instead of type atomic_int, the correction is still valid.

#include <stdatomic.h>

int compute_sum(atomic_int n)
{
    return n * (n + 1) / 2;
}
Issue

Atomic load and store sequence not atomic occurs when you use these functions to load, and then store an atomic variable.

  • C functions:

    • atomic_load()

    • atomic_load_explicit()

    • atomic_store()

    • atomic_store_explicit()

  • C++ functions:

    • std::atomic_load()

    • std::atomic_load_explicit()

    • std::atomic_store()

    • std::atomic_store_explicit()

    • std::atomic::load()

    • std::atomic::store()

A thread cannot interrupt an atomic load or an atomic store operation on a variable, but a thread can interrupt a store, and then load sequence.

Risk

A thread can modify a variable between the load and store operations, resulting in a data race condition.

Fix

To read, modify, and store a variable atomically, use a compound assignment operator such as +=, atomic_compare_exchange() or atomic_fetch_*-family functions.

Example - Loading Then Storing an Atomic Variable

In this example, variable flag of type atomic_bool is referenced twice inside the toggle_flag() function. The function loads the variable, negates its value, then stores the new value back to the variable. If two threads call toggle_flag(), the second thread can access flag between the load and store operations of the first thread. flag can end up in an incorrect state.

To see this violation, run Polyspace Bug Finder on this code and spcify gnu4.9 as the compiler. See Compiler (-compiler).

#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); //Noncompliant
}

bool get_flag(void)
{
    return atomic_load(&flag);
}
Correction — Use Compound Assignment to Modify Variable

One possible correction is to use a compound assignment operator to toggle the value of flag. The C standard defines the operation by using ^= as atomic.

#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;
}

Check Information

Group: Rule 14. Concurrency (CON)

Version History

Introduced in R2019a


1 This software has been created by MathWorks incorporating portions of: the “SEI CERT-C Website,” © 2017 Carnegie Mellon University, the SEI CERT-C++ Web site © 2017 Carnegie Mellon University, ”SEI CERT C Coding Standard – Rules for Developing safe, Reliable and Secure systems – 2016 Edition,” © 2016 Carnegie Mellon University, and “SEI CERT C++ Coding Standard – Rules for Developing safe, Reliable and Secure systems in C++ – 2016 Edition” © 2016 Carnegie Mellon University, with special permission from its Software Engineering Institute.

ANY MATERIAL OF CARNEGIE MELLON UNIVERSITY AND/OR ITS SOFTWARE ENGINEERING INSTITUTE CONTAINED HEREIN IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT.

This software and associated documentation has not been reviewed nor is it endorsed by Carnegie Mellon University or its Software Engineering Institute.