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

Compare with Current View Page History

« Previous Version 54 Next »

The methods java.lang.Object.notify() and java.lang.Object.notifyAll() are used to waken waiting thread(s). These methods must be called from code that holds the same object lock as the waiting thread(s).

Any thread that has called wait() expects to wake up when some condition predicate becomes true. As recommended by CON18-J. Always invoke wait() and await() methods inside a loop, any waiting thread, when woken, will test its condition predicate, and resume waiting if the predicate has not become true.

Consequently, the notifyAll() method is recommended rather than the notify() method. The notifyAll() method will wake up all threads, and only threads whose condition predicate is satisfied will remain awake. Furthermore, if all of the threads require a specific lock, only one will obtain the lock, and the others may, presumably, resume waiting. The notify() method wakes up only one thread, and makes no guarantees as to which thread gets woken up. If the thread's condition predicate is not satisified, the chosen thread may resume waiting, defeating the purpose of the notify() call.

The notify() method should only be called if

  • Every condition predicate on every thread waiting on the object is satisfied.
  • Only one thread must wake up on the notify signal. This is contingent on the condition predicate, in that, only one predicate must fulfill the condition and allow the thread to proceed.
  • No untrusted code has access to the object being waited on. If untrusted code has access to this object, it can wait() on the object and intercept a notify() call.

The java.util.concurrent utilities (interface Condition) provide the signal() and signalAll() methods to awaken waiting threads that are blocked on an await() call. Like the notify() method, the signal() method wakes up any one of the threads that is waiting on the condition and consequently, may be insecure. Similarly, any thread that is blocked on a wait() method invocation on a Java object being used as a condition queue, should be notified using notifyAll().

Noncompliant Code Example (notify())

This noncompliant code example demonstrates a complex multi-step process being undertaken by several threads. Each thread executes one step of the process; the step being currently performed is maintained by the step field. Each thread waits for the step field to indicate that it is time to perform that thread's step. When it is time to perform its step, each thread does so. It then increments step for the next thread, notifies the thread, and exits.

public class ProcessStep implements Runnable {
  private static final Object lock = new Object();
  private static int time = 1;
  private int myStep; // do stuff when the step raches this value

  public ProcessStep(int myStep) {
    this.myStep = myStep;
  }

  public void run() {
    try {
      synchronized (lock) {
        while (time != myStep) { 
          lock.wait();  
        }

        // ... do stuff

        time++;
        lock.notify();
      }
    } catch (InterruptedException ie) {
      Thread.currentThread().interrupt(); // Reset interrupted status
    }    
  }

  public static void main(String[] args) {
    for (int i = 5; i > 0; i--) {
      ProcessStep ms = new ProcessStep(i);
      new Thread(ms).start();
    }
  }
}

This noncompliant code example violates the liveness property. Each thread has a different condition predicate, as each requires step to have a different value before proceeding. The Object.notify() method wakes up only one thread at a time; unless it happens to wake up the thread that is to perform the next step, the program will deadlock.

Compliant Solution (notifyAll())

In this compliant solution, each thread uses notifyAll() to wake all of the other threads when its step is complete. Consequently the proper thread can perform its next step, while all other threads note that their condition predicate has not been satisified and promptly go back to sleep.

  public void run() {
    try {
      synchronized (lock) {
        while (time != myStep) { 
          lock.wait();  
        }

        // ... do stuff

        time++;
        lock.notifyAll();
      }
    } catch (InterruptedException ie) {
      Thread.currentThread().interrupt(); // Reset interrupted status
    }    
  }

Noncompliant Code Example (Condition interface)

This noncompliant code example derives from the previous noncompliant code example but uses the Condition interface. Field condition is used to let threads wait on different condition predicates.

public class ProcessStep implements Runnable {
  private static final Lock lock = new ReentrantLock();
  private static final Condition condition = lock.newCondition();
  private static int time = 1;
  private int myStep; // do stuff when the step raches this value

  public ProcessStep(int myStep) {
    this.myStep = myStep;
  }

  public void run() {
    lock.lock();
    try {
      while (time != myStep) { 
        condition.await();  
      }

      // ... do stuff

      time++;
      condition.signal();
    } catch (InterruptedException ie) {
      Thread.currentThread().interrupt(); // Reset interrupted status
    } finally {
      lock.unlock();
    }
  }

  public static void main(String[] args) {
    for (int i = 5; i > 0; i--) {
      ProcessStep ms = new ProcessStep(i);
      new Thread(ms).start();
    }
  }
}

Similar to Object.notify(), the cond.signal() method may choose any one thread and awaken it.

Compliant Solution (signalAll())

This compliant solution uses the signalAll() method to resume all the waiting threads whose condition predicate allows doing so.

  public void run() {
    lock.lock();
    try {
      while (time != myStep) { 
        condition.await();  
      }

      // ... do stuff

      time++;
      condition.signalAll();
    } catch (InterruptedException ie) {
      Thread.currentThread().interrupt(); // Reset interrupted status
    } finally {
      lock.unlock();
    }
  }

Compliant Solution (unique Condition per thread)

This compliant solution assigns each thread its own Condition, and makes them accessible to all the threads.

public class ProcessStep implements Runnable {
  private static final Lock lock = new ReentrantLock();
  private static final Condition[] conditions = new Condition[6];
  private static int time = 1;
  private int myStep; // do stuff when the step raches this value

  public ProcessStep(int myStep) {
    this.myStep = myStep;
    conditions[myStep] = lock.newCondition();
  }

  public void run() {
    lock.lock();
    try {
      while (time != myStep) { 
        conditions[myStep].await();  
      }

      // ... do stuff

      time++;
      if (myStep + 1 < conditions.length) {
        conditions[myStep+1].signal();
      }
    } catch (InterruptedException ie) {
      Thread.currentThread().interrupt(); // Reset interrupted status
    } finally {
      lock.unlock();
    }
  }

  public static void main(String[] args) {
    for (int i = 5; i > 0; i--) {
      ProcessStep ms = new ProcessStep(i);
      new Thread(ms).start();
    }
  }
}

Even though signal() is used, it is guaranteed that only one thread will awaken because each condition predicate corresponds to a unique Condition variable.

Risk Assessment

Invoking the notify() method instead of notifyAll() can be a threat to the liveness property of the system.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

CON19- J

low

unlikely

medium

P2

L3

Automated Detection

TODO

Related Vulnerabilities

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

References

[[JLS 05]] Chapter 17, Threads and Locks
[[Goetz 06]] Section 14.2.4, Notification
[[Bloch 01]] Item 50: Never invoke wait outside a loop


CON18-J. Always invoke wait() and await() methods inside a loop      11. Concurrency (CON)      CON20-J. Do not perform operations that may block while holding a lock

  • No labels