Skip to content

Commit 5924bb9

Browse files
authored
Allow whitelist of packages that can exit
Today, SecureSM has a mechanism that enables a hardcoded list of test packages to exit if the SecureSM instance is constructed with a boolean flag indicating that these packages will be permitted to exit. This commit replaces this mechanism by allowing the SecureSM instance to be constructed with a whitelist of packages that can exit. Relates elastic#4
1 parent 06817ea commit 5924bb9

File tree

2 files changed

+54
-38
lines changed

2 files changed

+54
-38
lines changed

main/java/org/elasticsearch/SecureSM.java

+50-33
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@
3939
* a thread must have {@code modifyThread} to even terminate its own pool, leaving
4040
* system threads unprotected.
4141
* </ul>
42-
* This class throws exception on {@code exitVM} calls, and provides a testing mode
43-
* where calls from test runners are allowed.
42+
* This class throws exception on {@code exitVM} calls, and provides a whitelist where calls
43+
* from exit are allowed.
4444
* <p>
4545
* Additionally it enforces threadgroup security with the following rules:
4646
* <ul>
@@ -62,23 +62,51 @@
6262
* http://cs.oswego.edu/pipermail/concurrency-interest/2009-August/006508.html</a>
6363
*/
6464
public class SecureSM extends SecurityManager {
65-
private final boolean allowTestExit;
66-
65+
66+
private final String[] packagesThatCanExit;
67+
6768
/**
68-
* Create a new SecurityManager.
69+
* Creates a new security manager where no packages can exit nor halt the virtual machine.
6970
*/
7071
public SecureSM() {
71-
this(false);
72+
this(new String[0]);
7273
}
73-
74-
/**
75-
* Expert: for testing only.
76-
* @param allowTestExit {@code true} if test-runners should be allowed to exit the VM.
74+
75+
/**
76+
* Creates a new security manager with the specified list of packages being the only packages
77+
* that can exit or halt the virtual machine.
78+
*
79+
* @param packagesThatCanExit the list of packages that can exit or halt the virtual machine
7780
*/
78-
public SecureSM(boolean allowTestExit) {
79-
this.allowTestExit = allowTestExit;
81+
public SecureSM(final String[] packagesThatCanExit) {
82+
this.packagesThatCanExit = packagesThatCanExit;
8083
}
81-
84+
85+
/**
86+
* Creates a new security manager with a standard set of test packages being the only packages
87+
* that can exit or halt the virtual machine. The packages that can exit are
88+
* <li><code>org.apache.maven.surefire.booter.</code></li>
89+
* <li><code>com.carrotsearch.ant.tasks.junit4.</code></li>
90+
* <li><code>org.eclipse.internal.junit.runner.</code></li>
91+
* <li><code>com.intellij.rt.execution.junit.</code></li>
92+
*
93+
* @return an instance of SecureSM where test packages can halt or exit the virtual machine
94+
*/
95+
public static SecureSM createTestSecureSM() {
96+
return new SecureSM(TEST_RUNNER_PACKAGES);
97+
}
98+
99+
private static final String[] TEST_RUNNER_PACKAGES = new String[] {
100+
// surefire test runner
101+
"org.apache.maven.surefire.booter.",
102+
// junit4 test runner
103+
"com.carrotsearch.ant.tasks.junit4.",
104+
// eclipse test runner
105+
"org.eclipse.jdt.internal.junit.runner.",
106+
// intellij test runner
107+
"com.intellij.rt.execution.junit."
108+
};
109+
82110
// java.security.debug support
83111
private static final boolean DEBUG = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
84112
@Override
@@ -170,28 +198,13 @@ protected void checkThreadGroupAccess(ThreadGroup g) {
170198
// exit permission logic
171199
@Override
172200
public void checkExit(int status) {
173-
if (allowTestExit) {
174-
checkTestExit(status);
175-
} else {
176-
throw new SecurityException("exit(" + status + ") not allowed by system policy");
177-
}
201+
innerCheckExit(status);
178202
}
179-
180-
static final String TEST_RUNNER_PACKAGES[] = {
181-
// surefire test runner
182-
"org.apache.maven.surefire.booter.",
183-
// junit4 test runner
184-
"com.carrotsearch.ant.tasks.junit4.",
185-
// eclipse test runner
186-
"org.eclipse.jdt.internal.junit.runner.",
187-
// intellij test runner
188-
"com.intellij.rt.execution.junit."
189-
};
190203

191204
/**
192205
* The "Uwe Schindler" algorithm.
193206
*/
194-
protected void checkTestExit(final int status) {
207+
protected void innerCheckExit(final int status) {
195208
AccessController.doPrivileged(new PrivilegedAction<Void>() {
196209
@Override
197210
public Void run() {
@@ -209,8 +222,11 @@ public Void run() {
209222
}
210223

211224
if (exitMethodHit != null) {
212-
for (String testPackage : TEST_RUNNER_PACKAGES) {
213-
if (className.startsWith(testPackage)) {
225+
if (packagesThatCanExit == null) {
226+
break;
227+
}
228+
for (String packageThatCanExit : packagesThatCanExit) {
229+
if (className.startsWith(packageThatCanExit)) {
214230
// this exit point is allowed, we return normally from closure:
215231
return null;
216232
}
@@ -224,11 +240,12 @@ public Void run() {
224240
// should never happen, only if JVM hides stack trace - replace by generic:
225241
exitMethodHit = "JVM exit method";
226242
}
227-
throw new SecurityException(exitMethodHit + " calls are not allowed because they terminate the test runner's JVM.");
243+
throw new SecurityException(exitMethodHit + " calls are not allowed");
228244
}
229245
});
230246

231247
// we passed the stack check, delegate to super, so default policy can still deny permission:
232248
super.checkExit(status);
233249
}
250+
234251
}

test/java/org/elasticsearch/TestSecureSM.java

+4-5
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,14 @@
1919
* under the License.
2020
*/
2121

22+
import junit.framework.TestCase;
23+
import org.junit.Test;
24+
2225
import java.security.Permission;
2326
import java.security.Policy;
2427
import java.security.ProtectionDomain;
2528
import java.util.concurrent.atomic.AtomicBoolean;
2629

27-
import org.junit.Test;
28-
29-
import junit.framework.TestCase;
30-
3130
/** Simple tests for SecureSM */
3231
public class TestSecureSM extends TestCase {
3332
static {
@@ -46,7 +45,7 @@ public boolean implies(ProtectionDomain domain, Permission permission) {
4645
return true;
4746
}
4847
});
49-
System.setSecurityManager(new SecureSM(true));
48+
System.setSecurityManager(SecureSM.createTestSecureSM());
5049
}
5150

5251
@Test

0 commit comments

Comments
 (0)