-
Bug
-
Resolution: Fixed
-
P2
-
1.0
-
1.0beta
-
sparc
-
solaris_2.4
-
Not verified
When stopping threads we currently rely on threads responding to a ThreadDeath
exception posted asynchronously against the threads to be stopped. However,
the ThreadDeath mechanism is parasitic on the exception mechanism. In the
interpreter, the only VM instructions that check for exceptions are those that might
have caused an exception to be raised synchronously (e.g. field access, method call
or divide). VM instructions that can't raise an exception don't even look. When a
ThreadDeath is posted, the nearest instruction that looks for exceptions will process
it and the thread will exit.
Unfortunately, it is possible to write long or infinite computations that contain no
instructions that can synchronously raise exceptions. These computations will never
check for ThreadDeath posted against them. The simplest example of this is a
while-forever loop with an empty body. Such a thread is currently impossible to
kill, which is a security hole.
A sufficient guard against this problem is to add a check for exceptions on every
backwards branch. Note that it is not sufficient to do this for a subset of the
branch instructions and compile loops using those, or to add a new "check
exceptions" instruction that would live in every loop, because we also need to
protect against code generated by evil compilers that use different instructions.
An example that exhibits the problem:
class Runner extends Thread {
public int tick = 1;
public void run() {
try {
while (tick < 1000000) {
// System.err.println("Hello " + tick);
tick++;
}
} catch (ThreadDeath exc) {
System.err.println("Runner Dying");
}
}
}
class Race {
public static void main(String args[]) {
Runner runner = new Runner();
runner.setPriority(2);
runner.start();
Thread.currentThread().sleep(100);
runner.stop();
System.err.println("Delivered stop to runner 1");
}
}
In the current runtime, the Runner will time out and never catch the ThreadDeath
exception, because its little loop contains no instructions that check for exceptions.
If the println of "Hello" is commented back in, the method calls it entails will
check, and the ThreadDeath will be caught.
exception posted asynchronously against the threads to be stopped. However,
the ThreadDeath mechanism is parasitic on the exception mechanism. In the
interpreter, the only VM instructions that check for exceptions are those that might
have caused an exception to be raised synchronously (e.g. field access, method call
or divide). VM instructions that can't raise an exception don't even look. When a
ThreadDeath is posted, the nearest instruction that looks for exceptions will process
it and the thread will exit.
Unfortunately, it is possible to write long or infinite computations that contain no
instructions that can synchronously raise exceptions. These computations will never
check for ThreadDeath posted against them. The simplest example of this is a
while-forever loop with an empty body. Such a thread is currently impossible to
kill, which is a security hole.
A sufficient guard against this problem is to add a check for exceptions on every
backwards branch. Note that it is not sufficient to do this for a subset of the
branch instructions and compile loops using those, or to add a new "check
exceptions" instruction that would live in every loop, because we also need to
protect against code generated by evil compilers that use different instructions.
An example that exhibits the problem:
class Runner extends Thread {
public int tick = 1;
public void run() {
try {
while (tick < 1000000) {
// System.err.println("Hello " + tick);
tick++;
}
} catch (ThreadDeath exc) {
System.err.println("Runner Dying");
}
}
}
class Race {
public static void main(String args[]) {
Runner runner = new Runner();
runner.setPriority(2);
runner.start();
Thread.currentThread().sleep(100);
runner.stop();
System.err.println("Delivered stop to runner 1");
}
}
In the current runtime, the Runner will time out and never catch the ThreadDeath
exception, because its little loop contains no instructions that check for exceptions.
If the println of "Hello" is commented back in, the method calls it entails will
check, and the ThreadDeath will be caught.