This is a prereq for cross-process suspend on WOW64 - the thread created for that needs to run even when the whole process is suspended by e.g. the debugger.
Will be follow-up changes for THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH and THREAD_CREATE_FLAGS_SKIP_LOADER_INIT to support creating a native thread for that.
From: Billy Laws blaws05@gmail.com
--- dlls/ntdll/tests/thread.c | 32 ++++++++++++++++++++++++++++++++ include/winternl.h | 5 +++-- 2 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/dlls/ntdll/tests/thread.c b/dlls/ntdll/tests/thread.c index 436b1bf89e0..0a8facc45ed 100644 --- a/dlls/ntdll/tests/thread.c +++ b/dlls/ntdll/tests/thread.c @@ -31,6 +31,9 @@ static NTSTATUS (WINAPI *pNtCreateThreadEx)( HANDLE *, ACCESS_MASK, OBJECT_ATTRIBUTES *, HANDLE, PRTL_THREAD_START_ROUTINE, void *, ULONG, ULONG_PTR, SIZE_T, SIZE_T, PS_ATTRIBUTE_LIST * ); +static NTSTATUS (WINAPI *pNtSuspendProcess)(HANDLE process); +static NTSTATUS (WINAPI *pNtResumeProcess)(HANDLE process); + static int * (CDECL *p_errno)(void);
static void init_function_pointers(void) @@ -38,6 +41,8 @@ static void init_function_pointers(void) HMODULE hntdll = GetModuleHandleA( "ntdll.dll" ); #define GET_FUNC(name) p##name = (void *)GetProcAddress( hntdll, #name ); GET_FUNC( NtCreateThreadEx ); + GET_FUNC( NtSuspendProcess ); + GET_FUNC( NtResumeProcess ); GET_FUNC( _errno ); #undef GET_FUNC } @@ -242,6 +247,32 @@ static void test_NtCreateUserProcess(void) CloseHandle( thread ); }
+static void CALLBACK test_thread_bypass_process_freeze_proc(void *param) +{ + pNtSuspendProcess(NtCurrentProcess()); + /* The current process will be suspended forever here if THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE is nonfunctional. */ + pNtResumeProcess(NtCurrentProcess()); +} + +static void test_thread_bypass_process_freeze(void) +{ + HANDLE thread; + NTSTATUS status; + + if (!pNtCreateThreadEx || !pNtSuspendProcess || !pNtResumeProcess) + { + win_skip( "NtCreateThreadEx/NtSuspendProcess/NtResumeProcess are not available.\n" ); + return; + } + + status = pNtCreateThreadEx( &thread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), test_thread_bypass_process_freeze_proc, + NULL, THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE, 0, 0, 0, NULL ); + todo_wine ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + + WaitForSingleObject( thread, INFINITE ); + CloseHandle( thread ); +} + START_TEST(thread) { init_function_pointers(); @@ -250,4 +281,5 @@ START_TEST(thread) test_unique_teb(); test_errno(); test_NtCreateUserProcess(); + test_thread_bypass_process_freeze(); } diff --git a/include/winternl.h b/include/winternl.h index 0bf166b8996..daa5906fdaa 100644 --- a/include/winternl.h +++ b/include/winternl.h @@ -3936,8 +3936,9 @@ typedef struct _RTL_PROCESS_MODULE_INFORMATION_EX #define THREAD_CREATE_FLAGS_CREATE_SUSPENDED 0x00000001 #define THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH 0x00000002 #define THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER 0x00000004 -#define THREAD_CREATE_FLAGS_HAS_SECURITY_DESCRIPTOR 0x00000010 -#define THREAD_CREATE_FLAGS_ACCESS_CHECK_IN_TARGET 0x00000020 +#define THREAD_CREATE_FLAGS_LOADER_WORKER 0x00000010 +#define THREAD_CREATE_FLAGS_SKIP_LOADER_INIT 0x00000020 +#define THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE 0x00000040 #define THREAD_CREATE_FLAGS_INITIAL_THREAD 0x00000080
#ifdef __WINESRC__
From: Billy Laws blaws05@gmail.com
--- dlls/ntdll/tests/thread.c | 2 +- dlls/ntdll/unix/thread.c | 3 ++- server/process.c | 8 ++++---- server/thread.c | 21 ++++++++++++++------- server/thread.h | 1 + 5 files changed, 22 insertions(+), 13 deletions(-)
diff --git a/dlls/ntdll/tests/thread.c b/dlls/ntdll/tests/thread.c index 0a8facc45ed..30b24007c7a 100644 --- a/dlls/ntdll/tests/thread.c +++ b/dlls/ntdll/tests/thread.c @@ -267,7 +267,7 @@ static void test_thread_bypass_process_freeze(void)
status = pNtCreateThreadEx( &thread, THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), test_thread_bypass_process_freeze_proc, NULL, THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE, 0, 0, 0, NULL ); - todo_wine ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status ); + ok( status == STATUS_SUCCESS, "Got unexpected status %#lx.\n", status );
WaitForSingleObject( thread, INFINITE ); CloseHandle( thread ); diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c index ce6683e075f..02d96c1a6fc 100644 --- a/dlls/ntdll/unix/thread.c +++ b/dlls/ntdll/unix/thread.c @@ -1315,7 +1315,8 @@ NTSTATUS WINAPI NtCreateThreadEx( HANDLE *handle, ACCESS_MASK access, OBJECT_ATT ULONG flags, ULONG_PTR zero_bits, SIZE_T stack_commit, SIZE_T stack_reserve, PS_ATTRIBUTE_LIST *attr_list ) { - static const ULONG supported_flags = THREAD_CREATE_FLAGS_CREATE_SUSPENDED | THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER; + static const ULONG supported_flags = THREAD_CREATE_FLAGS_CREATE_SUSPENDED | THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER | + THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE; sigset_t sigset; pthread_t pthread_id; pthread_attr_t pthread_attr; diff --git a/server/process.c b/server/process.c index 9b3b0b8fc6c..fc4850b36f9 100644 --- a/server/process.c +++ b/server/process.c @@ -1053,7 +1053,7 @@ void suspend_process( struct process *process ) LIST_FOR_EACH_SAFE( ptr, next, &process->thread_list ) { struct thread *thread = LIST_ENTRY( ptr, struct thread, proc_entry ); - if (!thread->suspend) stop_thread( thread ); + if (!thread->bypass_proc_suspend && !thread->suspend) stop_thread( thread ); } } } @@ -1069,7 +1069,7 @@ void resume_process( struct process *process ) LIST_FOR_EACH_SAFE( ptr, next, &process->thread_list ) { struct thread *thread = LIST_ENTRY( ptr, struct thread, proc_entry ); - if (!thread->suspend) wake_thread( thread ); + if (!thread->bypass_proc_suspend && !thread->suspend) wake_thread( thread ); } } } @@ -2005,7 +2005,7 @@ DECL_HANDLER(suspend_process) LIST_FOR_EACH_SAFE( ptr, next, &process->thread_list ) { struct thread *thread = LIST_ENTRY( ptr, struct thread, proc_entry ); - suspend_thread( thread ); + if (!thread->bypass_proc_suspend) suspend_thread( thread ); }
release_object( process ); @@ -2024,7 +2024,7 @@ DECL_HANDLER(resume_process) LIST_FOR_EACH_SAFE( ptr, next, &process->thread_list ) { struct thread *thread = LIST_ENTRY( ptr, struct thread, proc_entry ); - resume_thread( thread ); + if (!thread->bypass_proc_suspend) resume_thread( thread ); }
release_object( process ); diff --git a/server/thread.c b/server/thread.c index 05ec6a4ec00..46b126c0a5b 100644 --- a/server/thread.c +++ b/server/thread.c @@ -418,6 +418,7 @@ static inline void init_thread_structure( struct thread *thread ) thread->base_priority = 0; thread->suspend = 0; thread->dbg_hidden = 0; + thread->bypass_proc_suspend = 0; thread->desktop_users = 0; thread->token = NULL; thread->desc = NULL; @@ -436,6 +437,11 @@ static inline void init_thread_structure( struct thread *thread ) thread->inflight[i].server = thread->inflight[i].client = -1; }
+static int get_effective_proc_suspend( struct thread *thread ) +{ + return thread->bypass_proc_suspend ? 0 : thread->process->suspend; +} + /* check if address looks valid for a client-side data structure (TEB etc.) */ static inline int is_valid_address( client_ptr_t addr ) { @@ -927,7 +933,7 @@ int suspend_thread( struct thread *thread ) int old_count = thread->suspend; if (thread->suspend < MAXIMUM_SUSPEND_COUNT) { - if (!(thread->process->suspend + thread->suspend++)) stop_thread( thread ); + if (!(get_effective_proc_suspend( thread ) + thread->suspend++)) stop_thread( thread ); } else set_error( STATUS_SUSPEND_COUNT_EXCEEDED ); return old_count; @@ -940,7 +946,7 @@ int resume_thread( struct thread *thread ) if (thread->suspend > 0) { if (!(--thread->suspend)) resume_delayed_debug_events( thread ); - if (!(thread->suspend + thread->process->suspend)) wake_thread( thread ); + if (!(thread->suspend + get_effective_proc_suspend( thread ))) wake_thread( thread ); } return old_count; } @@ -1121,7 +1127,7 @@ static int check_wait( struct thread *thread ) return STATUS_KERNEL_APC;
/* Suspended threads may not acquire locks, but they can run system APCs */ - if (thread->process->suspend + thread->suspend > 0) return -1; + if (get_effective_proc_suspend( thread ) + thread->suspend > 0) return -1;
if (wait->select == SELECT_WAIT_ALL) { @@ -1208,7 +1214,7 @@ int wake_thread_queue_entry( struct wait_queue_entry *entry ) client_ptr_t cookie;
if (thread->wait != wait) return 0; /* not the current wait */ - if (thread->process->suspend + thread->suspend > 0) return 0; /* cannot acquire locks */ + if (get_effective_proc_suspend( thread ) + thread->suspend > 0) return 0; /* cannot acquire locks */
assert( wait->select != SELECT_WAIT_ALL );
@@ -1231,7 +1237,7 @@ static void thread_timeout( void *ptr )
wait->user = NULL; if (thread->wait != wait) return; /* not the top-level wait, ignore it */ - if (thread->suspend + thread->process->suspend > 0) return; /* suspended, ignore it */ + if (thread->suspend + get_effective_proc_suspend( thread ) > 0) return; /* suspended, ignore it */
if (debug_level) fprintf( stderr, "%04x: *wakeup* signaled=TIMEOUT\n", thread->id ); end_wait( thread, STATUS_TIMEOUT ); @@ -1369,7 +1375,7 @@ static inline struct list *get_apc_queue( struct thread *thread, enum apc_type t /* check if thread is currently waiting for a (system) apc */ static inline int is_in_apc_wait( struct thread *thread ) { - return (thread->process->suspend || thread->suspend || + return (get_effective_proc_suspend( thread ) || thread->suspend || (thread->wait && (thread->wait->flags & SELECT_INTERRUPTIBLE))); }
@@ -1646,6 +1652,7 @@ DECL_HANDLER(new_thread) thread->system_regs = current->system_regs; if (req->flags & THREAD_CREATE_FLAGS_CREATE_SUSPENDED) thread->suspend++; thread->dbg_hidden = !!(req->flags & THREAD_CREATE_FLAGS_HIDE_FROM_DEBUGGER); + thread->bypass_proc_suspend = !!(req->flags & THREAD_CREATE_FLAGS_BYPASS_PROCESS_FREEZE); reply->tid = get_thread_id( thread ); if ((reply->handle = alloc_handle_no_access_check( current->process, thread, req->access, objattr->attributes ))) @@ -1739,7 +1746,7 @@ DECL_HANDLER(init_thread) set_thread_base_priority( current, current->base_priority ); set_thread_affinity( current, current->affinity );
- reply->suspend = (current->suspend || current->process->suspend || current->context != NULL); + reply->suspend = (current->suspend || get_effective_proc_suspend( current ) || current->context != NULL); }
/* terminate a thread */ diff --git a/server/thread.h b/server/thread.h index 58081be7481..2a144c2b248 100644 --- a/server/thread.h +++ b/server/thread.h @@ -86,6 +86,7 @@ struct thread int base_priority; /* base priority level (relative to process base priority class) */ int suspend; /* suspend count */ int dbg_hidden; /* hidden from debugger */ + int bypass_proc_suspend; /* will still run if the process is suspended */ obj_handle_t desktop; /* desktop handle */ int desktop_users; /* number of objects using the thread desktop */ timeout_t creation_time; /* Thread creation time */