From 19bf03ed4b48b398236c8a998394089c93b50891 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Thu, 18 Apr 2019 10:41:48 +0300 Subject: [PATCH] ntdll: Add NtSuspendProcess()/NtResumeProcess() implementation. Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- dlls/ntdll/process.c | 24 +++++- dlls/ntdll/tests/exception.c | 151 ++++++++++++++++++++++++++++++++- include/wine/server_protocol.h | 32 ++++++- server/process.c | 38 +++++++++ server/protocol.def | 12 +++ server/request.h | 8 ++ server/thread.c | 4 +- server/thread.h | 2 + server/trace.c | 16 ++++ 9 files changed, 278 insertions(+), 9 deletions(-) diff --git a/dlls/ntdll/process.c b/dlls/ntdll/process.c index 6c6c4273729..573a7a771d2 100644 --- a/dlls/ntdll/process.c +++ b/dlls/ntdll/process.c @@ -763,8 +763,16 @@ NTSTATUS WINAPI NtOpenProcess(PHANDLE handle, ACCESS_MASK access, */ NTSTATUS WINAPI NtResumeProcess( HANDLE handle ) { - FIXME("stub: %p\n", handle); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS ret; + + SERVER_START_REQ( resume_process ) + { + req->handle = wine_server_obj_handle( handle ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + + return ret; } /****************************************************************************** @@ -773,8 +781,16 @@ NTSTATUS WINAPI NtResumeProcess( HANDLE handle ) */ NTSTATUS WINAPI NtSuspendProcess( HANDLE handle ) { - FIXME("stub: %p\n", handle); - return STATUS_NOT_IMPLEMENTED; + NTSTATUS ret; + + SERVER_START_REQ( suspend_process ) + { + req->handle = wine_server_obj_handle( handle ); + ret = wine_server_call( req ); + } + SERVER_END_REQ; + + return ret; } diff --git a/dlls/ntdll/tests/exception.c b/dlls/ntdll/tests/exception.c index b7bf2748b5b..3b3f72f07d3 100644 --- a/dlls/ntdll/tests/exception.c +++ b/dlls/ntdll/tests/exception.c @@ -53,6 +53,8 @@ static NTSTATUS (WINAPI *pNtQueryInformationProcess)(HANDLE, PROCESSINFOCLASS, static NTSTATUS (WINAPI *pNtSetInformationProcess)(HANDLE, PROCESSINFOCLASS, PVOID, ULONG); static BOOL (WINAPI *pIsWow64Process)(HANDLE, PBOOL); static NTSTATUS (WINAPI *pNtClose)(HANDLE); +static NTSTATUS (WINAPI *pNtSuspendProcess)(HANDLE process); +static NTSTATUS (WINAPI *pNtResumeProcess)(HANDLE process); #if defined(__x86_64__) typedef struct @@ -158,6 +160,9 @@ static VOID (CDECL *pRtlUnwindEx)(VOID*, VOID*, EXCEPTION_RECORD*, VOID*, C static int (CDECL *p_setjmp)(_JUMP_BUFFER*); #endif +static int my_argc; +static char** my_argv; + #ifdef __i386__ #ifndef __WINE_WINTRNL_H @@ -167,8 +172,6 @@ static int (CDECL *p_setjmp)(_JUMP_BUFFER*); #define MEM_EXECUTE_OPTION_PERMANENT 0x08 #endif -static int my_argc; -static char** my_argv; static int test_stage; static BOOL is_wow64; @@ -3190,6 +3193,138 @@ static void test_suspend_thread(void) CloseHandle(thread); } +static const char *suspend_process_event_name = "suspend_process_event"; +static const char *suspend_process_event2_name = "suspend_process_event2"; + +static DWORD WINAPI dummy_thread_proc( void *arg ) +{ + return 0; +} + +static void suspend_process_proc(void) +{ + HANDLE event = OpenEventA(SYNCHRONIZE, FALSE, suspend_process_event_name); + HANDLE event2 = OpenEventA(SYNCHRONIZE | EVENT_MODIFY_STATE, FALSE, suspend_process_event2_name); + unsigned int count; + NTSTATUS status; + HANDLE thread; + + ok(event != NULL, "Failed to open event handle.\n"); + ok(event2 != NULL, "Failed to open event handle.\n"); + + thread = CreateThread(NULL, 0, dummy_thread_proc, 0, CREATE_SUSPENDED, NULL); + ok(thread != NULL, "Failed to create auxiliary thread.\n"); + + /* Suspend up to limit. */ + while (!(status = NtSuspendThread(thread, NULL))) + ; + ok(status == STATUS_SUSPEND_COUNT_EXCEEDED, "Unexpected status %#x.\n", status); + + for (;;) + { + SetEvent(event2); + if (WaitForSingleObject(event, 100) == WAIT_OBJECT_0) + break; + } + + status = NtSuspendThread(thread, &count); + ok(!status, "Failed to suspend a thread, status %#x.\n", status); + ok(count == 125, "Unexpected suspend count %u.\n", count); + + status = NtResumeThread(thread, NULL); + ok(!status, "Failed to resume a thread, status %#x.\n", status); + + CloseHandle(event); + CloseHandle(event2); +} + +static void test_suspend_process(void) +{ + PROCESS_INFORMATION info; + char path_name[MAX_PATH]; + STARTUPINFOA startup; + HANDLE event, event2; + NTSTATUS status; + char **argv; + DWORD ret; + + event = CreateEventA(NULL, FALSE, FALSE, suspend_process_event_name); + ok(event != NULL, "Failed to create event.\n"); + + event2 = CreateEventA(NULL, FALSE, FALSE, suspend_process_event2_name); + ok(event2 != NULL, "Failed to create event.\n"); + + winetest_get_mainargs(&argv); + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + sprintf(path_name, "%s exception suspend_process", argv[0]); + + ret = CreateProcessA(NULL, path_name, NULL, NULL, FALSE, 0, NULL, NULL, &startup, &info), + ok(ret, "Failed to create target process.\n"); + + /* New process signals this event. */ + ResetEvent(event2); + ret = WaitForSingleObject(event2, INFINITE); + ok(ret == WAIT_OBJECT_0, "Wait failed, %#x.\n", ret); + + /* Suspend main thread */ + status = NtSuspendThread(info.hThread, &ret); + ok(!status && !ret, "Failed to suspend main thread, status %#x.\n", status); + + /* Process wasn't suspended yet. */ + status = pNtResumeProcess(info.hProcess); + ok(!status, "Failed to resume a process, status %#x.\n", status); + + status = pNtSuspendProcess(0); + ok(status == STATUS_INVALID_HANDLE, "Unexpected status %#x.\n", status); + + status = pNtResumeProcess(info.hProcess); + ok(!status, "Failed to resume a process, status %#x.\n", status); + + ResetEvent(event2); + ret = WaitForSingleObject(event2, 200); + ok(ret == WAIT_OBJECT_0, "Wait failed.\n"); + + status = pNtSuspendProcess(info.hProcess); + ok(!status, "Failed to suspend a process, status %#x.\n", status); + + status = NtSuspendThread(info.hThread, &ret); + ok(!status && ret == 1, "Failed to suspend main thread, status %#x.\n", status); + status = NtResumeThread(info.hThread, &ret); + ok(!status && ret == 2, "Failed to resume main thread, status %#x.\n", status); + + ResetEvent(event2); + ret = WaitForSingleObject(event2, 200); + ok(ret == WAIT_TIMEOUT, "Wait failed.\n"); + + status = pNtSuspendProcess(info.hProcess); + ok(!status, "Failed to suspend a process, status %#x.\n", status); + + status = pNtResumeProcess(info.hProcess); + ok(!status, "Failed to resume a process, status %#x.\n", status); + + ResetEvent(event2); + ret = WaitForSingleObject(event2, 200); + ok(ret == WAIT_TIMEOUT, "Wait failed.\n"); + + status = pNtResumeProcess(info.hProcess); + ok(!status, "Failed to resume a process, status %#x.\n", status); + + ResetEvent(event2); + ret = WaitForSingleObject(event2, 200); + ok(ret == WAIT_OBJECT_0, "Wait failed.\n"); + + SetEvent(event); + + winetest_wait_child_process(info.hProcess); + + CloseHandle(info.hProcess); + CloseHandle(info.hThread); + + CloseHandle(event); + CloseHandle(event2); +} + START_TEST(exception) { HMODULE hntdll = GetModuleHandleA("ntdll.dll"); @@ -3197,6 +3332,14 @@ START_TEST(exception) HMODULE hmsvcrt = LoadLibraryA("msvcrt.dll"); #endif + my_argc = winetest_get_mainargs( &my_argv ); + + if (my_argc >= 3 && !strcmp(my_argv[2], "suspend_process")) + { + suspend_process_proc(); + return; + } + code_mem = VirtualAlloc(NULL, 65536, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); if(!code_mem) { trace("VirtualAlloc failed\n"); @@ -3224,6 +3367,8 @@ START_TEST(exception) pNtSetInformationProcess = (void*)GetProcAddress( hntdll, "NtSetInformationProcess" ); pIsWow64Process = (void *)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsWow64Process"); + pNtSuspendProcess = (void *)GetProcAddress( hntdll, "NtSuspendProcess" ); + pNtResumeProcess = (void *)GetProcAddress( hntdll, "NtResumeProcess" ); #ifdef __i386__ if (!pIsWow64Process || !pIsWow64Process( GetCurrentProcess(), &is_wow64 )) is_wow64 = FALSE; @@ -3309,6 +3454,7 @@ START_TEST(exception) test_prot_fault(); test_thread_context(); test_suspend_thread(); + test_suspend_process(); #elif defined(__x86_64__) pRtlAddFunctionTable = (void *)GetProcAddress( hntdll, @@ -3351,6 +3497,7 @@ START_TEST(exception) test_dpe_exceptions(); test_wow64_context(); test_suspend_thread(); + test_suspend_process(); if (pRtlAddFunctionTable && pRtlDeleteFunctionTable && pRtlInstallFunctionTableCallback && pRtlLookupFunctionEntry) test_dynamic_unwind(); diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 88b933cc14e..c3d1bb6a593 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -5742,6 +5742,30 @@ struct terminate_job_reply }; + +struct suspend_process_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct suspend_process_reply +{ + struct reply_header __header; +}; + + + +struct resume_process_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct resume_process_reply +{ + struct reply_header __header; +}; + + enum request { REQ_new_process, @@ -6040,6 +6064,8 @@ enum request REQ_set_job_limits, REQ_set_job_completion_port, REQ_terminate_job, + REQ_suspend_process, + REQ_resume_process, REQ_NB_REQUESTS }; @@ -6343,6 +6369,8 @@ union generic_request struct set_job_limits_request set_job_limits_request; struct set_job_completion_port_request set_job_completion_port_request; struct terminate_job_request terminate_job_request; + struct suspend_process_request suspend_process_request; + struct resume_process_request resume_process_request; }; union generic_reply { @@ -6644,8 +6672,10 @@ union generic_reply struct set_job_limits_reply set_job_limits_reply; struct set_job_completion_port_reply set_job_completion_port_reply; struct terminate_job_reply terminate_job_reply; + struct suspend_process_reply suspend_process_reply; + struct resume_process_reply resume_process_reply; }; -#define SERVER_PROTOCOL_VERSION 578 +#define SERVER_PROTOCOL_VERSION 579 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */ diff --git a/server/process.c b/server/process.c index 473d3b1a27a..1260baf0a97 100644 --- a/server/process.c +++ b/server/process.c @@ -1705,3 +1705,41 @@ DECL_HANDLER(set_job_completion_port) release_object( job ); } + +/* 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_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 ); + } +} diff --git a/server/protocol.def b/server/protocol.def index b6ad514463f..888e22faf7e 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -3909,3 +3909,15 @@ struct handle_info obj_handle_t handle; /* handle to the job */ int status; /* process exit code */ @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/request.h b/server/request.h index a3e95137e10..a21252c7a62 100644 --- a/server/request.h +++ b/server/request.h @@ -408,6 +408,8 @@ DECL_HANDLER(process_in_job); DECL_HANDLER(set_job_limits); DECL_HANDLER(set_job_completion_port); DECL_HANDLER(terminate_job); +DECL_HANDLER(suspend_process); +DECL_HANDLER(resume_process); #ifdef WANT_REQUEST_HANDLERS @@ -710,6 +712,8 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_set_job_limits, (req_handler)req_set_job_completion_port, (req_handler)req_terminate_job, + (req_handler)req_suspend_process, + (req_handler)req_resume_process, }; C_ASSERT( sizeof(affinity_t) == 8 ); @@ -2436,6 +2440,10 @@ C_ASSERT( sizeof(struct set_job_completion_port_request) == 32 ); C_ASSERT( FIELD_OFFSET(struct terminate_job_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct terminate_job_request, status) == 16 ); C_ASSERT( sizeof(struct terminate_job_request) == 24 ); +C_ASSERT( FIELD_OFFSET(struct suspend_process_request, handle) == 12 ); +C_ASSERT( sizeof(struct suspend_process_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct resume_process_request, handle) == 12 ); +C_ASSERT( sizeof(struct resume_process_request) == 16 ); #endif /* WANT_REQUEST_HANDLERS */ diff --git a/server/thread.c b/server/thread.c index b1d324f7dec..7057c9bbd0c 100644 --- a/server/thread.c +++ b/server/thread.c @@ -570,7 +570,7 @@ void stop_thread_if_suspended( struct thread *thread ) } /* suspend a thread */ -static int suspend_thread( struct thread *thread ) +int suspend_thread( struct thread *thread ) { int old_count = thread->suspend; if (thread->suspend < MAXIMUM_SUSPEND_COUNT) @@ -582,7 +582,7 @@ static int suspend_thread( struct thread *thread ) } /* resume a thread */ -static int resume_thread( struct thread *thread ) +int resume_thread( struct thread *thread ) { int old_count = thread->suspend; if (thread->suspend > 0) diff --git a/server/thread.h b/server/thread.h index bafc08ed421..8bf7d73ead8 100644 --- a/server/thread.h +++ b/server/thread.h @@ -131,6 +131,8 @@ extern struct token *thread_get_impersonation_token( struct thread *thread ); extern int set_thread_affinity( struct thread *thread, affinity_t affinity ); extern int is_cpu_supported( enum cpu_type cpu ); extern unsigned int get_supported_cpu_mask(void); +extern int suspend_thread( struct thread *thread ); +extern int resume_thread( struct thread *thread ); /* ptrace functions */ diff --git a/server/trace.c b/server/trace.c index d4d0c3eb8df..5c6325a2576 100644 --- a/server/trace.c +++ b/server/trace.c @@ -4582,6 +4582,16 @@ static void dump_terminate_job_request( const struct terminate_job_request *req fprintf( stderr, ", status=%d", req->status ); } +static void dump_suspend_process_request( const struct suspend_process_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + +static void dump_resume_process_request( const struct resume_process_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_new_process_request, (dump_func)dump_exec_process_request, @@ -4879,6 +4889,8 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_set_job_limits_request, (dump_func)dump_set_job_completion_port_request, (dump_func)dump_terminate_job_request, + (dump_func)dump_suspend_process_request, + (dump_func)dump_resume_process_request, }; static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { @@ -5178,6 +5190,8 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { NULL, NULL, NULL, + NULL, + NULL, }; static const char * const req_names[REQ_NB_REQUESTS] = { @@ -5477,6 +5491,8 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "set_job_limits", "set_job_completion_port", "terminate_job", + "suspend_process", + "resume_process", }; static const struct