From 34f55cc1771af01e49322148c89710f965d0197d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20M=C3=BCller?= 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 + +#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