mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2024-09-13 09:17:20 -07:00
Added patch to implement NtSuspendProcess and NtResumeProcess.
This commit is contained in:
parent
2dd39323aa
commit
b9a9f51c59
@ -0,0 +1,363 @@
|
||||
From cf25edc841ba3490113f23f8f5b64ba099f84d8e Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
|
||||
Date: Tue, 21 Mar 2017 23:12:09 +0100
|
||||
Subject: ntdll: Implement NtSuspendProcess and NtResumeProcess.
|
||||
|
||||
---
|
||||
dlls/ntdll/process.c | 28 +++++-
|
||||
dlls/ntdll/tests/Makefile.in | 1 +
|
||||
dlls/ntdll/tests/process.c | 207 +++++++++++++++++++++++++++++++++++++++++++
|
||||
server/protocol.def | 12 +++
|
||||
server/thread.c | 49 ++++++++++
|
||||
5 files changed, 293 insertions(+), 4 deletions(-)
|
||||
create mode 100644 dlls/ntdll/tests/process.c
|
||||
|
||||
diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c
|
||||
index ffc9bff690..ac1dc0a889 100644
|
||||
--- a/dlls/ntdll/process.c
|
||||
+++ b/dlls/ntdll/process.c
|
||||
@@ -737,8 +737,18 @@ NTSTATUS WINAPI NtOpenProcess(PHANDLE handle, ACCESS_MASK access,
|
||||
*/
|
||||
NTSTATUS WINAPI NtResumeProcess( HANDLE handle )
|
||||
{
|
||||
- FIXME("stub: %p\n", handle);
|
||||
- return STATUS_NOT_IMPLEMENTED;
|
||||
+ NTSTATUS status;
|
||||
+
|
||||
+ TRACE("(%p)\n", handle);
|
||||
+
|
||||
+ SERVER_START_REQ( resume_process )
|
||||
+ {
|
||||
+ req->handle = wine_server_obj_handle( handle );
|
||||
+ status = wine_server_call( req );
|
||||
+ }
|
||||
+ SERVER_END_REQ;
|
||||
+
|
||||
+ return status;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
@@ -747,6 +757,16 @@ NTSTATUS WINAPI NtResumeProcess( HANDLE handle )
|
||||
*/
|
||||
NTSTATUS WINAPI NtSuspendProcess( HANDLE handle )
|
||||
{
|
||||
- FIXME("stub: %p\n", handle);
|
||||
- return STATUS_NOT_IMPLEMENTED;
|
||||
+ NTSTATUS status;
|
||||
+
|
||||
+ TRACE("(%p)\n", handle);
|
||||
+
|
||||
+ SERVER_START_REQ( suspend_process )
|
||||
+ {
|
||||
+ req->handle = wine_server_obj_handle( handle );
|
||||
+ status = wine_server_call( req );
|
||||
+ }
|
||||
+ SERVER_END_REQ;
|
||||
+
|
||||
+ return status;
|
||||
}
|
||||
diff --git a/dlls/ntdll/tests/Makefile.in b/dlls/ntdll/tests/Makefile.in
|
||||
index 0de4fe8f20..c5c519150e 100644
|
||||
--- a/dlls/ntdll/tests/Makefile.in
|
||||
+++ b/dlls/ntdll/tests/Makefile.in
|
||||
@@ -16,6 +16,7 @@ C_SRCS = \
|
||||
path.c \
|
||||
pipe.c \
|
||||
port.c \
|
||||
+ process.c \
|
||||
reg.c \
|
||||
rtl.c \
|
||||
rtlbitmap.c \
|
||||
diff --git a/dlls/ntdll/tests/process.c b/dlls/ntdll/tests/process.c
|
||||
new file mode 100644
|
||||
index 0000000000..41303b7e9a
|
||||
--- /dev/null
|
||||
+++ b/dlls/ntdll/tests/process.c
|
||||
@@ -0,0 +1,207 @@
|
||||
+/*
|
||||
+ * Unit test suite for process functions
|
||||
+ *
|
||||
+ * Copyright 2017 Michael Müller
|
||||
+ *
|
||||
+ * This library is free software; you can redistribute it and/or
|
||||
+ * modify it under the terms of the GNU Lesser General Public
|
||||
+ * License as published by the Free Software Foundation; either
|
||||
+ * version 2.1 of the License, or (at your option) any later version.
|
||||
+ *
|
||||
+ * This library 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
|
||||
+ * Lesser General Public License for more details.
|
||||
+ *
|
||||
+ * You should have received a copy of the GNU Lesser General Public
|
||||
+ * License along with this library; if not, write to the Free Software
|
||||
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
+ */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+
|
||||
+#include "ntdll_test.h"
|
||||
+
|
||||
+#include "windef.h"
|
||||
+#include "winbase.h"
|
||||
+
|
||||
+static NTSTATUS (WINAPI *pNtResumeProcess)(HANDLE);
|
||||
+static NTSTATUS (WINAPI *pNtSuspendProcess)(HANDLE);
|
||||
+static NTSTATUS (WINAPI *pNtSuspendThread)(HANDLE,PULONG);
|
||||
+static NTSTATUS (WINAPI *pNtResumeThread)(HANDLE);
|
||||
+
|
||||
+static void test_NtSuspendProcess(char *process_name)
|
||||
+{
|
||||
+ PROCESS_INFORMATION info;
|
||||
+ DEBUG_EVENT ev;
|
||||
+ STARTUPINFOA startup;
|
||||
+ NTSTATUS status;
|
||||
+ HANDLE event;
|
||||
+ char buffer[MAX_PATH];
|
||||
+ ULONG count;
|
||||
+ DWORD ret;
|
||||
+
|
||||
+ status = pNtResumeProcess(GetCurrentProcess());
|
||||
+ ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
|
||||
+
|
||||
+ event = CreateEventA(NULL, TRUE, FALSE, "wine_suspend_event");
|
||||
+ ok(!!event, "Failed to create event: %u\n", GetLastError());
|
||||
+
|
||||
+ memset(&startup, 0, sizeof(startup));
|
||||
+ startup.cb = sizeof(startup);
|
||||
+
|
||||
+ sprintf(buffer, "%s tests/process.c dummy_process wine_suspend_event", process_name);
|
||||
+ ret = CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info);
|
||||
+ ok(ret, "CreateProcess failed with error %u\n", GetLastError());
|
||||
+
|
||||
+ ret = WaitForSingleObject(event, 500);
|
||||
+ ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret);
|
||||
+
|
||||
+ status = pNtSuspendProcess(info.hProcess);
|
||||
+ ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
|
||||
+
|
||||
+ ResetEvent(event);
|
||||
+
|
||||
+ ret = WaitForSingleObject(event, 200);
|
||||
+ ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
|
||||
+
|
||||
+ status = NtResumeThread(info.hThread, &count);
|
||||
+ ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
|
||||
+ ok(count == 1, "Expected count 1, got %d\n", count);
|
||||
+
|
||||
+ ret = WaitForSingleObject(event, 200);
|
||||
+ ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret);
|
||||
+
|
||||
+ status = pNtResumeProcess(info.hProcess);
|
||||
+ ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
|
||||
+
|
||||
+ status = pNtSuspendThread(info.hThread, &count);
|
||||
+ ok(status == STATUS_SUCCESS, "NtSuspendThread failed: %x\n", status);
|
||||
+ ok(count == 0, "Expected count 0, got %d\n", count);
|
||||
+
|
||||
+ ResetEvent(event);
|
||||
+
|
||||
+ ret = WaitForSingleObject(event, 200);
|
||||
+ ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
|
||||
+
|
||||
+ status = pNtResumeProcess(info.hProcess);
|
||||
+ ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
|
||||
+
|
||||
+ ret = WaitForSingleObject(event, 200);
|
||||
+ ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret);
|
||||
+
|
||||
+ status = pNtSuspendThread(info.hThread, &count);
|
||||
+ ok(status == STATUS_SUCCESS, "NtSuspendThread failed: %x\n", status);
|
||||
+ ok(count == 0, "Expected count 0, got %d\n", count);
|
||||
+
|
||||
+ status = pNtSuspendThread(info.hThread, &count);
|
||||
+ ok(status == STATUS_SUCCESS, "NtSuspendThread failed: %x\n", status);
|
||||
+ ok(count == 1, "Expected count 1, got %d\n", count);
|
||||
+
|
||||
+ ResetEvent(event);
|
||||
+
|
||||
+ ret = WaitForSingleObject(event, 200);
|
||||
+ ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
|
||||
+
|
||||
+ status = pNtResumeProcess(info.hProcess);
|
||||
+ ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
|
||||
+
|
||||
+ ret = WaitForSingleObject(event, 200);
|
||||
+ ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
|
||||
+
|
||||
+ status = pNtResumeProcess(info.hProcess);
|
||||
+ ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
|
||||
+
|
||||
+ ret = WaitForSingleObject(event, 200);
|
||||
+ ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret);
|
||||
+
|
||||
+ ret = DebugActiveProcess(info.dwProcessId);
|
||||
+ ok(ret, "Failed to debug process: %d\n", GetLastError());
|
||||
+
|
||||
+ ResetEvent(event);
|
||||
+
|
||||
+ ret = WaitForSingleObject(event, 200);
|
||||
+ ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
|
||||
+
|
||||
+ for (;;)
|
||||
+ {
|
||||
+ ret = WaitForDebugEvent(&ev, INFINITE);
|
||||
+ ok(ret, "WaitForDebugEvent failed, last error %#x.\n", GetLastError());
|
||||
+ if (!ret) break;
|
||||
+
|
||||
+ if (ev.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) break;
|
||||
+
|
||||
+ ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE);
|
||||
+ ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError());
|
||||
+ if (!ret) break;
|
||||
+ }
|
||||
+
|
||||
+ ResetEvent(event);
|
||||
+
|
||||
+ ret = WaitForSingleObject(event, 200);
|
||||
+ ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
|
||||
+
|
||||
+ status = pNtResumeProcess(info.hProcess);
|
||||
+ ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
|
||||
+
|
||||
+ ret = WaitForSingleObject(event, 200);
|
||||
+ ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
|
||||
+
|
||||
+ status = NtResumeThread(info.hThread, &count);
|
||||
+ ok(status == STATUS_SUCCESS, "NtResumeProcess failed: %x\n", status);
|
||||
+ ok(count == 0, "Expected count 0, got %d\n", count);
|
||||
+
|
||||
+ ret = WaitForSingleObject(event, 200);
|
||||
+ ok(ret == WAIT_TIMEOUT, "Expected timeout, got: %d\n", ret);
|
||||
+
|
||||
+ ret = ContinueDebugEvent(ev.dwProcessId, ev.dwThreadId, DBG_CONTINUE);
|
||||
+ ok(ret, "ContinueDebugEvent failed, last error %#x.\n", GetLastError());
|
||||
+
|
||||
+ ret = WaitForSingleObject(event, 200);
|
||||
+ ok(ret == WAIT_OBJECT_0, "Event was not signaled: %d\n", ret);
|
||||
+
|
||||
+ TerminateProcess(info.hProcess, 0);
|
||||
+
|
||||
+ CloseHandle(info.hProcess);
|
||||
+ CloseHandle(info.hThread);
|
||||
+}
|
||||
+
|
||||
+static void dummy_process(char *event_name)
|
||||
+{
|
||||
+ HANDLE event = OpenEventA(EVENT_ALL_ACCESS, FALSE, event_name);
|
||||
+
|
||||
+ while (TRUE)
|
||||
+ {
|
||||
+ SetEvent(event);
|
||||
+ OutputDebugStringA("test");
|
||||
+ Sleep(5);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+START_TEST(process)
|
||||
+{
|
||||
+ HMODULE mod;
|
||||
+ char **argv;
|
||||
+ int argc;
|
||||
+
|
||||
+ argc = winetest_get_mainargs(&argv);
|
||||
+ if (argc >= 4 && strcmp(argv[2], "dummy_process") == 0)
|
||||
+ {
|
||||
+ dummy_process(argv[3]);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ mod = GetModuleHandleA("ntdll.dll");
|
||||
+ if (!mod)
|
||||
+ {
|
||||
+ win_skip("Not running on NT, skipping tests\n");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ pNtResumeProcess = (void*)GetProcAddress(mod, "NtResumeProcess");
|
||||
+ pNtSuspendProcess = (void*)GetProcAddress(mod, "NtSuspendProcess");
|
||||
+ pNtResumeThread = (void*)GetProcAddress(mod, "NtResumeThread");
|
||||
+ pNtSuspendThread = (void*)GetProcAddress(mod, "NtSuspendThread");
|
||||
+
|
||||
+ test_NtSuspendProcess(argv[0]);
|
||||
+}
|
||||
diff --git a/server/protocol.def b/server/protocol.def
|
||||
index 04377e16c5..f886d958d8 100644
|
||||
--- a/server/protocol.def
|
||||
+++ b/server/protocol.def
|
||||
@@ -3887,3 +3887,15 @@ struct handle_info
|
||||
unsigned int threads; /* number of threads */
|
||||
unsigned int handles; /* number of handles */
|
||||
@END
|
||||
+
|
||||
+
|
||||
+/* Suspend a process */
|
||||
+@REQ(suspend_process)
|
||||
+ obj_handle_t handle; /* process handle */
|
||||
+@END
|
||||
+
|
||||
+
|
||||
+/* Resume a process */
|
||||
+@REQ(resume_process)
|
||||
+ obj_handle_t handle; /* process handle */
|
||||
+@END
|
||||
diff --git a/server/thread.c b/server/thread.c
|
||||
index 108444ebc5..5b9b80d303 100644
|
||||
--- a/server/thread.c
|
||||
+++ b/server/thread.c
|
||||
@@ -1797,3 +1797,52 @@ DECL_HANDLER(get_selector_entry)
|
||||
release_object( thread );
|
||||
}
|
||||
}
|
||||
+
|
||||
+/* Suspend a process */
|
||||
+DECL_HANDLER(suspend_process)
|
||||
+{
|
||||
+ struct process *process;
|
||||
+
|
||||
+ if ((process = get_process_from_handle( req->handle, PROCESS_SUSPEND_RESUME )))
|
||||
+ {
|
||||
+ struct list *ptr, *next;
|
||||
+
|
||||
+ LIST_FOR_EACH( ptr, &process->thread_list )
|
||||
+ {
|
||||
+ struct thread *thread = LIST_ENTRY( ptr, struct thread, proc_entry );
|
||||
+ if (thread->suspend >= MAXIMUM_SUSPEND_COUNT)
|
||||
+ {
|
||||
+ set_error( STATUS_SUSPEND_COUNT_EXCEEDED );
|
||||
+ release_object( process );
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ LIST_FOR_EACH_SAFE( ptr, next, &process->thread_list )
|
||||
+ {
|
||||
+ struct thread *thread = LIST_ENTRY( ptr, struct thread, proc_entry );
|
||||
+ suspend_thread( thread );
|
||||
+ }
|
||||
+
|
||||
+ release_object( process );
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/* Resume a process */
|
||||
+DECL_HANDLER(resume_process)
|
||||
+{
|
||||
+ struct process *process;
|
||||
+
|
||||
+ if ((process = get_process_from_handle( req->handle, PROCESS_SUSPEND_RESUME )))
|
||||
+ {
|
||||
+ struct list *ptr, *next;
|
||||
+
|
||||
+ LIST_FOR_EACH_SAFE( ptr, next, &process->thread_list )
|
||||
+ {
|
||||
+ struct thread *thread = LIST_ENTRY( ptr, struct thread, proc_entry );
|
||||
+ resume_thread( thread );
|
||||
+ }
|
||||
+
|
||||
+ release_object( process );
|
||||
+ }
|
||||
+}
|
||||
--
|
||||
2.11.0
|
||||
|
2
patches/ntdll-NtSuspendProcess/definition
Normal file
2
patches/ntdll-NtSuspendProcess/definition
Normal file
@ -0,0 +1,2 @@
|
||||
Fixes: Implement NtSuspendProcess and NtResumeProcess
|
||||
Depends: kernel32-K32GetPerformanceInfo
|
@ -249,6 +249,7 @@ patch_enable_all ()
|
||||
enable_ntdll_NtQueryVirtualMemory="$1"
|
||||
enable_ntdll_NtSetInformationToken="$1"
|
||||
enable_ntdll_NtSetLdtEntries="$1"
|
||||
enable_ntdll_NtSuspendProcess="$1"
|
||||
enable_ntdll_Pipe_SpecialCharacters="$1"
|
||||
enable_ntdll_ProcessImageFileNameWin32="$1"
|
||||
enable_ntdll_ProcessPriorityClass="$1"
|
||||
@ -974,6 +975,9 @@ patch_enable ()
|
||||
ntdll-NtSetLdtEntries)
|
||||
enable_ntdll_NtSetLdtEntries="$2"
|
||||
;;
|
||||
ntdll-NtSuspendProcess)
|
||||
enable_ntdll_NtSuspendProcess="$2"
|
||||
;;
|
||||
ntdll-Pipe_SpecialCharacters)
|
||||
enable_ntdll_Pipe_SpecialCharacters="$2"
|
||||
;;
|
||||
@ -2316,6 +2320,13 @@ if test "$enable_ntdll_Purist_Mode" -eq 1; then
|
||||
enable_ntdll_DllRedirects=1
|
||||
fi
|
||||
|
||||
if test "$enable_ntdll_NtSuspendProcess" -eq 1; then
|
||||
if test "$enable_kernel32_K32GetPerformanceInfo" -gt 1; then
|
||||
abort "Patchset kernel32-K32GetPerformanceInfo disabled, but ntdll-NtSuspendProcess depends on that."
|
||||
fi
|
||||
enable_kernel32_K32GetPerformanceInfo=1
|
||||
fi
|
||||
|
||||
if test "$enable_ntdll_LdrEnumerateLoadedModules" -eq 1; then
|
||||
if test "$enable_ntdll_RtlQueryPackageIdentity" -gt 1; then
|
||||
abort "Patchset ntdll-RtlQueryPackageIdentity disabled, but ntdll-LdrEnumerateLoadedModules depends on that."
|
||||
@ -5652,6 +5663,21 @@ if test "$enable_ntdll_NtSetLdtEntries" -eq 1; then
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset ntdll-NtSuspendProcess
|
||||
# |
|
||||
# | This patchset has the following (direct or indirect) dependencies:
|
||||
# | * kernel32-K32GetPerformanceInfo
|
||||
# |
|
||||
# | Modified files:
|
||||
# | * dlls/ntdll/process.c, dlls/ntdll/tests/Makefile.in, dlls/ntdll/tests/process.c, server/protocol.def, server/thread.c
|
||||
# |
|
||||
if test "$enable_ntdll_NtSuspendProcess" -eq 1; then
|
||||
patch_apply ntdll-NtSuspendProcess/0001-ntdll-Implement-NtSuspendProcess-and-NtResumeProcess.patch
|
||||
(
|
||||
printf '%s\n' '+ { "Michael Müller", "ntdll: Implement NtSuspendProcess and NtResumeProcess.", 1 },';
|
||||
) >> "$patchlist"
|
||||
fi
|
||||
|
||||
# Patchset ntdll-Pipe_SpecialCharacters
|
||||
# |
|
||||
# | This patchset fixes the following Wine bugs:
|
||||
|
Loading…
Reference in New Issue
Block a user