Calling overridable methods from the clone()
method is insecure. First, a malicious subclass may override the method and affect the behavior of the clone()
method. Second, a trusted subclass can observe (and potentially modify) the cloned object in a partially initialized state before its construction has concluded. Consequently, the subclass can leave the clone, the object being cloned, or both in an inconsistent state.
This rule is closely related to MET04-J. Ensure that constructors do not call overridable methods.
Noncompliant Code Example
This noncompliant code example shows two classes, BadClone
and Sub
. The class BadClone
calls an overridable method doSomething()
. The overridden method sets the value of the cookies; the overriding method sets the values of the domain names. The doSomething()
method of the subclass Sub
is erroneously executed twice at runtime because of polymorphism. The first invocation comes from BadClone.clone()
, and the other comes from Sub.clone()
. Consequently, the cookies never get their values initialized, while their domains are initialized twice.
Furthermore, the subclass not only sees the clone in an inconsistent state but also modifies the clone in a manner that creates inconsistent copies. This is because the deepCopy()
method occurs after the call to the doSomething()
method, and the overriding doSomething()
implementation erroneously modifies the object.
class CloneExample implements Cloneable { HttpCookie[] cookies; CloneExample(HttpCookie[] c) { cookies = c; } public Object clone() throws CloneNotSupportedException { final CloneExample clone = (CloneExample) super.clone(); clone.doSomething(); // Invokes overridable method clone.cookies = clone.deepCopy(); return clone; } void doSomething() { // Overridable for (int i = 0; i < cookies.length; i++) { cookies[i].setValue("" + i); } } HttpCookie[] deepCopy() { if (cookies == null) { throw new NullPointerException(); } // deep copy HttpCookie[] cookiesCopy = new HttpCookie[cookies.length]; for (int i = 0; i < cookies.length; i++) { // Manually create a copy of each element in array cookiesCopy[i] = (HttpCookie) cookies[i].clone(); } return cookiesCopy; } } class Sub extends CloneExample { Sub(HttpCookie[] c) { super(c); } public Object clone() throws CloneNotSupportedException { final Sub clone = (Sub) super.clone(); clone.doSomething(); return clone; } void doSomething() { // Erroneously executed for (int i = 0;i < cookies.length; i++) { cookies[i].setDomain(i + ".foo.com"); } } public static void main(String[] args) throws CloneNotSupportedException { HttpCookie[] hc = new HttpCookie[20]; for (int i = 0 ; i < hc.length; i++){ hc[i] = new HttpCookie("cookie" + i,"" + i); } CloneExample bc = new Sub(hc); bc.clone(); } }
If an overridable method is invoked on a shallow copy of the object, the original object is also modified.
Compliant Solution
This compliant solution declares both the doSomething()
and the deepCopy()
methods final, preventing overriding of these methods.
class CloneExample implements Cloneable { final void doSomething() { // ... } final HttpCookie[] deepCopy() { // ... } // ... }
Alternative solutions that prevent invocation of overloaded methods include declaring these methods private, declaring the class final, or eliminating the method calls by congregating the code.
Risk Assessment
Calling overridable methods on the clone under construction can expose class internals to malicious code or violate class invariants by exposing the clone to trusted code in a partially initialized state, affording the opportunity to corrupt the state of the clone, the object being cloned, or both.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
MET07-J |
medium |
probable |
low |
P12 |
L1 |
Automated Detection
Automated detection appears to be straightforward.
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Other Languages
This rule appears in the C++ Secure Coding Standard as ARR40-CPP. Use a valid ordering rule.
Bibliography
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="ecdd8577-bbc6-4bbc-a8d3-52d1f554812a"><ac:plain-text-body><![CDATA[ |
[[Bloch 2008 |
AA. Bibliography#Bloch 08]] |
Item 11: Override |
]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="6641009c-0384-40f3-815f-4f50782c0347"><ac:plain-text-body><![CDATA[ |
[[Gong 2003 |
AA. Bibliography#Gong 03]] |
|
]]></ac:plain-text-body></ac:structured-macro> |
05. Methods (MET) MET11-J. Never declare a class method that hides a method declared in a superclass or superinterface