Add SecureSM support for newer IDEA versions (#49747)

IntelliJ IDEA moved their JUnit runner to a different package. While this does not break running
tests in IDEA, it leads to an ugly exception being thrown at the end of the tests:

Exception in thread "main" java.lang.SecurityException: java.lang.System#exit(0) calls are not
allowed
	at org.elasticsearch.secure_sm.SecureSM$2.run(SecureSM.java:248)
	at org.elasticsearch.secure_sm.SecureSM$2.run(SecureSM.java:215)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:310)
	at org.elasticsearch.secure_sm.SecureSM.innerCheckExit(SecureSM.java:215)
	at org.elasticsearch.secure_sm.SecureSM.checkExit(SecureSM.java:206)
	at java.base/java.lang.Runtime.exit(Runtime.java:111)
	at java.base/java.lang.System.exit(System.java:1781)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:59)

This commit adds support for newer IDEA versions in SecureSM.
This commit is contained in:
Yannick Welsch 2019-12-04 13:49:43 +01:00
parent aa443c6362
commit 6dcb7fa50e
1 changed files with 20 additions and 18 deletions

View File

@ -46,16 +46,16 @@ import java.util.Objects;
* <ul> * <ul>
* <li>{@code modifyThread} and {@code modifyThreadGroup} are required for any thread access * <li>{@code modifyThread} and {@code modifyThreadGroup} are required for any thread access
* checks: with these permissions, access is granted as long as the thread group is * checks: with these permissions, access is granted as long as the thread group is
* the same or an ancestor ({@code sourceGroup.parentOf(targetGroup) == true}). * the same or an ancestor ({@code sourceGroup.parentOf(targetGroup) == true}).
* <li>code without these permissions can do very little, except to interrupt itself. It may * <li>code without these permissions can do very little, except to interrupt itself. It may
* not even create new threads. * not even create new threads.
* <li>very special cases (like test runners) that have {@link ThreadPermission} can violate * <li>very special cases (like test runners) that have {@link ThreadPermission} can violate
* threadgroup security rules. * threadgroup security rules.
* </ul> * </ul>
* <p> * <p>
* If java security debugging ({@code java.security.debug}) is enabled, and this SecurityManager * If java security debugging ({@code java.security.debug}) is enabled, and this SecurityManager
* is installed, it will emit additional debugging information when threadgroup access checks fail. * is installed, it will emit additional debugging information when threadgroup access checks fail.
* *
* @see SecurityManager#checkAccess(Thread) * @see SecurityManager#checkAccess(Thread)
* @see SecurityManager#checkAccess(ThreadGroup) * @see SecurityManager#checkAccess(ThreadGroup)
* @see <a href="http://cs.oswego.edu/pipermail/concurrency-interest/2009-August/006508.html"> * @see <a href="http://cs.oswego.edu/pipermail/concurrency-interest/2009-August/006508.html">
@ -105,8 +105,10 @@ public class SecureSM extends SecurityManager {
"com\\.carrotsearch\\.ant\\.tasks\\.junit4\\.slave\\..*", "com\\.carrotsearch\\.ant\\.tasks\\.junit4\\.slave\\..*",
// eclipse test runner // eclipse test runner
"org\\.eclipse.jdt\\.internal\\.junit\\.runner\\..*", "org\\.eclipse.jdt\\.internal\\.junit\\.runner\\..*",
// intellij test runner // intellij test runner (before IDEA version 2019.3)
"com\\.intellij\\.rt\\.execution\\.junit\\..*" "com\\.intellij\\.rt\\.execution\\.junit\\..*",
// intellij test runner (since IDEA version 2019.3)
"com\\.intellij\\.rt\\.junit\\..*"
}; };
// java.security.debug support // java.security.debug support
@ -122,7 +124,7 @@ public class SecureSM extends SecurityManager {
} }
} }
}); });
@Override @Override
@SuppressForbidden(reason = "java.security.debug messages go to standard error") @SuppressForbidden(reason = "java.security.debug messages go to standard error")
public void checkAccess(Thread t) { public void checkAccess(Thread t) {
@ -137,7 +139,7 @@ public class SecureSM extends SecurityManager {
throw e; throw e;
} }
} }
@Override @Override
@SuppressForbidden(reason = "java.security.debug messages go to standard error") @SuppressForbidden(reason = "java.security.debug messages go to standard error")
public void checkAccess(ThreadGroup g) { public void checkAccess(ThreadGroup g) {
@ -157,7 +159,7 @@ public class SecureSM extends SecurityManager {
System.err.println("access: caller group=" + caller); System.err.println("access: caller group=" + caller);
System.err.println("access: target group=" + target); System.err.println("access: target group=" + target);
} }
// thread permission logic // thread permission logic
private static final Permission MODIFY_THREAD_PERMISSION = new RuntimePermission("modifyThread"); private static final Permission MODIFY_THREAD_PERMISSION = new RuntimePermission("modifyThread");
@ -168,31 +170,31 @@ public class SecureSM extends SecurityManager {
// first, check if we can modify threads at all. // first, check if we can modify threads at all.
checkPermission(MODIFY_THREAD_PERMISSION); checkPermission(MODIFY_THREAD_PERMISSION);
// check the threadgroup, if its our thread group or an ancestor, its fine. // check the threadgroup, if its our thread group or an ancestor, its fine.
final ThreadGroup source = Thread.currentThread().getThreadGroup(); final ThreadGroup source = Thread.currentThread().getThreadGroup();
final ThreadGroup target = t.getThreadGroup(); final ThreadGroup target = t.getThreadGroup();
if (target == null) { if (target == null) {
return; // its a dead thread, do nothing. return; // its a dead thread, do nothing.
} else if (source.parentOf(target) == false) { } else if (source.parentOf(target) == false) {
checkPermission(MODIFY_ARBITRARY_THREAD_PERMISSION); checkPermission(MODIFY_ARBITRARY_THREAD_PERMISSION);
} }
} }
private static final Permission MODIFY_THREADGROUP_PERMISSION = new RuntimePermission("modifyThreadGroup"); private static final Permission MODIFY_THREADGROUP_PERMISSION = new RuntimePermission("modifyThreadGroup");
private static final Permission MODIFY_ARBITRARY_THREADGROUP_PERMISSION = new ThreadPermission("modifyArbitraryThreadGroup"); private static final Permission MODIFY_ARBITRARY_THREADGROUP_PERMISSION = new ThreadPermission("modifyArbitraryThreadGroup");
protected void checkThreadGroupAccess(ThreadGroup g) { protected void checkThreadGroupAccess(ThreadGroup g) {
Objects.requireNonNull(g); Objects.requireNonNull(g);
// first, check if we can modify thread groups at all. // first, check if we can modify thread groups at all.
checkPermission(MODIFY_THREADGROUP_PERMISSION); checkPermission(MODIFY_THREADGROUP_PERMISSION);
// check the threadgroup, if its our thread group or an ancestor, its fine. // check the threadgroup, if its our thread group or an ancestor, its fine.
final ThreadGroup source = Thread.currentThread().getThreadGroup(); final ThreadGroup source = Thread.currentThread().getThreadGroup();
final ThreadGroup target = g; final ThreadGroup target = g;
if (source == null) { if (source == null) {
return; // we are a dead thread, do nothing return; // we are a dead thread, do nothing
} else if (source.parentOf(target) == false) { } else if (source.parentOf(target) == false) {
@ -205,7 +207,7 @@ public class SecureSM extends SecurityManager {
public void checkExit(int status) { public void checkExit(int status) {
innerCheckExit(status); innerCheckExit(status);
} }
/** /**
* The "Uwe Schindler" algorithm. * The "Uwe Schindler" algorithm.
* *
@ -227,7 +229,7 @@ public class SecureSM extends SecurityManager {
exitMethodHit = className + '#' + methodName + '(' + status + ')'; exitMethodHit = className + '#' + methodName + '(' + status + ')';
continue; continue;
} }
if (exitMethodHit != null) { if (exitMethodHit != null) {
if (classesThatCanExit == null) { if (classesThatCanExit == null) {
break; break;
@ -240,7 +242,7 @@ public class SecureSM extends SecurityManager {
break; break;
} }
} }
if (exitMethodHit == null) { if (exitMethodHit == null) {
// should never happen, only if JVM hides stack trace - replace by generic: // should never happen, only if JVM hides stack trace - replace by generic:
exitMethodHit = "JVM exit method"; exitMethodHit = "JVM exit method";
@ -248,7 +250,7 @@ public class SecureSM extends SecurityManager {
throw new SecurityException(exitMethodHit + " calls are not allowed"); throw new SecurityException(exitMethodHit + " calls are not allowed");
} }
}); });
// we passed the stack check, delegate to super, so default policy can still deny permission: // we passed the stack check, delegate to super, so default policy can still deny permission:
super.checkExit(status); super.checkExit(status);
} }