# HG changeset patch # User mgronlun # Date 1562548071 -7200 # Mon Jul 08 03:07:51 2019 +0200 # Node ID 361873b7b716ddd475341f4c968bf408cfe9b57f # Parent 78a2b1bb15cf3d6a431f45c073794d1c93f254d5 [mq]: windows_process_abort diff --git a/src/hotspot/share/utilities/vmError.cpp b/src/hotspot/share/utilities/vmError.cpp --- a/src/hotspot/share/utilities/vmError.cpp +++ b/src/hotspot/share/utilities/vmError.cpp @@ -1837,3 +1837,11 @@ ShouldNotReachHere(); } #endif // !PRODUCT + +extern "C" { +JNIEXPORT void JNICALL + JVM_Abort(const char* message) { + VMError::report_and_die(message); + } +} // extern + diff --git a/src/jdk.attach/aix/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/aix/native/libattach/VirtualMachineImpl.c --- a/src/jdk.attach/aix/native/libattach/VirtualMachineImpl.c +++ b/src/jdk.attach/aix/native/libattach/VirtualMachineImpl.c @@ -260,3 +260,8 @@ } while (remaining > 0); } + +JNIEXPORT void JNICALL +Java_jdk_test_lib_process_ProcessAbort_deliverAbort(JNIEnv* env, jclass cls, jlong pid) { + // No impl +} diff --git a/src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c --- a/src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c +++ b/src/jdk.attach/linux/native/libattach/VirtualMachineImpl.c @@ -263,3 +263,8 @@ } while (remaining > 0); } + +JNIEXPORT void JNICALL +Java_jdk_test_lib_process_ProcessAbort_deliverAbort(JNIEnv* env, jclass cls, jlong pid) { + // No impl +} diff --git a/src/jdk.attach/macosx/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/macosx/native/libattach/VirtualMachineImpl.c --- a/src/jdk.attach/macosx/native/libattach/VirtualMachineImpl.c +++ b/src/jdk.attach/macosx/native/libattach/VirtualMachineImpl.c @@ -329,3 +329,8 @@ return (*env)->NewStringUTF(env, "/tmp"); #endif /* __APPLE__ */ } + +JNIEXPORT void JNICALL +Java_jdk_test_lib_process_ProcessAbort_deliverAbort(JNIEnv* env, jclass cls, jlong pid) { + // No impl +} diff --git a/src/jdk.attach/solaris/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/solaris/native/libattach/VirtualMachineImpl.c --- a/src/jdk.attach/solaris/native/libattach/VirtualMachineImpl.c +++ b/src/jdk.attach/solaris/native/libattach/VirtualMachineImpl.c @@ -387,3 +387,8 @@ free(buf); return result; } + +JNIEXPORT void JNICALL +Java_jdk_test_lib_process_ProcessAbort_deliverAbort(JNIEnv* env, jclass cls, jlong pid) { + // No impl +} diff --git a/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c b/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c --- a/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c +++ b/src/jdk.attach/windows/native/libattach/VirtualMachineImpl.c @@ -26,8 +26,10 @@ #include "jni_util.h" #include +#include #include #include +#include #include "sun_tools_attach_VirtualMachineImpl.h" @@ -50,6 +52,9 @@ typedef jint (WINAPI* EnqueueOperationFunc) (const char* cmd, const char* arg1, const char* arg2, const char* arg3, const char* pipename); +/* exported function in target VM */ +typedef void (WINAPI* AbortFunc)(const char* message); + /* OpenProcess with SE_DEBUG_NAME privilege */ static HANDLE doPrivilegedOpenProcess(DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId); @@ -80,6 +85,15 @@ char pipename[MAX_PIPE_NAME_LENGTH]; } DataBlock; +typedef struct { + GetModuleHandleFunc _GetModuleHandle; + GetProcAddressFunc _GetProcAddress; + char jvmLib[MAX_LIBNAME_LENGTH]; /* "jvm.dll" */ + char func1[MAX_FUNC_LENGTH]; + char func2[MAX_FUNC_LENGTH]; + char msg[MAX_ARG_LENGTH]; /* "User induced dump, ... */ +} AbortDataBlock; + /* * Return codes from enqueue function executed in target VM */ @@ -632,3 +646,190 @@ } } } + +static void initialize() { + _GetModuleHandle = (GetModuleHandleFunc)GetModuleHandle; + _GetProcAddress = (GetProcAddressFunc)GetProcAddress; +} + +/* + * Code copied to target process + */ +#pragma check_stack (off) + /* Switch off all runtime checks (checks caused by /RTC). They cause the + * generated code to contain relative jumps to check functions which make + * the code position dependent. */ +#pragma runtime_checks ("scu", off) +static DWORD WINAPI bindToAbortFunction(AbortDataBlock* pData) { + HINSTANCE h; + AbortFunc addr; + + h = pData->_GetModuleHandle(pData->jvmLib); + if (h == NULL) { + return 1; + } + + addr = (AbortFunc)(pData->_GetProcAddress(h, pData->func1)); + if (addr == NULL) { + addr = (AbortFunc)(pData->_GetProcAddress(h, pData->func2)); + } + + if (addr == NULL) { + return 1; + } + + (*addr)(pData->msg); + return 0; +} + +/* This function marks the end of jvm_attach_thread_func. */ +static void WINAPI bindToAbortFunctionEnd(void) {} + +#pragma check_stack +#pragma runtime_checks ("scu", restore) + +static void* allocateMemory(HANDLE hProcess, size_t size, BOOL execute) { + return VirtualAllocEx(hProcess, 0, size, MEM_COMMIT, execute ? PAGE_EXECUTE_READ : PAGE_READWRITE); +} + +static void freeMemory(HANDLE hProcess, void* data) { + VirtualFreeEx(hProcess, data, 0, MEM_RELEASE); +} + +static void writeMemory(HANDLE hProcess, const void* where, void* data, size_t size) { + WriteProcessMemory(hProcess, (LPVOID)where, (LPCVOID)data, (SIZE_T)size, NULL); +} + +static HANDLE processHandle(DWORD processID) { + return OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID); +} + +static void close(HANDLE h) { + CloseHandle(h); +} + +static BOOL openSecurityToken(HANDLE* hToken) { + return OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, hToken) + || OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, hToken); +} + +static BOOL setTokenPrivilege(HANDLE hToken, BOOL bEnable, LPCTSTR lpszPrivilege) { + TOKEN_PRIVILEGES tp; + LUID luid; + + if (hToken == NULL) { + return FALSE; + } + + if (!LookupPrivilegeValue( + NULL, // lookup privilege on local system + lpszPrivilege, // privilege to lookup + &luid)) { // receives LUID of privilege + return FALSE; + } + + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = (bEnable) ? SE_PRIVILEGE_ENABLED : 0; + + // Enable the privilege or disable all privileges. + if (!AdjustTokenPrivileges(hToken, + FALSE, + &tp, + sizeof(TOKEN_PRIVILEGES), + (PTOKEN_PRIVILEGES)NULL, + (PDWORD)NULL)) { + return FALSE; + } + + return TRUE; +} + +static BOOL setDebugPrivilege(HANDLE hToken) { + if (!setTokenPrivilege(hToken, TRUE, SE_DEBUG_NAME)) { + _tprintf(TEXT("Could not enable debug privilege\n")); + return FALSE; + } + return TRUE; +} + +static void* setupCode(HANDLE hProcess) { + DWORD size; + size = (DWORD)((LPBYTE)bindToAbortFunctionEnd - (LPBYTE)bindToAbortFunction); + void* const codeMemory = allocateMemory(hProcess, size, TRUE); + writeMemory(hProcess, codeMemory, &bindToAbortFunction, (size_t)size); + return codeMemory; +} + +static void* setupData(HANDLE hProcess) { + AbortDataBlock data; + + data._GetModuleHandle = _GetModuleHandle; + data._GetProcAddress = _GetProcAddress; + strcpy(data.jvmLib, "jvm"); + strcpy(data.func1, "JVM_Abort"); + strcpy(data.func2, "_JVM_Abort@8"); + strcpy(data.msg, "User induced abort"); + + void* const dataMemory = allocateMemory(hProcess, sizeof(AbortDataBlock), FALSE); + writeMemory(hProcess, dataMemory, &data, sizeof(AbortDataBlock)); + return dataMemory; +} + +static HANDLE injectRemoteCrashThread(HANDLE hProcess, void* code, void* data) { + return CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)code, data, 0, NULL); +} + +static int abortProcess(HANDLE hProcess) { + HANDLE hToken = 0; + HANDLE hThread = 0; + + if (hProcess == NULL) { + return 1; + } + + if (!openSecurityToken(&hToken)) { + close(hToken); + return 1; + } + + if (!setDebugPrivilege(hToken)) { + close(hToken); + return 1; + } + + initialize(); + + void* const code = setupCode(hProcess); + if (code == NULL) { + close(hToken); + return 1; + } + + void* const data = setupData(hProcess); + if (data == NULL) { + close(hToken); + return 1; + } + + hThread = injectRemoteCrashThread(hProcess, code, data); + + close(hToken); + + if (!hThread) { + return 1; + } + + close(hThread); + return 0; +} + +static int abortProcessPid(DWORD pid) { + return abortProcess(processHandle(pid)); +} + +JNIEXPORT void JNICALL +Java_jdk_test_lib_process_ProcessAbort_deliverAbort(JNIEnv* env, jclass cls, jlong pid) { + abortProcessPid((DWORD)pid); +} + diff --git a/test/lib/jdk/test/lib/process/OutputBuffer.java b/test/lib/jdk/test/lib/process/OutputBuffer.java --- a/test/lib/jdk/test/lib/process/OutputBuffer.java +++ b/test/lib/jdk/test/lib/process/OutputBuffer.java @@ -121,9 +121,22 @@ logProgress("Waiting for completion"); boolean aborted = true; try { - int result = p.waitFor(); - logProgress("Waiting for completion finished"); - aborted = false; + int result = 0; + if (ProcessAbort.TIMEOUT == 0) { + result = p.waitFor(); + logProgress("Waiting for completion finished"); + aborted = false; + } else { + if (!p.waitFor(ProcessAbort.TIMEOUT, ProcessAbort.TIMEOUT_UNIT)) { + if (ProcessAbort.ABORT_ON_TIMEOUT) { + ProcessAbort.abort(p); + result = p.waitFor(); + throw new RuntimeException("Child process aborted after timeout : " + ProcessAbort.TIMEOUT + " " + ProcessAbort.TIMEOUT_UNIT); + } + } else { + result = p.exitValue(); + } + } return result; } finally { if (aborted) { diff --git a/test/lib/jdk/test/lib/process/ProcessAbort.java b/test/lib/jdk/test/lib/process/ProcessAbort.java new file mode 100644 --- /dev/null +++ b/test/lib/jdk/test/lib/process/ProcessAbort.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019, 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 + * 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. + */ + +package jdk.test.lib.process; + +import com.sun.tools.attach.AttachOperationFailedException; +import com.sun.tools.attach.VirtualMachine; +import com.sun.tools.attach.VirtualMachineDescriptor; +import com.sun.tools.attach.AttachNotSupportedException; +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import sun.tools.attach.HotSpotVirtualMachine; + +public class ProcessAbort { + public static final long TIMEOUT = getTimeoutProperty(); + public static final TimeUnit TIMEOUT_UNIT = getTimeoutUnitProperty(); + public static final boolean ABORT_ON_TIMEOUT = getAbortOnTimeoutProperty(); + + static { + System.out.println("ProcessTools: TimeOut : " + TIMEOUT); + System.out.println("ProcessTools: TimeOutUnit : " + TIMEOUT_UNIT); + System.out.println("ProcessTools: AbortOnTimeOut : " + ABORT_ON_TIMEOUT); + System.loadLibrary("attach"); + } + + private static long getTimeoutProperty() { + final String timeoutProperty = System.getProperty("test.processtools.timeout"); + return timeoutProperty != null ? Long.parseLong(timeoutProperty) : 0; + } + + private static TimeUnit getTimeoutUnitProperty() { + final String timeoutUnitProperty = System.getProperty("test.processtools.timeoutUnit"); + if (timeoutUnitProperty == null) { + return TimeUnit.MILLISECONDS; + } + switch (timeoutUnitProperty) { + case "ns" : return TimeUnit.NANOSECONDS; + case "us" : return TimeUnit.MICROSECONDS; + case "ms" : return TimeUnit.MILLISECONDS; + case "s" : return TimeUnit.SECONDS; + case "m" : return TimeUnit.MINUTES; + case "h" : return TimeUnit.HOURS; + case "d" : return TimeUnit.DAYS; + default: return TimeUnit.MILLISECONDS; + } + } + + private static boolean getAbortOnTimeoutProperty() { + final String abortOnTimeoutProperty = System.getProperty("test.processtools.abortOnTimeout"); + return abortOnTimeoutProperty != null ? Boolean.parseBoolean(abortOnTimeoutProperty) : false; + } + + public static void abort(Process p) { + System.out.println("Aborting..."); + final long pid = p.pid(); + Thread t = new Thread(() -> { + deliverAbort(pid); + }); + t.setDaemon(true); + t.start(); + } + + private static native void deliverAbort(long pid); +}