Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

To

...

avoid

...

data

...

corruption

...

in

...

multithreaded

...

Java

...

programs,

...

shared

...

data

...

must

...

be

...

protected

...

from

...

concurrent

...

modifications

...

and

...

accesses.

...

This

...

can

...

be

...

performed

...

at

...

the

...

object

...

level

...

by

...

using

...

synchronized

...

methods

...

or

...

blocks,

...

or

...

by

...

using

...

dynamic

...

lock

...

objects.

...

However,

...

excessive

...

use

...

of

...

locking

...

may

...

result

...

in

...

deadlocks

...

(See

...

CON08-J.

...

Do

...

not

...

call

...

alien

...

methods

...

that

...

synchronize

...

on

...

the

...

same

...

objects

...

as

...

any

...

callers

...

in

...

the

...

execution

...

chain

...

).

...

For

...

instance,

...

to

...

avoid

...

deadlocks,

...

the

...

paint()

...

,

...

dispose()

...

,

...

stop()

...

,

...

destroy()

...

methods

...

in

...

an

...

applet

...

should

...

not

...

be

...

synchronized

...

because

...

they

...

are

...

always

...

called

...

and

...

used

...

from

...

dedicated

...

threads.

...

Wiki Markup
"The Java programming language neither prevents nor requires detection of deadlock conditions." \[[JLS 05|AA. Java References#JLS 05]\]. Deadlocks can arise when two or more threads request and release locks in different orders. 

Noncompliant Code Example

This noncompliant code example can deadlock because of excessive synchronization. Assume that an attacker has two bank accounts and is capable of requesting two depositAllAmount() operations in succession, one each from the two threads started in main().

Code Block
bgColor#FFcccc



h2. Noncompliant Code Example

This noncompliant code example can deadlock because of excessive synchronization. Assume that an attacker has two bank accounts and is capable of requesting two {{depositAllAmount()}} operations in succession, one each from the two threads started in {{main()}}. 

{code:bgColor=#FFcccc}
class BankAccount {
  private int balanceAmount;  // Total amount in bank account
	 
  private BankAccount(int balance) {
    this.balanceAmount = balance;
  }

  // Deposits the amount from this object instance to BankAccount instance argument ba 
  private void depositAllAmount(BankAccount ba) {
    synchronized (this) {
      synchronized(ba) {
        ba.balanceAmount += this.balanceAmount;
        this.balanceAmount = 0; // withdraw all amount from this instance
        ba.displayAllAmount();  // Display the new balanceAmount in ba (may cause deadlock)
      }
    } 
  }
  
  private synchronized void displayAllAmount() {
    System.out.println(balanceAmount);
  }

  public static void initiateTransfer(final BankAccount first, final BankAccount second) {
    Thread t = new Thread(new Runnable() {
      public void run() {
        first.depositAllAmount(second);
      }
    });
    t.start();
  }
}
{code}

Objects

...

of

...

class

...

BankAccount

...

represent

...

bank

...

accounts. The balanceAmount field represents the total balance amount available for a particular object (bank account). A user is allowed to initiate an operation deposit all amount that transfers the balance amount from one account to another. This is equivalent to closing a bank account and transferring the balance to a different (existing or new) account.

Objects of this class are deadlock-prone. An attacker may cause the program to construct two threads that initiate balance transfers from two different BankAccount object instances, a and b. Consider the following code that does this:

Code Block
 The {{balanceAmount}} field represents the total balance amount available for a particular object (bank account). A user is allowed to initiate an operation _deposit all amount_ that transfers the balance amount from one account to another. This is equivalent to closing a bank account and transferring the balance to a different (existing or new) account. 

Objects of this class are deadlock-prone. An attacker may cause the program to construct two threads that initiate balance transfers from two different {{BankAccount}} object instances, {{a}} and {{b}}. Consider the following code that does this:

{code}
BankAccount a = new BankAccount(5000);
BankAccount b = new BankAccount(6000);
initiateTransfer(a, b); // starts thread 1
initiateTransfer(b, a); // starts thread 2
{code}

The

...

two

...

transfers

...

are

...

performed

...

in

...

their

...

own

...

threads,

...

from

...

instance

...

a

...

to b and b to a. The first thread atomically transfers the amount from a to b by depositing the balance from a to b and withdrawing the entire balance from a. The second thread performs the reverse operation, that is, it transfers the balance from b to a and withdraws the balance from b. When executing depositAllAmount(), the first thread might acquire a lock on object a while the second thread may acquire a lock on object b. Subsequently, the first thread requests a lock on b which is already held by the second thread and the second thread requests a lock on a which is already held by the first thread. This constitutes a deadlock condition, as neither thread can proceed.

The threads in this program request monitors in varying order depending on the interleaving of method calls. If Thread T1 finishes executing before Thread T2, or T2 before T1, there are no issues because in these cases, locks are acquired and released in the same order. Sequences where the threads alternate, such as, T1, T2, T1, T2 may deadlock.

Compliant Solution (single private lock)

The deadlock can be avoided by using a single lock to acquire before doing any account transfers.

Code Block
bgColor#ccccff
 {{b}} and {{b}} to {{a}}. The first thread atomically transfers the amount from {{a}} to {{b}} by depositing the balance from {{a}} to {{b}} and withdrawing the entire balance from {{a}}. The second thread performs the reverse operation, that is, it transfers the balance from {{b}} to {{a}} and withdraws the balance from {{b}}. When executing {{depositAllAmount()}}, the first thread might acquire a lock on object {{a}} while the second thread may acquire a lock on object {{b}}. Subsequently, the first thread requests a lock on {{b}} which is already held by the second thread and the second thread requests a lock on {{a}} which is already held by the first thread. This constitutes a deadlock condition, as neither thread can proceed.

The threads in this program request monitors in varying order depending on the interleaving of method calls. If {{Thread T1}} finishes executing before {{Thread T2}}, or {{T2}} before {{T1}}, there are no issues because in these cases, locks are acquired and released in the same order. Sequences where the threads alternate, such as, {{T1}}, {{T2}}, {{T1}}, {{T2}} may deadlock.    


h2. Compliant Solution (single private lock)

The deadlock can be avoided by using a single lock to acquire before doing any account transfers.

{code:bgColor=#ccccff}
class BankAccount {
  private static int balanceAmount;  // Total amount in bank account
	 
  private final Object lock;

  private BankAccount(int balance) {
    this.balanceAmount = balance;
    this.lock = new Object();
  }

  // Deposits the amount from this object instance to BankAccount instance argument ba 
  private void depositAllAmount(BankAccount ba) {
    synchronized (lock) {
      ba.balanceAmount += this.balanceAmount;
      this.balanceAmount = 0; // withdraw all amount from this instance
      ba.displayAllAmount();  // Display the new balanceAmount in ba (may cause deadlock)
    } 
  }
  
  private void displayAllAmount() {
    synchronized (lock) {
      System.out.println(balanceAmount);
    }
  }

  public static void initiateTransfer(final BankAccount first, final BankAccount second) {
    Thread t = new Thread(new Runnable() {
      public void run() {
        first.depositAllAmount(second);
      }
    });
    t.start();
  }
}
{code}

In

...

this

...

scenario,

...

if

...

two

...

threads

...

with

...

two

...

different

...

BankAccount

...

objects

...

try

...

to

...

tranfer

...

to

...

each

...

others'

...

accounts

...

simultaneously,

...

deadlock

...

cannot

...

occur.

...

One

...

thread

...

will

...

acquire

...

the

...

private

...

lock,

...

complete

...

its

...

transfer,

...

and

...

release

...

the

...

lock,

...

before

...

the

...

other

...

thread

...

may

...

proceed.

...

This

...

solution

...

comes

...

with

...

a

...

performance

...

penalty,

...

as

...

a

...

private

...

static

...

lock

...

restricts

...

the

...

system

...

to

...

only

...

performing

...

one

...

transfer

...

at

...

a

...

time.

...

Two

...

transfers

...

involving

...

four

...

distinct

...

accounts

...

(and

...

distinct

...

target

...

accounts)

...

may

...

not

...

happen

...

concurrently.

...

The

...

impact

...

of

...

this

...

penalty

...

increases

...

considerably

...

as

...

the

...

number

...

of

...

BankAccount

...

objects

...

increase.

...

Consequently

...

this

...

solution

...

does

...

not

...

scale

...

very

...

well.

...

Compliant Solution (ordered

...

locks)

...

This

...

compliant

...

solution

...

ensures

...

that

...

multiple

...

locks

...

are

...

acquired

...

and

...

released

...

in

...

the

...

same

...

order.

...

It

...

requires

...

that

...

an

...

ordering

...

over

...

BankAccount

...

objects

...

is

...

available.

...

The

...

ordering

...

is

...

enforced

...

by

...

having

...

the

...

class

...

BankAccount

...

extend

...

the

...

java.lang.Comparable

...

interface

...

and

...

overriding

...

the

...

compareTo()

...

method.

{:=
Code Block
bgColor
#ccccff
}
class BankAccount implements Comparable {
  private int balanceAmount;  // Total amount in bank account
	 
  private final Object lock;

  private BankAccount(int balance) {
    this.balanceAmount = balance;
    this.lock = new Object();
  }

  // Deposits the amount from this object instance to BankAccount instance argument ba 
  private void depositAllAmount(BankAccount ba) {
    BankAccount former, latter;
    if (compareTo(ba) < 0) {
      former = this;
      latter = ba;
    } else {
      former = ba;
      latter = this;
    }
    synchronized (former) {
      synchronized (latter) {
        ba.balanceAmount += this.balanceAmount;
        this.balanceAmount = 0; // withdraw all amount from this instance
        ba.displayAllAmount(); // Display the new balanceAmount in ba (may cause deadlock)
      } 
    }
  }
 
  private synchronized void displayAllAmount() {
    System.out.println(balanceAmount);
  }

  public static void initiateTransfer(final BankAccount first, final BankAccount second) {
    Thread t = new Thread(new Runnable() {
      public void run() {
        first.depositAllAmount(second);
      }
    });
    t.start();
  }

  public int compareTo(BankAccount ba) {
   if(this.balanceAmount < ba.balanceAmount) {
     return -1;
   } else if(this.balanceAmount > ba.balanceAmount) {
     return 1;
   } else {
     return 0;
   }
  }
}
{code}

In

...

this

...

compliant

...

solution,

...

whenever

...

a

...

transfer

...

occurs,

...

the

...

two

...

BankAccount

...

objects

...

are

...

ordered,

...

with

...

the

...

first

...

object's

...

lock

...

being

...

acquired

...

before

...

the

...

second

...

object's

...

lock.

...

Consequently

...

if

...

two

...

threads

...

attempt

...

transfers

...

between

...

the

...

same

...

two

...

accounts,

...

they

...

will

...

both

...

try

...

to

...

acquire

...

the

...

first

...

account's

...

lock

...

first,

...

with

...

the

...

result

...

that

...

one

...

thread

...

will

...

acquire

...

both

...

locks,

...

complete

...

the

...

transfer,

...

and

...

release

...

both

...

locks

...

before

...

the

...

other

...

may

...

proceed.

...

Unlike

...

the

...

previous

...

compliant

...

solution,

...

this

...

solution

...

incurs

...

no

...

performance

...

penalty,

...

as

...

multiple

...

transfers

...

can

...

occur

...

concurrently

...

as

...

long

...

as

...

the

...

transfers

...

involve

...

distinct

...

target

...

accounts.

Noncompliant Code Example

This noncompliant code example consists of an application to monitor a sports race. It maintains a list of racers. Each racer has two statistics that can be reported about them: their current speed, and their current distance travelled. This example provides methods to report on the average current speed of the racers, and the average distance of the racers. To be thread-safe, it locks each racer before it reads her statistics until after it reports the average. Consequently, the statics reported by the methods are accurate at the time the methods actually return their results.

Code Block
bgColor#FFcccc

public interface Racer {
  public double getCurrentSpeed() {
    // ...
  }

  public double getDistance() {
    // ...
  }

  //...
}

public class Race {
  final static int MAX = 20;
  static Racer[] racers = new Racer[MAX];
 
  public Race() {
    for (int i = 0; i < MAX; i++) {
      racers[i] = new Racer(/* initialize racer */);
    }
  }

  double getAverageCurrentSpeed() {
    return averageCurrentSpeedCalculator(0, 0.0);
  }

  double averageCurrentSpeedCalculator(int i, double currentSpeed) { // Acquires locks in increasing order
    if (i > MAX - 1) {
      return currentSpeed / MAX;
    }

    synchronized(racers[i]) {
      currentSpeed += racers[i].getCurrentSpeed();
      return averageCurrentSpeedCalculator(++i, currentSpeed);
    }	 
  }


  double getAverageDistance() {
    return averageDistanceCalculator(MAX - 1, 0.0);
  }

  double averageDistanceCalculator(int i, double distance) { // Acquires locks in decreasing order
    if (i <= -1) {		 
      return distance / MAX;
    } 

    synchronized(racers[i]) {
      distance += racers[i].getDistance();
      return averageDistanceCalculator(--i, distance);
    }
  }
}

This implementation is prone to deadlock because the recursive calls occur within the synchronized regions of these methods and acquire locks in opposite numerical orders. That is, averageCurrentSpeedCalculator() requests locks from index 0 to MAX - 1 (19) whereas averageDistanceCalculator() requests them from index MAX - 1 (19) to 0. Because of recursion, no previously acquired locks are released by either method. A deadlock occurs when two threads call these methods out of order in that, one thread calls averageSpeedCalculator() while the other calls averageTimeCalculator() before either method has finished executing.

For example, if one thread calls getCurrentSpeed(), it will first acquire the intrinsic lock for Racer 0, the first in the array. Meanwhile a second thread calls getCurrentDistance(), and it will first acquire the intrinsic lock for Racer 19, the last in the array. Consequently, deadlock results, as neither thread can acquire all of the locks and proceed with the calculation.

Compliant Solution

In this compliant solution, the two calculation methods use the same ordering for locking the racer objects.

Code Block
bgColor#ccccff

public class Race {
  double getAverageCurrentSpeed() {
    return averageCurrentSpeedCalculator(0, 0.0);
  }

  double averageCurrentSpeedCalculator(int i, double currentSpeed


h2. Noncompliant Code Example

This noncompliant code example consists of three integer arrays: {{distances}}, {{speeds}} and {{times}}. The distances are fixed and cannot be changed by the client. The client can pass a {{time}} array as an argument to method {{getAverageSpeed()}} to find the average speed. It can also pass a {{speed}} array as argument to method {{getAverageTime()}} to find the average time taken. This allows the client to calculate the third parameter from two given parameters. For example, speed is calculated as distance/time.

The example also uses an array of internal lock objects so that multiple threads do not interfere with the array elements when the arrays are being traversed. Because it is not possible to lock on primitive types, a direct lock on the array elements cannot be obtained. Instead, an array of raw objects, ({{locks}}) is used. This fine-grained locking strategy is more flexible than using a single global lock object which has the effect of blocking all the array elements from being accessed from other threads while the computation is in progress.

{code:bgColor=#FFcccc}
public class RecursiveTravel {
  final static int MAX = 20;
  static int[] distances = new int[MAX];
  static int[] times = new int[MAX];
  static int[] speeds = new int[MAX];
  static Object[] locks = new Object[MAX];
  
  static {
    for (int i = 0; i < MAX; i++) {
      distances[i] = 10;  // Assuming all distances are 10 for illustration
      locks[i] = new Object(); // Create lock objects
    }
  }

  double getAverageSpeed(int[] time) {
    times = time.clone();
    return averageSpeedCalculator(0, 0, 0);
  }

  double getAverageTime(int[] speed) {
    speeds = speed.clone();
    return averageTimeCalculator(MAX - 1, 0, 0);
  }

  int averageSpeedCalculator(int i, int distance, int time) { // Acquires locks in nondecreasing order
    if(i > MAX - 1) {
      return distance/time;
    }

    synchronized(locks[i]) {
      distance += distances[i];
      time += times[i];    
      return averageSpeedCalculator(++i, distance, time);
    }	  
  }

  int averageTimeCalculator(int i, int distance, int speed) { // Acquires locks in nonincreasingincreasing order
    if (i > <=MAX - 1) {		 
      return currentSpeed distance/speed MAX;
    } 

    synchronized(locksracers[i]) {
      distancecurrentSpeed += distancesracers[i];
      speed += speeds[i];  .getCurrentSpeed();
      return averageTimeCalculatoraverageCurrentSpeedCalculator(--++i, distance, speedcurrentSpeed);
    }	  
  }
}
{code}

The {{averageSpeedCalculator()}} and {{averageTimeCalculator()}} methods recursively calculate the sum of the speeds and times, respectively, for each distance value in the array. This implementation is deadlock prone because the recursive calls occur within the synchronized regions of these methods and acquire locks in opposite numerical orders. That is, {{averageSpeedCalculator()}} requests locks from index 0 to {{MAX}} - 1 (19) whereas {{averageTimeCalculator()}} requests them from index {{MAX}} - 1 (19) to 0. Because of recursion, no previously acquired locks are released by either method. A deadlock occurs when two threads call these methods out of order in that, one thread calls {{averageSpeedCalculator()}} while the other calls {{averageTimeCalculator()}} before either method has finished executing.

One such execution order that causes a deadlock is shown below:

{code}
Thread T1 (in getTime()) acquires lock:
i = 19
...
i = 0

Thread T1 (in getSpeed()) acquires lock:
i = 0
...
i = 18

Thread T2 (in getTime()) acquires lock:
i = 19

Thread T1 next wants, i = 19 which T2 holds
Thread T2 next wants, i = 18 which T1 holds
{code}


{mc}
// Class to make the above code run in a multi-threaded environment
public class RecursiveControl implements Runnable {
  static RecursiveTravel t = new RecursiveTravel();
  static int[] speed = new int[MAX];
  static int[] time = new int[MAX];
  static {
    for (int i = 0; i < MAX; i++) {
      speed[i] = 2;
      time[i] = 2;
    }
  }

  public void run() {
    t.getTime(speed); 	
    t.getSpeed(time);
  }

  public static void main(String[] args) throws InterruptedException {
    Runnable r1 = new RecursiveControl();
    Runnable r2 = new RecursiveControl();
    Thread c1 = new Thread(r1);
    Thread c2 = new Thread(r2);
    c1.start();
    c2.start();	
  }
}
{mc}


h2. Compliant Solution 

This compliant solution moves the recursive calls from the {{averageSpeedCalculator()}} and {{averageTimeCalculator()}} methods to outside the {{synchronized}} block. 

{code:bgColor=#ccccff}
public class RecursiveTravel {
  // ...

  double getAverageSpeed(int[] time) {
    times = time.clone();
    return averageSpeedCalculator(0, 0, 0);
  }

  double getAverageTime(int[] speed) {
    speeds = speed.clone();
    return averageTimeCalculator(0, 0, 0);
  }

  int averageSpeedCalculator(int i, int distance, int time) { // Acquires locks in nondecreasing order
    if(i > MAX - 1) {
      return distance/time;
    }

    synchronized(locks[i]) {
      distance += distances[i];
      time += times[i];    
    }	  
    return averageSpeedCalculator(++i, distance, time); // Moved outside the synchronized region
  }

  int averageTimeCalculator(int i, int distance, int speed) { // Acquires locks in nondecreasing order
    if(i <= -1) {		 
      return distance/speed;
    } 

    synchronized(locks[i]) {
      distance += distances[i];
      speed += speeds[i];  
    }	  
    return averageTimeCalculator(--i, distance, speed); // Moved outside the synchronized region
  }
}
{code}

Consequently, locks are released as soon as they are no longer needed. Also, the locks are acquired in the same order (nondecreasing) from both these methods. This eliminates potential deadlock conditions.  


h2. Risk Assessment

Acquiring and releasing locks in the wrong order may result in deadlocks.

|| Rule || Severity || Likelihood || Remediation Cost || Priority || Level ||
| CON12- J | low | likely | high | {color:green}{*}P3{*}{color} | {color:green}{*}L3{*}{color} |

h3. Automated Detection

TODO

h3. Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the [CERT website|https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+CON34-J].

h2. References

\[[JLS 05|AA. Java References#JLS 05]\] [Chapter 17, Threads and Locks|http://java.sun.com/docs/books/jls/third_edition/html/memory.html]
\[[Halloway 00|AA. Java References#Halloway 00]\] 
\[[MITRE 09|AA. Java References#MITRE 09]\] [CWE ID 412|http://cwe.mitre.org/data/definitions/412.html] "Unrestricted Lock on Critical Resource"

----
[!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_left.png!|CON11-J. Do not assume that declaring an object volatile guarantees visibility of its members]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_up.png!|11. Concurrency (CON)]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[!The CERT Sun Microsystems Secure Coding Standard for Java^button_arrow_right.png!|CON13-J. Do not try to force thread shutdown]

 double getAverageDistance() {
    return averageDistanceCalculator(0, 0.0);
  }

  double averageDistanceCalculator(int i, double distance) { // Acquires locks in increasing order
    if (i > MAX - 1) {		 
      return distance / MAX;
    } 

    synchronized(racers[i]) {
      distance += racers[i].getDistance();
      return averageDistanceCalculator(++i, distance);
    }
  }
}

Consequently, while one thread is producing a calculation, no other thread can produce a calculation. This is because the other thread would first have to synchronize on racers0, which is impossible until the first calculation is complete.

Risk Assessment

Acquiring and releasing locks in the wrong order may result in deadlocks.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CON12- J

low

likely

high

P3

L3

Automated Detection

TODO

Related Vulnerabilities

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

References

Wiki Markup
\[[JLS 05|AA. Java References#JLS 05]\] [Chapter 17, Threads and Locks|http://java.sun.com/docs/books/jls/third_edition/html/memory.html]
\[[Halloway 00|AA. Java References#Halloway 00]\] 
\[[MITRE 09|AA. Java References#MITRE 09]\] [CWE ID 412|http://cwe.mitre.org/data/definitions/412.html] "Unrestricted Lock on Critical Resource"

...

CON11-J. Do not assume that declaring an object volatile guarantees visibility of its members      11. Concurrency (CON)      CON13-J. Do not try to force thread shutdown