The Java Language Specification (JLS) allows 64-bit long
and double
values to be treated as two 32-bit values. For example, a 64-bit write operation may could be performed as two separate 32-bit operations.
According to the Java Language Specification \[[ JLS 05|AA. Java References#JLS 05]\], section 17.7 , §17.7, "Non-atomic Atomic Treatment of {{ Wiki Markup double
}} and {{long
}}": [JLS 2015]:
... this behavior is implementation specific; Java virtual machines are free to perform writes to
long
anddouble
values atomically or in two parts. For the purposes of the Java programming language memory model, a single write to a non-volatilelong
ordouble
value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write....
Some implementations may find it convenient to divide a single write action on a 64-bit
long
ordouble
value into two write actions on adjacent 32-bit values. For efficiency's sake, this behavior is implementation-specific; an implementation of the Java Virtual Machine is free to perform writes tolong
anddouble
values atomically or in two parts.
This behavior can be result in reading indeterminate values being read in code that is required to be thread-safe. Consequently, multithreaded programs must ensure atomicity when reading or writing 64-bit values.
Noncompliant Code Example
The Java programming language allows threads to access shared variables. If, in In this noncompliant code example, if one thread repeatedly calls the method oneassignValue()
(but no more than Integer.MAX_VALUE
times in all), method and another thread repeatedly calls the method two()
printLong()
method, the printLong()
method could occasionally print a value of i
that is neither zero nor the value of the j
argument:
Code Block | ||
---|---|---|
| ||
class TestLongContainer { staticprivate long i = 0; static void oneassignValue(long j) { i++; i = j; } static void twoprintLong() { System.out.println("i = " + i); } } |
then method two()
could occasionally print a value for i
that is not one more than the previous value printed by two()
. A similar problem occurs if can occur when i
is declared as a double
.
Compliant Solution (Volatile)
This compliant solution declares the variables as i
volatile. Writes and reads of volatile long
and double
volatile values are always atomic.
Code Block | ||
---|---|---|
| ||
class TestLongContainer { private staticvolatile long i = 0; static void oneassignValue(long j) { i++; i = j; } static void twoprintLong() { System.out.println("i = " + i); } } |
Compliant Solution
This compliant solution synchronizes calls to methods one()
and two()}}. This guarantees these two method calls are treated as atomic operation, including reading and writing to the variable i
.
Code Block | ||
---|---|---|
| ||
class Test {
static long i = 0;
static void synchronized one() { i++; }
static synchronized void two() {
System.out.println("i =" + i);
}
}
|
It is important to ensure that the argument to the assignValue()
method is obtained from a volatile variable or obtained as the result of an atomic read. Otherwise, a read of the variable argument can itself expose a vulnerability.
The semantics of volatile
explicitly exclude any guarantee of the atomicity of compound operations that involve read-modify-write sequences such as incrementing a value (see VNA02-J. Ensure that compound operations on shared variables are atomic for more information).
Exceptions
VNA05-J-EX0: If all reads and writes of 64-bit long
and double
values occur within a synchronized region, the atomicity of the read/write is guaranteed. This requires both that the value is exposed only through synchronized methods in the class and that the value is inaccessible from other code (whether directly or indirectly). For more information, see VNA02-J. Ensure that compound operations on shared variables are atomic.
VNA05-J-EX1: This rule can be ignored for platforms that guarantee that 64-bit long
and double
values are read and written as atomic operations. Note, however, that such guarantees are not portable across different platformsConsequently, there is no need to qualify i
as a volatile type.
Risk Assessment
Failure to ensure the atomicity of operations involving 64-bit values in multithreaded applications can result in reading and writing indeterminate values. However, many Java Virtual Machines read and write 64-bit values atomically even though the specification does not require them to.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
CON39VNA05-J | low Low | probable Unlikely | medium Medium | P4 P2 | L3 |
Automated Detection
TODO
Related Vulnerabilities
Search for vulnerabilities resulting from the violation Some static analysis tools are capable of detecting violations of this rule on the CERT website.
References
Wiki Markup |
---|
\[[JLS 05|AA. Java References#JLS 05]\] 17.7 Non-atomic Treatment of double and long
\[[Goetz 06|AA. Java References#Goetz 06]\] 3.1.2. Nonatomic 64-bit Operations |
.
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
ThreadSafe |
| CCE_SL_INCONSISTENT | Implemented |
Related Guidelines
Bibliography
Section 3.1.2, "Non-atomic 64-Bit Operations" | |
| |
[JLS 2015] |
...
CON06-J. Ensure atomicity of thread-safe code 11. Concurrency (CON) CON02-J. Do not use background threads during class initialization