Skip to content

Commit f0282d7

Browse files
author
Roger Riggs
committed
8279488: ProcessBuilder inherits contextClassLoader when spawning a process reaper thread
Reviewed-by: alanb
1 parent a577656 commit f0282d7

File tree

3 files changed

+103
-18
lines changed

3 files changed

+103
-18
lines changed

src/java.base/share/classes/java/lang/ProcessHandleImpl.java

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -24,6 +24,8 @@
2424
*/
2525
package java.lang;
2626

27+
import jdk.internal.misc.InnocuousThread;
28+
2729
import java.lang.annotation.Native;
2830
import java.security.AccessController;
2931
import java.security.PrivilegedAction;
@@ -89,22 +91,16 @@ final class ProcessHandleImpl implements ProcessHandle {
8991
// of the processReaper threads.
9092
ThreadLocalRandom.current();
9193

92-
ThreadGroup tg = Thread.currentThread().getThreadGroup();
93-
while (tg.getParent() != null) tg = tg.getParent();
94-
ThreadGroup systemThreadGroup = tg;
95-
9694
// For a debug build, the stack shadow zone is larger;
9795
// Increase the total stack size to avoid potential stack overflow.
9896
int debugDelta = "release".equals(System.getProperty("jdk.debug")) ? 0 : (4*4096);
9997
final long stackSize = Boolean.getBoolean("jdk.lang.processReaperUseDefaultStackSize")
10098
? 0 : REAPER_DEFAULT_STACKSIZE + debugDelta;
10199

102100
ThreadFactory threadFactory = grimReaper -> {
103-
Thread t = new Thread(systemThreadGroup, grimReaper,
104-
"process reaper", stackSize, false);
101+
Thread t = InnocuousThread.newSystemThread("process reaper", grimReaper,
102+
stackSize, Thread.MAX_PRIORITY);
105103
t.setDaemon(true);
106-
// A small attempt (probably futile) to avoid priority inversion
107-
t.setPriority(Thread.MAX_PRIORITY);
108104
return t;
109105
};
110106

src/java.base/share/classes/jdk/internal/misc/InnocuousThread.java

+33-9
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -72,13 +72,15 @@ public static Thread newThread(String name, Runnable target) {
7272
*/
7373
public static Thread newThread(String name, Runnable target, int priority) {
7474
if (System.getSecurityManager() == null) {
75-
return createThread(name, target, ClassLoader.getSystemClassLoader(), priority);
75+
return createThread(name, target, 0L,
76+
ClassLoader.getSystemClassLoader(), priority);
7677
}
7778
return AccessController.doPrivileged(
7879
new PrivilegedAction<Thread>() {
7980
@Override
8081
public Thread run() {
81-
return createThread(name, target, ClassLoader.getSystemClassLoader(), priority);
82+
return createThread(name, target, 0L,
83+
ClassLoader.getSystemClassLoader(), priority);
8284
}
8385
});
8486
}
@@ -104,28 +106,50 @@ public static Thread newSystemThread(String name, Runnable target) {
104106
*/
105107
public static Thread newSystemThread(String name, Runnable target, int priority) {
106108
if (System.getSecurityManager() == null) {
107-
return createThread(name, target, null, priority);
109+
return createThread(name, target, 0L, null, priority);
108110
}
109111
return AccessController.doPrivileged(
110112
new PrivilegedAction<Thread>() {
111113
@Override
112114
public Thread run() {
113-
return createThread(name, target, null, priority);
115+
return createThread(name, target, 0L,
116+
null, priority);
114117
}
115118
});
116119
}
117120

118-
private static Thread createThread(String name, Runnable target, ClassLoader loader, int priority) {
121+
/**
122+
* Returns a new InnocuousThread with null context class loader.
123+
* Thread priority is set to the given priority.
124+
*/
125+
public static Thread newSystemThread(String name, Runnable target,
126+
long stackSize, int priority) {
127+
if (System.getSecurityManager() == null) {
128+
return createThread(name, target, stackSize, null, priority);
129+
}
130+
return AccessController.doPrivileged(
131+
new PrivilegedAction<Thread>() {
132+
@Override
133+
public Thread run() {
134+
return createThread(name, target, 0L,
135+
null, priority);
136+
}
137+
});
138+
}
139+
140+
private static Thread createThread(String name, Runnable target, long stackSize,
141+
ClassLoader loader, int priority) {
119142
Thread t = new InnocuousThread(INNOCUOUSTHREADGROUP,
120-
target, name, loader);
143+
target, name, stackSize, loader);
121144
if (priority >= 0) {
122145
t.setPriority(priority);
123146
}
124147
return t;
125148
}
126149

127-
private InnocuousThread(ThreadGroup group, Runnable target, String name, ClassLoader tccl) {
128-
super(group, target, name, 0L, false);
150+
private InnocuousThread(ThreadGroup group, Runnable target, String name,
151+
long stackSize, ClassLoader tccl) {
152+
super(group, target, name, stackSize, false);
129153
UNSAFE.putReferenceRelease(this, INHERITEDACCESSCONTROLCONTEXT, ACC);
130154
UNSAFE.putReferenceRelease(this, CONTEXTCLASSLOADER, tccl);
131155
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/*
25+
* @test
26+
* @bug 8269488
27+
* @summary verify that Process Reaper threads have a null CCL
28+
* @run testng ProcessReaperCCL
29+
*/
30+
31+
import java.io.File;
32+
import java.net.URL;
33+
import java.net.URLClassLoader;
34+
import java.util.List;
35+
import java.util.concurrent.CompletableFuture;
36+
37+
import org.testng.Assert;
38+
import org.testng.annotations.Test;
39+
40+
41+
public class ProcessReaperCCL {
42+
43+
@Test
44+
static void test() throws Exception {
45+
// create a class loader
46+
File dir = new File(".");
47+
URL[] urls = new URL[] {dir.toURI().toURL()};
48+
ClassLoader cl = new URLClassLoader(urls);
49+
Thread.currentThread().setContextClassLoader(cl);
50+
51+
// Invoke a subprocess with processBuilder
52+
ProcessBuilder pb = new ProcessBuilder(List.of("echo", "abc", "xyz"));
53+
Process p = pb.start();
54+
CompletableFuture<Process> cf = p.onExit();
55+
int exitValue = cf.get().exitValue();
56+
Assert.assertEquals(exitValue, 0, "error exit value");
57+
58+
// Verify all "Process Reaper" threads have a null CCL
59+
for (Thread th : Thread.getAllStackTraces().keySet()) {
60+
if ("process reaper".equals(th.getName())) {
61+
Assert.assertEquals(th.getContextClassLoader(), null, "CCL not null");
62+
}
63+
}
64+
}
65+
}

0 commit comments

Comments
 (0)