=?UTF-8?Q?Stefan=20D=C3=B6singer=20?=: ntdll: Make RtlDeregisterWaitEx( handle, INVALID_HANDLE_VALUE) thread safe.

Alexandre Julliard julliard at winehq.org
Wed Sep 6 14:45:36 CDT 2017


Module: wine
Branch: master
Commit: af35aada9b078f8dc71dcc85e505da8eee4571da
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=af35aada9b078f8dc71dcc85e505da8eee4571da

Author: Stefan Dösinger <stefan at codeweavers.com>
Date:   Tue Sep  5 17:34:03 2017 +0200

ntdll: Make RtlDeregisterWaitEx(handle, INVALID_HANDLE_VALUE) thread safe.

Chromium signals the wait semaphore and calls DeregisterWaitEx with
CompletionHandle = INVALID_HANDLE_VALUE in close succession. Sometimes
the worker thread decides to run the callback, but before it sets
CallbackInProgress RtlDeregisterWaitEx decides that the callback is not
running and returns STATUS_SUCCESS. Chromium then releases resources
that the callback needs to run, resulting in random crashes.

Signed-off-by: Stefan Dösinger <stefan at codeweavers.com>
Signed-off-by: Sebastian Lackner <sebastian at fds-team.de>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/ntdll/threadpool.c | 59 +++++++++++++++++++++++++++----------------------
 1 file changed, 32 insertions(+), 27 deletions(-)

diff --git a/dlls/ntdll/threadpool.c b/dlls/ntdll/threadpool.c
index 6063d51..1b0546a 100644
--- a/dlls/ntdll/threadpool.c
+++ b/dlls/ntdll/threadpool.c
@@ -541,11 +541,13 @@ static DWORD CALLBACK wait_thread_proc(LPVOID Arg)
             break;
     }
 
-    completion_event = wait_work_item->CompletionEvent;
-    if (completion_event) NtSetEvent( completion_event, NULL );
 
     if (interlocked_inc( &wait_work_item->DeleteCount ) == 2 )
+    {
+        completion_event = wait_work_item->CompletionEvent;
         delete_wait_work_item( wait_work_item );
+        if (completion_event) NtSetEvent( completion_event, NULL );
+    }
 
     return 0;
 }
@@ -633,44 +635,47 @@ NTSTATUS WINAPI RtlRegisterWait(PHANDLE NewWaitObject, HANDLE Object,
 NTSTATUS WINAPI RtlDeregisterWaitEx(HANDLE WaitHandle, HANDLE CompletionEvent)
 {
     struct wait_work_item *wait_work_item = WaitHandle;
-    NTSTATUS status = STATUS_SUCCESS;
+    NTSTATUS status;
+    HANDLE LocalEvent = NULL;
+    BOOLEAN CallbackInProgress;
 
-    TRACE( "(%p)\n", WaitHandle );
+    TRACE( "(%p %p)\n", WaitHandle, CompletionEvent );
 
     if (WaitHandle == NULL)
         return STATUS_INVALID_HANDLE;
 
-    NtSetEvent( wait_work_item->CancelEvent, NULL );
-    if (wait_work_item->CallbackInProgress)
+    CallbackInProgress = wait_work_item->CallbackInProgress;
+    if (CompletionEvent == INVALID_HANDLE_VALUE || !CallbackInProgress)
     {
-        if (CompletionEvent != NULL)
-        {
-            if (CompletionEvent == INVALID_HANDLE_VALUE)
-            {
-                status = NtCreateEvent( &CompletionEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE );
-                if (status != STATUS_SUCCESS)
-                    return status;
-                interlocked_xchg_ptr( &wait_work_item->CompletionEvent, CompletionEvent );
-                if (wait_work_item->CallbackInProgress)
-                    NtWaitForSingleObject( CompletionEvent, FALSE, NULL );
-                NtClose( CompletionEvent );
-            }
-            else
-            {
-                interlocked_xchg_ptr( &wait_work_item->CompletionEvent, CompletionEvent );
-                if (wait_work_item->CallbackInProgress)
-                    status = STATUS_PENDING;
-            }
-        }
-        else
-            status = STATUS_PENDING;
+        status = NtCreateEvent( &LocalEvent, EVENT_ALL_ACCESS, NULL, NotificationEvent, FALSE );
+        if (status != STATUS_SUCCESS)
+            return status;
+        interlocked_xchg_ptr( &wait_work_item->CompletionEvent, LocalEvent );
+    }
+    else if (CompletionEvent != NULL)
+    {
+        interlocked_xchg_ptr( &wait_work_item->CompletionEvent, CompletionEvent );
     }
 
+    NtSetEvent( wait_work_item->CancelEvent, NULL );
+
     if (interlocked_inc( &wait_work_item->DeleteCount ) == 2 )
     {
         status = STATUS_SUCCESS;
         delete_wait_work_item( wait_work_item );
     }
+    else if (LocalEvent)
+    {
+        NtWaitForSingleObject( LocalEvent, FALSE, NULL );
+        status = STATUS_SUCCESS;
+    }
+    else
+    {
+        status = STATUS_PENDING;
+    }
+
+    if (LocalEvent)
+        NtClose( LocalEvent );
 
     return status;
 }




More information about the wine-cvs mailing list