From: "Joseph Bowbeer" <jozart@csi.com>
To: <java-security@java.sun.com>
Subject: JDK 1.2.1 security-related bug
Date: Fri, 9 Apr 1999 16:16:32 -0700
In JDK 1.2, an applet was permitted to interrupt a thread that was no longer
alive, but in JDK 1.2.1, this raises a security exception.
I believe this incompatible change is a bug. It seems likely that it is a
side-effect of the applet security bug-fix introduced in JDK 1.2.1.
I've tested this on JDK 1.2.1 Win32. I haven't tested on JDK1.2.2EA.
I'm appending a simple test applet. The test is implemented in the button
action: a 'waiter' thread first waits for a 'sleeper' thread to finish and
then interrupts the sleeper thread. join() is used to wait for the sleeper
to finish. Now, ordinarily one wouldn't wait for a thread to finish and then
interrupt it, but the test does this to demonstrate the problem:
=>appletviewer Test.html
java.security.AccessControlException: access denied
(java.lang.RuntimePermission modifyThread )
at java.security.AccessControlContext.checkPermission(Compiled Code)
at java.security.AccessController.checkPermission(Compiled Code)
at java.lang.SecurityManager.checkPermission(Compiled Code)
at sun.applet.AppletSecurity.checkAccess(AppletSecurity.java:112)
at java.lang.Thread.checkAccess(Thread.java:1043)
at java.lang.Thread.interrupt(Thread.java:660)
at Test$3.run(Test.java:39)
at java.lang.Thread.run(Thread.java:479)
Note: If the join() is changed to join(1000), or removed entirely, there is
no security exception because the sleeper thread will still be sleeping when
it is interrupted. (But if the thread is already dead, then the attempt to
interrupt throws a security exception..)
One workaround is to synchronize around the interrupt:
synchronized(sleeper) {
if (sleeper.isAlive()) {
sleeper.interrupt();
}
}
Another workaround is to catch the exception.
/* Test.java */
import javax.swing.*;
import java.awt.event.*;
public class Test extends JApplet {
private JButton jButton;
public Test() {
jButton = new JButton ();
jButton.setText ("Start");
jButton.addActionListener (new ActionListener () {
public void actionPerformed (ActionEvent evt) {
jButtonActionPerformed (evt);
}
}
);
getContentPane ().add (jButton);
}
private void jButtonActionPerformed (ActionEvent evt) {
final Thread sleeper = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(3000);
}
catch (InterruptedException ex) { }
}
});
Thread waiter = new Thread(new Runnable() {
public void run() {
try {
sleeper.join();
}
catch (InterruptedException ex) { }
sleeper.interrupt(); /* EXCEPTION */
SwingUtilities.invokeLater(new Runnable() {
public void run() {
jButton.setEnabled(true);
}
});
}
});
jButton.setEnabled(false);
sleeper.start();
waiter.start();
}
}
-- Joseph Bowbeer