You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 31 Next »

Synchronizing on the return value of the Object.getClass() method, rather than a class literal can lead to unexpected behavior. Whenever the implementing class is subclassed, the subclass locks on the subclass's type, which is a completely different Class object.

Section 4.3.2 "The Class Object" of the Java Language specification describes how method synchronization works [[JLS 05]]:

A class method that is declared synchronized synchronizes on the lock associated with the Class object of the class.

This does not mean that a subclass using getClass() can only synchronize on the Class object of the base class. In fact, it will lock on its own Class object, which may or may not be what the programmer intended. The intent should be clearly documented or annotated. Note that if a subclass does not override an accessible noncompliant superclass's method, it inherits the method which may lead to the false conclusion that the superclass's intrinsic lock is available in the subclass.

When synchronizing on a class literal, the corresponding lock object should not be accessible to untrusted code. If the class is package-private, callers from other packages may not access the class object, ensuring its trustworthiness as an intrinsic lock object. For more information, see [CON07-J. Use private final lock objects to synchronize classes that may interact with untrusted code].

Noncompliant Code Example (getClass() Lock Object)

In this noncompliant code example, the parse() method of class Base parses a date and synchronizes on the class object returned by getClass(). The class Derived also inherits the parse() method. However, this inherited method synchronizes on the class object of Derived because of the particular return value of getClass().

Derived also adds a method doSomethingAndParse() that locks on the class object of the base class because the developer misconstrued that the parse() method in Base always obtains a lock on the Base class object, and doSomethingAndParse() must follow the same locking policy. Consequently, the class Derived has two different locking strategies and is not thread-safe.

class Base {
  static DateFormat format =
    DateFormat.getDateInstance(DateFormat.MEDIUM);

  public Date parse(String str) throws ParseException {
    synchronized (getClass()) {
      return format.parse(str);
    }
  }
}

class Derived extends Base {
  public Date doSomethingAndParse(String str) throws ParseException {
    synchronized(Base.class) {
      // ...
      return format.parse(str); 
    }
  }
}
Unknown macro: {mc}

// Hidden main() method to be put in class Derived
// Prints arbitrary date values and throws exceptions at times

public static void main(String[] args) {
for(int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override public void run() {
try

Unknown macro: { System.out.println(new Derived().parse("Jan 1, 2010")); }

catch (ParseException e)

Unknown macro: { // Forward to handler }

}
}).start();

new Thread(new Runnable() {
@Override public void run() {
try

Unknown macro: { System.out.println(new Derived().doSomethingAndParse("Jan 2, 2010")); }

catch (ParseException e)

}
}).start();
}
}

Compliant Solution (Class Name Qualification)

In this compliant solution, the class name providing the lock (Base) is fully qualified.

class Base {
  static DateFormat format =
    DateFormat.getDateInstance(DateFormat.MEDIUM);
    
  public Date parse(String str) throws ParseException {
    synchronized (Base.class) {
      return format.parse(str);
    }
  }
}

// ...

This code example always synchronizes on the Base.class object, even if it is called from a Derived object.

Compliant Solution (Class.forName())

This compliant solution uses the Class.forName() method to synchronize on the Base class's Class object.

class Base {
  static DateFormat format =
    DateFormat.getDateInstance(DateFormat.MEDIUM);
    
  public Date parse(String str) throws ParseException {
    try {
      synchronized (Class.forName("Base")) {
        return format.parse(str);
      }
    } catch (ClassNotFoundException x) {
      // forward to handler
    }
    return null;
  }
}

// ...

It is important that untrusted inputs are not accepted as arguments while loading classes using Class.forName(). See [SEC05-J. Do not expose standard APIs that use the immediate caller's class loader instance to untrusted code] for more information.

Noncompliant Code Example (getClass() Lock Object, Inner Class)

This noncompliant code example synchronizes on the class object returned by getClass() in the parse() method of Base class. The Base class also has a nested Helper class, whose doSomethingAndParse() method incorrectly synchronizes on the value returned by getClass().

class Base {
  static DateFormat format =
    DateFormat.getDateInstance(DateFormat.MEDIUM);
    
  public Date parse(String str) throws ParseException {
    synchronized (getClass()) {
      return format.parse(str);
    }
  }

  public Date doSomething(String str) throws ParseException {
    return new Helper().doSomethingAndParse(str);
  }
  
  private class Helper { 
    public Date doSomethingAndParse(String str) throws ParseException {    
      synchronized(getClass()) { // Synchronizes on getClass()
        // ...
        return format.parse(str); 
      }
    }
  }
}

The call to getClass() in class Helper returns a Helper class object instead of the Base class object. Consequently, a thread that calls Base.parse() locks on a different object than a thread that calls Base.doSomething(). It is easy to overlook concurrency errors in inner classes because they exist within the body of the containing outer class. A reviewer might incorrectly assume that the two classes have the same locking strategy.

Compliant Solution (Class Name Qualification)

This compliant solution synchronizes using a Base class literal in the parse() and doSomethingAndParse() methods.

class Base {
  static DateFormat format =
    DateFormat.getDateInstance(DateFormat.MEDIUM);

  public Date parse(String str) throws ParseException {
    synchronized (Base.class) {
      return format.parse(str);
    }
  }

  private class Helper {
    public Date doSomethingAndParse(String str) throws ParseException {    
      synchronized(Base.class) { // Synchronizes on Base class literal
        // ...
        return format.parse(str); 
      }
    }
  }
}

Consequently, both Base and Helper lock on Base's intrinsic lock. Similarly, the Class.forname() method can be used instead of a class literal.

Issue Tracking

0%

Review List

Risk Assessment

Synchronizing on the class object returned by getClass() can result in non-deterministic behavior.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CON09- J

medium

probable

medium

P8

L2

Automated Detection

Unknown macro: {mc}

// Ignore
The following table summarizes the examples flagged as violations by FindBugs:

Noncompliant Code Example

Flagged

Checker

Message

getClass() lock object

No

WL_USING_GETCLASS_RATHER_THAN_CLASS_LITERAL

n/a

The following table summarizes the examples flagged as violations by SureLogic Flashlight:

Noncompliant Code Example

Flagged

Message

getClass() lock object

No

No data available about field accesses

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

References

[[API 06]]
[[Findbugs 08]].
[[Pugh 08]] "Synchronization"
[[Miller 09]] Locking


[!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_left.png!]      [!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_up.png!]      [!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_right.png!]

  • No labels