Declaring shared variables as volatile ensures visibility and limits reordering of accesses. Volatile accesses do not guarantee the atomicity of composite operations such as incrementing a variable (CON01-J. Design APIs that ensure atomicity of composite operations and visibility of results).
A write to a volatile
field happens-before every subsequent read of that field. Statements that occur before the write to the volatile
field also happen-before the read of the volatile
field.
Declaring variables as volatile
establishes a happens-before relationship such that a write to the volatile
variable is always seen by a subsequent read. Consequently, these operations appear to be sequentially consistent with respect to each other. Statements that occur before the write to the volatile
field also happen-before the read of the volatile
field.
Consider two threads that are executing some statements:
...
Volatile read and write operations cannot be reordered with respect to each other and also with respect to nonvolatile variables accesses. When Thread 2 reads the volatile
variable it will see sees the results of all the writes occurring before the write to the volatile
variable in Thread 1.
...
This noncompliant code example declares a non-volatile int
variable that nonvolatile variable of type int
which is initialized in the constructor depending on a security check. In a multi-threading scenario, it is possible that the statements will be reordered so that the boolean
flag initialized
is set to true
before the initialization has concluded. If it is possible to obtain a partially initialized instance of the class in a subclass using a finalizer attack (OBJ04-J. Do not allow partially initialized objects to be accessed), a race condition can be exploited by invoking the getBalance()
method to obtain the balance even though initialization is still underway.
Code Block | ||
---|---|---|
| ||
class BankOperation { private int balance = 0; private boolean initialized = false; public BankOperation() { if (!performAccountVerification()) { throw new SecurityException("Invalid Account"); } balance = 1000; initialized = true; } private int getBalance() { if (initialized == true) { return balance; } else { return -1; } } } |
In a multi-threading scenario, it is possible that the statements will be reordered so that the boolean
flag initialized
is set to true
before the initialization has concluded. If it is possible to obtain a partially initialized instance of the class in a subclass using a finalizer attack (OBJ04-J. Do not allow partially initialized objects to be accessed), a race condition can be exploited by invoking the getBalance()
method to obtain the balance even though initialization is still underway.
Compliant Solution (volatile
guard)
...
This noncompliant code example consists of two classes, an immutable ImmutablePoint
class and a mutable Holder
class. Holder
is mutable because a new ImmutablePoint
instance can be assigned to it using the setPoint()
method. If one thread updates the value of the ipoint
field, another thread may still see the reference of the old value.
Code Block | ||
---|---|---|
| ||
class Holder { ImmutablePoint ipoint; Holder(ImmutablePoint ip) { ipoint = ip; } ImmutablePoint getPoint() { return ipoint; } void setPoint(ImmutablePoint ip) { this.ipoint = ip; } } public class ImmutablePoint { final int x; final int y; public ImmutablePoint(int x, int y) { this.x = x; this.y = y; } } |
Holder
is mutable because a new ImmutablePoint
instance can be assigned to it using the setPoint()
method. If one thread updates the value of the ipoint
field, another thread may still see the reference of the old value. This is a violation of CON28-J. Synchronize access to shared fields that refer to immutable data.
Compliant Solution (visibility)
...
Code Block | ||
---|---|---|
| ||
public class Container<K,V> { volatile Map<K,V> map; // ... } |
Alternative solutions to using volatile
for safe publication are discussed in CON26-J. Do not publish partially-constructed objects.
Risk Assessment
Failing to use volatile to guarantee visibility of shared values across multiple thread and prevent reordering of statements accesses can result in unpredictable control flow.
...