[PATCH v2] ntdll: Implement NtGetNextThread().
Paul Gofman
pgofman at codeweavers.com
Wed Apr 28 10:21:21 CDT 2021
Largely based on a patch by Nikolay Sivov for NtGetNextProcess().
Signed-off-by: Paul Gofman <pgofman at codeweavers.com>
---
v2:
- stop searching for thread even if handle allocation failed;
- do not zero reply handle in server;
- set output handle to NULL in case of error (also add tests supporting that);
- move return handle assignment outside of SERVER_START_REQ() scope.
dlls/ntdll/ntdll.spec | 1 +
dlls/ntdll/tests/om.c | 93 ++++++++++++++++++++++++++++++++++++++++
dlls/ntdll/unix/thread.c | 29 +++++++++++++
server/protocol.def | 12 ++++++
server/thread.c | 50 +++++++++++++++++++++
5 files changed, 185 insertions(+)
diff --git a/dlls/ntdll/ntdll.spec b/dlls/ntdll/ntdll.spec
index a93fa08c406..cade83da4b1 100644
--- a/dlls/ntdll/ntdll.spec
+++ b/dlls/ntdll/ntdll.spec
@@ -223,6 +223,7 @@
@ stdcall -norelay -syscall NtGetContextThread(long ptr)
@ stdcall -syscall NtGetCurrentProcessorNumber()
# @ stub NtGetDevicePowerState
+@ stdcall -syscall NtGetNextThread(ptr ptr long long long ptr)
@ stdcall -syscall NtGetNlsSectionPtr(long long long ptr ptr)
@ stub NtGetPlugPlayEvent
@ stdcall NtGetTickCount()
diff --git a/dlls/ntdll/tests/om.c b/dlls/ntdll/tests/om.c
index e0988769c9c..7d9ca47be12 100644
--- a/dlls/ntdll/tests/om.c
+++ b/dlls/ntdll/tests/om.c
@@ -78,6 +78,8 @@ static void (WINAPI *pRtlWakeAddressAll)( const void * );
static void (WINAPI *pRtlWakeAddressSingle)( const void * );
static NTSTATUS (WINAPI *pNtOpenProcess)( HANDLE *, ACCESS_MASK, const OBJECT_ATTRIBUTES *, const CLIENT_ID * );
static NTSTATUS (WINAPI *pNtCreateDebugObject)( HANDLE *, ACCESS_MASK, OBJECT_ATTRIBUTES *, ULONG );
+static NTSTATUS (WINAPI *pNtGetNextThread)(HANDLE process, HANDLE thread, ACCESS_MASK access, ULONG attributes,
+ ULONG flags, HANDLE *handle);
#define KEYEDEVENT_WAIT 0x0001
#define KEYEDEVENT_WAKE 0x0002
@@ -2564,6 +2566,95 @@ static void test_object_types(void)
}
}
+static DWORD WINAPI test_get_next_thread_proc( void *arg )
+{
+ HANDLE event = (HANDLE)arg;
+
+ WaitForSingleObject(event, INFINITE);
+ return 0;
+}
+
+static void test_get_next_thread(void)
+{
+ HANDLE hprocess = GetCurrentProcess();
+ HANDLE handle, thread, event, prev;
+ NTSTATUS status;
+ DWORD thread_id;
+ BOOL found;
+
+ if (!pNtGetNextThread)
+ {
+ win_skip("NtGetNextThread is not available.\n");
+ return;
+ }
+
+ event = CreateEventA(NULL, FALSE, FALSE, NULL);
+
+ thread = CreateThread( NULL, 0, test_get_next_thread_proc, event, 0, &thread_id );
+
+ status = pNtGetNextThread(hprocess, NULL, THREAD_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, NULL);
+ ok(status == STATUS_ACCESS_VIOLATION, "Got unexected status %#x.\n", status);
+
+ found = FALSE;
+ prev = NULL;
+ while (!(status = pNtGetNextThread(hprocess, prev, THREAD_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, &handle)))
+ {
+ if (prev)
+ {
+ if (GetThreadId(handle) == thread_id)
+ found = TRUE;
+ pNtClose(prev);
+ }
+ else
+ {
+ ok(GetThreadId(handle) == GetCurrentThreadId(), "Got unexpected thread id %04x, current %04x.\n",
+ GetThreadId(handle), GetCurrentThreadId());
+ }
+ prev = handle;
+ handle = (HANDLE)0xdeadbeef;
+ }
+ pNtClose(prev);
+ ok(!handle, "Got unexpected handle %p.\n", handle);
+ ok(status == STATUS_NO_MORE_ENTRIES, "Unexpected status %#x.\n", status);
+ ok(found, "Thread not found.\n");
+
+ handle = (HANDLE)0xdeadbeef;
+ status = pNtGetNextThread((void *)0xdeadbeef, 0, PROCESS_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, &handle);
+ ok(status == STATUS_INVALID_HANDLE, "Unexpected status %#x.\n", status);
+ ok(!handle, "Got unexpected handle %p.\n", handle);
+ handle = (HANDLE)0xdeadbeef;
+ status = pNtGetNextThread(hprocess, (void *)0xdeadbeef, PROCESS_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, &handle);
+ ok(status == STATUS_INVALID_HANDLE, "Unexpected status %#x.\n", status);
+ ok(!handle, "Got unexpected handle %p.\n", handle);
+
+ /* Reversed search is only supported on recent enough Win10. */
+ status = pNtGetNextThread(hprocess, 0, PROCESS_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 1, &handle);
+ ok(!status || broken(status == STATUS_INVALID_PARAMETER), "Unexpected status %#x.\n", status);
+ if (!status)
+ pNtClose(handle);
+
+ status = pNtGetNextThread(hprocess, 0, PROCESS_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 2, &handle);
+ ok(status == STATUS_INVALID_PARAMETER, "Unexpected status %#x.\n", status);
+
+ SetEvent(event);
+ WaitForSingleObject(thread, INFINITE);
+
+ found = FALSE;
+ prev = NULL;
+ while (!(status = pNtGetNextThread(hprocess, prev, THREAD_QUERY_LIMITED_INFORMATION, OBJ_INHERIT, 0, &handle)))
+ {
+ if (prev)
+ pNtClose(prev);
+ if (GetThreadId(handle) == thread_id)
+ found = TRUE;
+ prev = handle;
+ }
+ pNtClose(prev);
+ ok(found, "Thread not found.\n");
+
+ CloseHandle(thread);
+}
+
START_TEST(om)
{
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
@@ -2615,6 +2706,7 @@ START_TEST(om)
pRtlWakeAddressSingle = (void *)GetProcAddress(hntdll, "RtlWakeAddressSingle");
pNtOpenProcess = (void *)GetProcAddress(hntdll, "NtOpenProcess");
pNtCreateDebugObject = (void *)GetProcAddress(hntdll, "NtCreateDebugObject");
+ pNtGetNextThread = (void *)GetProcAddress(hntdll, "NtGetNextThread");
test_case_sensitive();
test_namespace_pipe();
@@ -2632,4 +2724,5 @@ START_TEST(om)
test_wait_on_address();
test_process();
test_object_types();
+ test_get_next_thread();
}
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c
index 274fae24873..36f90372611 100644
--- a/dlls/ntdll/unix/thread.c
+++ b/dlls/ntdll/unix/thread.c
@@ -1382,3 +1382,32 @@ ULONG WINAPI NtGetCurrentProcessorNumber(void)
/* fallback to the first processor */
return 0;
}
+
+
+/******************************************************************************
+ * NtGetNextThread (NTDLL.@)
+ */
+NTSTATUS WINAPI NtGetNextThread(HANDLE hprocess, HANDLE hthread, ACCESS_MASK access, ULONG attributes,
+ ULONG flags, HANDLE *handle)
+{
+ HANDLE ret_handle;
+ NTSTATUS ret;
+
+ TRACE( "hprocess %p, hthread %p, access %#x, attributes %#x, flags %#x, handle %p.\n",
+ hprocess, hthread, access, attributes, flags, handle );
+
+ SERVER_START_REQ( get_next_thread )
+ {
+ req->process = wine_server_obj_handle( hprocess );
+ req->last = wine_server_obj_handle( hthread );
+ req->access = access;
+ req->attributes = attributes;
+ req->flags = flags;
+ ret = wine_server_call( req );
+ ret_handle = ret ? NULL : wine_server_ptr_handle( reply->handle );
+ }
+ SERVER_END_REQ;
+
+ *handle = ret_handle;
+ return ret;
+}
diff --git a/server/protocol.def b/server/protocol.def
index d061fca7073..9361933d47c 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -3687,3 +3687,15 @@ struct handle_info
@REQ(resume_process)
obj_handle_t handle; /* process handle */
@END
+
+
+/* Iterate thread list for process */
+ at REQ(get_next_thread)
+ obj_handle_t process; /* process handle */
+ obj_handle_t last; /* thread handle to start with */
+ unsigned int access; /* desired access for returned handle */
+ unsigned int attributes; /* returned handle attributes */
+ unsigned int flags; /* controls iteration direction */
+ at REPLY
+ obj_handle_t handle; /* next thread handle */
+ at END
diff --git a/server/thread.c b/server/thread.c
index 48f793b6a74..fc8362e2ca0 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -1925,3 +1925,53 @@ DECL_HANDLER(get_selector_entry)
release_object( thread );
}
}
+
+/* Iterate thread list for process. Use global thread list to also
+ * return terminated but not yet destroyed threads. */
+DECL_HANDLER(get_next_thread)
+{
+ struct thread *thread, *next;
+ struct process *process;
+ struct list *ptr;
+
+ if ( req->flags > 1 )
+ {
+ set_error( STATUS_INVALID_PARAMETER );
+ return;
+ }
+
+ if (!(process = get_process_from_handle( req->process, PROCESS_QUERY_INFORMATION )))
+ return;
+
+ if (!req->last)
+ {
+ ptr = req->flags ? list_tail( &thread_list ) : list_head( &thread_list );
+ }
+ else if ((thread = get_thread_from_handle( req->last, 0 )))
+ {
+ ptr = req->flags ? list_prev( &thread_list, &thread->entry ) :
+ list_next( &thread_list, &thread->entry );
+ release_object( thread );
+ }
+ else
+ {
+ release_object( process );
+ return;
+ }
+
+ while (ptr)
+ {
+ next = LIST_ENTRY( ptr, struct thread, entry );
+ if (next->process == process)
+ {
+ reply->handle = alloc_handle( current->process, next, req->access, req->attributes );
+ release_object( process );
+ return;
+ }
+ ptr = req->flags ? list_prev( &thread_list, &next->entry )
+ : list_next( &thread_list, &next->entry );
+ }
+
+ set_error( STATUS_NO_MORE_ENTRIES );
+ release_object( process );
+}
--
2.31.1
More information about the wine-devel
mailing list