An object that has volatile-qualified type may be modified in ways unknown to the implementation or have other unknown side effects. Asynchronous signal handling, for example, may cause objects to be modified in a manner unknown to the compiler. Without this type qualifier, unintended optimizations may occur. These optimizations may cause race conditions because a programmer may write code that prevents a race condition, yet the compiler is not aware of the programmer's data model and may modify the code during compilation to permit race conditions.

The volatile keyword eliminates this confusion by imposing restrictions on access and caching. According to the C99 Rationale [C99 Rationale 2003],

No caching through this lvalue: each operation in the abstract semantics must be performed (that is, no caching assumptions may be made, because the location is not guaranteed to contain any previous value). In the absence of this qualifier, the contents of the designated location may be assumed to be unchanged except for possible aliasing.

Type qualifying objects as volatile does not guarantee synchronization between multiple threads, protect against simultaneous memory accesses, or, unless used to declare objects of type sig_atomic_t, guarantee atomicity of accesses to the object. For restrictions specific to signal handlers, see SIG31-C. Do not access shared objects in signal handlers. However, type qualifying objects as volatile does ensure that a conforming compiler will not elide or reorder access to the object.

Noncompliant Code Example

In this noncompliant code example, the programmer is targeting a custom piece of hardware that controls an LED by writing values into a register bank. The register bank is memory mapped into the process such that writing to a specific memory location will actually place a value into a hardware register to be read by the LED controller. The programmer intends to turn the LED on by placing value 1 into the first register, and then turn the LED off later by placing the value 0 into the first register.

#include <stddef.h>
#include <stdint.h>

extern void get_register_bank(int32_t **bank,
                              size_t *num_registers);
extern void external_wait(void);

void func(void) {
  int32_t bank[3];
  size_t num_regs = 3;

  get_register_bank((int32_t **)&bank, &num_regs);
  if (num_regs < 3) {
    /* Handle error */
   }

  bank[0] = 1;
  external_wait();
  bank[0] = 0;
}

The compiler is free to optimize what it perceives as being a dead store to bank[0] by removing the first assignment to the variable. This would cause the LED to never be turned on in an optimized build. 

Compliant Solution

In this compliant solution, the register bank's memory is qualified with the volatile keyword, ensuring the compiler does not optimize access to the memory.

#include <stddef.h>
#include <stdint.h>

extern void get_register_bank(volatile int32_t **bank,
                              size_t *num_registers);
extern void external_wait(void);

void func(void) {
  volatile int32_t bank[3];
  size_t num_regs = 3;

  get_register_bank((volatile int32_t **)&bank, &num_regs);
  if (num_regs < 3) {
    /* Handle error */
   }

  bank[0] = 1;
  external_wait();
  bank[0] = 0;
}

Risk Assessment

Failure to declare variables containing data that cannot be cached as volatile can result in unexpected runtime behavior resulting from compiler optimizations.

Recommendation

Severity

Likelihood

Remediation Cost

Priority

Level

DCL22-C

Low

Probable

High

P2

L3

Automated Detection

Tool

Version

Checker

Description

LDRA tool suite
 
8 DPartially implemented
Parasoft C/C++test

CERT_C-DCL22-a

Avoid unused values
Polyspace Bug Finder

CERT C: Rec. DCL22-C


Checks for write without a further read (rule partially covered)

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

Related Guidelines

CERT C Secure Coding StandardSIG31-C. Do not access shared objects in signal handlers
SEI CERT C++ Coding StandardVOID DCL20-CPP. Use volatile for data that cannot be cached
MISRA C:2012Rule 2.2 (required)

Bibliography

[C99 Rationale 2003] Subclause 6.7.3, "Type Qualifiers"