From af35aada9b078f8dc71dcc85e505da8eee4571da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20D=C3=B6singer?= Date: Tue, 5 Sep 2017 17:34:03 +0200 Subject: [PATCH] ntdll: Make RtlDeregisterWaitEx(handle, INVALID_HANDLE_VALUE) thread safe. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Signed-off-by: Sebastian Lackner Signed-off-by: Alexandre Julliard --- 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 6063d51d9f9..1b0546a7816 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; }