mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8373867: Improve robustness of Attach API for finding tmp directory
Reviewed-by: sspitsyn, amenkov
This commit is contained in:
parent
0dd5b59194
commit
436c62afd2
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -41,6 +41,8 @@ import java.util.regex.Pattern;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import sun.jvmstat.monitor.MonitoredHost;
|
||||
|
||||
/*
|
||||
* Linux implementation of HotSpotVirtualMachine
|
||||
*/
|
||||
@ -228,7 +230,7 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine {
|
||||
|
||||
// Return the socket file for the given process.
|
||||
private File findSocketFile(long pid, long ns_pid) throws AttachNotSupportedException, IOException {
|
||||
return new File(findTargetProcessTmpDirectory(pid, ns_pid), ".java_pid" + ns_pid);
|
||||
return new File(findTargetProcessTmpDirectory(pid), ".java_pid" + ns_pid);
|
||||
}
|
||||
|
||||
// On Linux a simple handshake is used to start the attach mechanism
|
||||
@ -243,14 +245,14 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine {
|
||||
// Do not canonicalize the file path, or we will fail to attach to a VM in a container.
|
||||
f.createNewFile();
|
||||
} catch (IOException _) {
|
||||
f = new File(findTargetProcessTmpDirectory(pid, ns_pid), fn.toString());
|
||||
f = new File(findTargetProcessTmpDirectory(pid), fn.toString());
|
||||
f.createNewFile();
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
private String findTargetProcessTmpDirectory(long pid, long ns_pid) throws AttachNotSupportedException, IOException {
|
||||
final var procPidRoot = PROC.resolve(Long.toString(pid)).resolve(ROOT_TMP);
|
||||
private String findTargetProcessTmpDirectory(long pid) throws AttachNotSupportedException {
|
||||
final var tmpOnProcPidRoot = PROC.resolve(Long.toString(pid)).resolve(ROOT_TMP);
|
||||
|
||||
/* We need to handle at least 4 different cases:
|
||||
* 1. Caller and target processes share PID namespace and root filesystem (host to host or container to
|
||||
@ -261,21 +263,44 @@ public class VirtualMachineImpl extends HotSpotVirtualMachine {
|
||||
* 4. Caller and target processes share neither PID namespace nor root filesystem (host to container)
|
||||
*
|
||||
* if target is elevated, we cant use /proc/<pid>/... so we have to fallback to /tmp, but that may not be shared
|
||||
* with the target/attachee process, we can try, except in the case where the ns_pid also exists in this pid ns
|
||||
* which is ambiguous, if we share /tmp with the intended target, the attach will succeed, if we do not,
|
||||
* then we will potentially attempt to attach to some arbitrary process with the same pid (in this pid ns)
|
||||
* as that of the intended target (in its * pid ns).
|
||||
* with the target/attachee process, so we should check whether /tmp on both is same. This method would throw
|
||||
* AttachNotSupportedException if they are different because we cannot make a connection with target VM.
|
||||
*
|
||||
* so in that case we should prehaps throw - or risk sending SIGQUIT to some arbitrary process... which could kill it
|
||||
*
|
||||
* however we can also check the target pid's signal masks to see if it catches SIGQUIT and only do so if in
|
||||
* In addition, we can also check the target pid's signal masks to see if it catches SIGQUIT and only do so if in
|
||||
* fact it does ... this reduces the risk of killing an innocent process in the current ns as opposed to
|
||||
* attaching to the actual target JVM ... c.f: checkCatchesAndSendQuitTo() below.
|
||||
*
|
||||
* note that if pid == ns_pid we are in a shared pid ns with the target and may (potentially) share /tmp
|
||||
*/
|
||||
|
||||
return (Files.isWritable(procPidRoot) ? procPidRoot : TMPDIR).toString();
|
||||
try {
|
||||
if (Files.isWritable(tmpOnProcPidRoot)) {
|
||||
return tmpOnProcPidRoot.toString();
|
||||
} else if (Files.isSameFile(tmpOnProcPidRoot, TMPDIR)) {
|
||||
return TMPDIR.toString();
|
||||
} else {
|
||||
throw new AttachNotSupportedException("Unable to access the filesystem of the target process");
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
try {
|
||||
boolean found = MonitoredHost.getMonitoredHost("//localhost")
|
||||
.activeVms()
|
||||
.stream()
|
||||
.anyMatch(i -> pid == i.intValue());
|
||||
if (found) {
|
||||
// We can use /tmp because target process is on same host
|
||||
// even if we cannot access /proc/<PID>/root.
|
||||
// The process with capsh/setcap would fall this pattern.
|
||||
return TMPDIR.toString();
|
||||
} else {
|
||||
throw new AttachNotSupportedException("Unable to access the filesystem of the target process", ioe);
|
||||
}
|
||||
} catch (AttachNotSupportedException e) {
|
||||
// AttachNotSupportedException happened in above should go through
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
// Other exceptions would be wrapped with AttachNotSupportedException
|
||||
throw new AttachNotSupportedException("Unable to access the filesystem of the target process", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the inner most namespaced PID if there is one,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2005, 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -62,4 +62,15 @@ public class AttachNotSupportedException extends Exception {
|
||||
super(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an <code>AttachNotSupportedException</code> with
|
||||
* the specified cause.
|
||||
*
|
||||
* @param message the detail message.
|
||||
* @param cause the cause of this exception.
|
||||
*/
|
||||
public AttachNotSupportedException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
102
test/jdk/com/sun/tools/attach/TestWithoutDumpableProcess.java
Normal file
102
test/jdk/com/sun/tools/attach/TestWithoutDumpableProcess.java
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2026, NTT DATA
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.Linker;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
|
||||
import com.sun.tools.attach.VirtualMachine;
|
||||
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.thread.ProcessThread;
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8226919 8373867
|
||||
* @summary Test to make sure attach target process which is not dumpable.
|
||||
* @library /test/lib
|
||||
* @modules jdk.attach
|
||||
* @requires os.family == "linux"
|
||||
*
|
||||
* @run main/timeout=200 TestWithoutDumpableProcess
|
||||
*/
|
||||
public class TestWithoutDumpableProcess {
|
||||
|
||||
private static final String EXPECTED_PROP_KEY = "attach.test";
|
||||
private static final String EXPECTED_PROP_VALUE = "true";
|
||||
|
||||
public static class Debuggee {
|
||||
|
||||
// Disable dumpable attribute via prctl(2)
|
||||
private static void disableDumpable() throws Throwable {
|
||||
var linker = Linker.nativeLinker();
|
||||
var prctl = linker.downcallHandle(linker.defaultLookup().findOrThrow("prctl"),
|
||||
FunctionDescriptor.of(ValueLayout.JAVA_INT, ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG),
|
||||
Linker.Option.firstVariadicArg(1), Linker.Option.captureCallState("errno"));
|
||||
var errnoSeg = Arena.global().allocate(Linker.Option.captureStateLayout());
|
||||
final int PR_SET_DUMPABLE = 4; // from linux/prctl.h
|
||||
|
||||
int ret = (int)prctl.invoke(errnoSeg, PR_SET_DUMPABLE, 0L);
|
||||
if (ret == -1){
|
||||
var hndErrno = Linker.Option
|
||||
.captureStateLayout()
|
||||
.varHandle(MemoryLayout.PathElement.groupElement("errno"));
|
||||
int errno = (int)hndErrno.get(errnoSeg, 0L);
|
||||
throw new RuntimeException("prctl: errno=" + errno);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
disableDumpable();
|
||||
IO.println(Application.READY_MSG);
|
||||
|
||||
while (IO.readln().equals(Application.SHUTDOWN_MSG));
|
||||
}
|
||||
|
||||
public static ProcessThread start() {
|
||||
var args = new String[]{
|
||||
"--enable-native-access=ALL-UNNAMED",
|
||||
String.format("-D%s=%s", EXPECTED_PROP_KEY, EXPECTED_PROP_VALUE), Debuggee.class.getName()
|
||||
};
|
||||
var pb = ProcessTools.createLimitedTestJavaProcessBuilder(args);
|
||||
var pt = new ProcessThread("runApplication", Application.READY_MSG::equals, pb);
|
||||
pt.start();
|
||||
return pt;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
var pt = Debuggee.start();
|
||||
var vm = VirtualMachine.attach(Long.toString(pt.getPid()));
|
||||
var val = vm.getSystemProperties().getProperty(EXPECTED_PROP_KEY);
|
||||
|
||||
Asserts.assertNotNull(val, "Expected sysprop not found");
|
||||
Asserts.assertEquals(val, "true", "Unexpected sysprop value");
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user