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). The method notify()
is deceptive in most cases unless all of the following conditions hold: [[Goetz 06]]
- Only one condition predicate is used with the locked object. Also, each thread must execute the same code after waking up from a wait.
- 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.
These requirements are typically true when only one thread is waiting. Otherwise, if either condition does not hold, incorrect program behavior may result.
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.
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.
class SleepyThread implements Runnable { private static final Object lock = new Object(); private static int step = 1; private int myStep; // do stuff when the step raches this value public SleepyThread(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--) { SleepyThread ms = new SleepyThread(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 cond
is used to let threads wait on different condition predicates.
final Condition cond = lock.newCondition(); // ... public void run (){ lock.lock(); try { if(number == 1) { while(buffer_count == 0) { cond.await(); } } else if(number == 2) { while(buffer_count == 10) { cond.await(); } } else if(number == 3) { cond.signal(); } } catch (InterruptedException ie) { // Handle the exception Thread.currentThread().interrupt(); // Reset interrupted status } finally { lock.unlock(); } }
Similar to Object.notify()
, the cond.signal()
method may choose either of the threads 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.
} else if(number == 3) { cond.signalAll(); }
Compliant Solution (unique Condition variable per thread)
This compliant solution uses two different condition variables full
and empty
to indicate whether the the buffer is full or empty, respectively.
final Lock lock = new ReentrantLock(); final Condition full = lock.newCondition(); final Condition empty = lock.newCondition(); // ... public void run (){ lock.lock(); try { if(number == 1) { while(buffer_count == 0) { empty.await(); } } else if(number == 2) { while(buffer_count == 10) { full.await(); } } else if(number == 3) { empty.signal(); full.signal(); } } catch (InterruptedException ie) { // Handle the exception Thread.currentThread().interrupt(); // Reset interrupted status } finally { lock.unlock(); } }
Even though signal()
is used, it is guaranteed that only one thread will awaken because each condition predicate corresponds to a unique Condition
variable.
Exceptions
EX1: If there are several similar threads waiting for a notification, and it is permissible to invoke any of them, notify()
may be used. The criteria for liveness is relaxed in this case.
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