The volatile
keyword is a hint to the compiler telling it not to optimize operations on that memory location, but instead perform memory accesses for reads and writes. This property of the volatile
keyword is sometimes confused as providing atomicity of a variable that is shared between threads in a multithreaded program. A variable declared as volatile
is not cached in a register leading to this confusion that it can be used safely as a synchronization primitive. When declared as volatile
the compiler does not re-order the sequence of reads and writes to that memory location. However, the compiler might re-order these reads and writes with those to other memory locations. This might result in non atomic operations on the synchronization variable resulting in errors.
Noncompliant Code Example
The following code uses flag
as a synchronization primitive.
bool flag = false; void test(){ while (!flag){ sleep(1000); } } void wakeup(){ flag = true; } void debit(int amount){ test(); account_balance -= amount; }
In the above piece of code, the value of flag
is used to determine if the critical section can be executed or not. flag
is not declared as being of type volatile
and so may be cached in registers. Before the value in the register is written to memory, another thread might be scheduled to run and so may end up reading stale data.
Noncompliant Code Example
The following code uses flag
as a synchronization primitive but this time declared as type volatile
volatile bool flag = false; void test(){ while (!flag){ sleep(1000); } } void wakeup(){ flag = true; } void debit(int amount){ test(); account_balance -= amount; }
Declaring flag
as volatile solves the problem of reading stale data, but still does not provide atomicity guarantees needed for synchronization primitives to work correctly. The volatile
keyword does not promise to provide the guarantees needed for synchronization primitives.
Compliant Solution
The compliant solution would involve using a mutex to protect critical sections.
#include <pthread.h> int account_balance; pthread_mutex_t flag = PTHREAD_MUTEX_INITIALIZER; void debit(int amount) { pthread_mutex_lock(&flag); account_balance -= amount;//Inside critical section pthread_mutex_unlock(&flag); }
Risk Assessment
Recommendation |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
POS03-C |
Medium |
Probable |
Medium |
P12 |
L1 |
Other Languages
This rule can be found in the C++ Secure Coding Practices as MEM11-CPP. Do not use volatile as a synchronization primitive