wine-staging/patches/ntdll-NtSuspendProcess/0001-ntdll-Implement-NtSuspendProcess-and-NtResumeProcess.patch

366 lines
11 KiB
Diff

From 34f55cc1771af01e49322148c89710f965d0197d 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 623bf653..6cd16a42 100644
--- a/dlls/ntdll/process.c
+++ b/dlls/ntdll/process.c
@@ -763,8 +763,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;
}
/******************************************************************************
@@ -773,8 +783,18 @@ 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 5c70f3f0..a1b85883 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 00000000..41303b7e
--- /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 41d8f5a4..2aa76bdc 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3864,3 +3864,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 7162fc33..8bdfe41d 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -1824,3 +1824,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.19.1