A common error misconception is to assume that shared references to immutable objects need not be synchronized. However, a correctly synchronized program must synchronize access to shared references.are immediately visible across multiple threads as soon as they are updated. For example, a developer can mistakenly believe that a class containing fields that refer only to immutable objects is itself immutable and consequently thread-safe.
Section The Java⢠Programming Language, Fourth Edition \[[JPL 06|AA. Java References#JPL 06]\], "Section 14.10.2. , "Final Fields and Security" states:," of Java Programming Language, Fourth Edition [JPL 2006] states: Wiki Markup
... you can use
final
fields to define immutable objects. There is a common misconception that shared access to immutable objects does not require any synchronization because the state of the object never changes. This is a misconception in general because it relies on the assumption that a thread will be guaranteed to see the initialized state of the immutable object, and that need not be the case. The problem is that, while the shared object is immutable, the reference used to access the shared object is itself shared and often mutable. Consequently, a correctly synchronized program must synchronize access to that shared reference, but often programs do not do this, because programmers do not recognize the need to do it. For example, suppose one thread creates aString
object and stores a reference to it in astatic
field. A second thread then uses that reference to access the string. There is no guarantee, based on what we've discussed so far, that the values written by the first thread when constructing the string will be seen by the second thread when it accesses the string
References to both immutable and mutable objects must be made visible to all the threads. Immutable objects can be shared safely among multiple threads. However, references to mutable objects can be made visible before the objects are fully constructed. TSM03-J. Do not publish partially initialized objects describes object construction and visibility issues specific to mutable objects.
Noncompliant Code Example
This noncompliant code example consists of the immutable Helper
class Helper
:
Code Block | ||
---|---|---|
| ||
// Immutable Helper public final class Helper { private final int n; public Helper(int n) { this.n = n; } // ... } |
and a mutable Foo
class Foo
:
Code Block | ||
---|---|---|
| ||
final class Foo { private Helper helper; public Helper getHelper() { return helper; } public void initializesetHelper(int num) { helper = new Helper(num); } } |
The Foo.getHelper()
method publishes the mutable helper
field. Because the Helper
class is immutable, it cannot be changed after it is initialized and is therefore thread-safe.
HoweverFurthermore, because the helper
field of class Foo
is not properly synchronized, it is possible that the Foo.getHelper()
method will return a reference to a partially or incorrectly initialized helper
object, if invoked from a separate thread Helper
is immutable, it is always constructed properly before its reference is made visible, in compliance with TSM03-J. Do not publish partially initialized objects. Unfortunately, a separate thread could observe a stale reference in the helper
field of the Foo
class.
Compliant Solution (
...
Synchronization)
This compliant solution synchronizes the methods of the Foo
class Foo
to ensure that no thread sees a partially initialized helper
field. stale Helper
reference:
Code Block | ||
---|---|---|
| ||
final class Foo { private Helper helper; public synchronized Helper getHelper() { return helper; } public synchronized void initializesetHelper(int num) { helper = new Helper(num); } } |
The immutable Helper
class is remains unchanged.
Compliant Solution (volatile
)
Immutable members References to immutable member objects can be safely published made visible by declaring them volatile as described in CON00-J. Know when to use volatile.:
Code Block | ||
---|---|---|
| ||
final class Foo { private volatile Helper helper; public Helper getHelper() { return helper; } public void initializesetHelper(int num) { helper = new Helper(num); } } |
The immutable Helper
class remains unchanged.
Compliant Solution (java.util.concurrent
Utilities)
This compliant solution wraps the mutable reference to the immutable Helper
object within an AtomicReference
wrapper that can be updated atomically:
Code Block | ||
---|---|---|
| ||
final class Foo {
private final AtomicReference<Helper> helperRef =
new AtomicReference<Helper>();
public Helper getHelper() {
return helperRef.get();
}
public void setHelper(int num) {
helperRef.set(new Helper(num));
}
}
|
The immutable Helper
class remains is unchanged.
Risk Assessment
The incorrect assumption that classes containing that contain only references to immutable objects are themselves immutable is misleading and can cause serious thread-safety issues.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|
VNA01-J |
Low |
Probable |
Medium | P4 |
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 |
---|
\[[API 06|AA. Java References#API 06]\]
\[[JPL 06|AA. Java References#JPL 06]\], 14.10.2. Final Fields and Security: |
.
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
ThreadSafe |
| CCE_SL_INCONSISTENT | Implemented | ||||||
SonarQube |
| S2886 | Getters and setters should be synchronized in pairs |
Bibliography
Issue Tracking
Tasklist | ||||
---|---|---|---|---|
| ||||
||Completed||Priority||Locked||CreatedDate||CompletedDate||Assignee||Name||
|F|M|F|1270826173609| |dmohindr|"Unfortunately, a separate thread -could- *can* observe a stale reference in the helper field of the Foo class."|
|T|M|F|1270826698362|1271441478121|svoboda|"This compliant solution synchronizes the methods of *class* Foo -class- " (it sounds strange with class occuring after Foo)|
|
...
FIO36-J. Do not create multiple buffered wrappers on an InputStream 09. Input Output (FIO) 09. Input Output (FIO)