[PATCH 2/2] server: Always set the async result when the APC object is destroyed.

Zebediah Figura z.figura12 at gmail.com
Wed Feb 24 16:11:35 CST 2021


This can happen if the async is terminated while there is no thread to queue
the APC to (as in the relevant test), or if the client dies before getting the
APC, or before transferring the APC results back to the server.

This also fixes a leak of async objects present since 61abc500f5. If a process
dies while accept asyncs are pending, the asyncs will be terminated but will
not find a valid thread to queue the APC to, and thus async_set_result() and
the completion callback are never called.

Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
 dlls/kernel32/tests/pipe.c |  6 +++---
 server/thread.c            | 15 +++++++++------
 2 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/dlls/kernel32/tests/pipe.c b/dlls/kernel32/tests/pipe.c
index f1feb6c53f0..0ac356c8483 100644
--- a/dlls/kernel32/tests/pipe.c
+++ b/dlls/kernel32/tests/pipe.c
@@ -4168,9 +4168,9 @@ static void test_exit_process_async(void)
     size = 0xdeadbeef;
     ret = GetQueuedCompletionStatus(port, &size, &key, &overlapped, 1000);
     ok(!ret, "expected failure\n");
-    todo_wine ok(GetLastError() == ERROR_OPERATION_ABORTED, "got error %u\n", GetLastError());
-    todo_wine ok(!size, "got size %u\n", size);
-    todo_wine ok(key == 123, "got key %Iu\n", key);
+    ok(GetLastError() == ERROR_OPERATION_ABORTED, "got error %u\n", GetLastError());
+    ok(!size, "got size %u\n", size);
+    ok(key == 123, "got key %Iu\n", key);
 
     CloseHandle(port);
     CloseHandle(server);
diff --git a/server/thread.c b/server/thread.c
index 3cee717e169..d544970f3c1 100644
--- a/server/thread.c
+++ b/server/thread.c
@@ -490,8 +490,16 @@ static int thread_apc_signaled( struct object *obj, struct wait_queue_entry *ent
 static void thread_apc_destroy( struct object *obj )
 {
     struct thread_apc *apc = (struct thread_apc *)obj;
+
     if (apc->caller) release_object( apc->caller );
-    if (apc->owner) release_object( apc->owner );
+    if (apc->owner)
+    {
+        if (apc->result.type == APC_ASYNC_IO)
+            async_set_result( apc->owner, apc->result.async_io.status, apc->result.async_io.total );
+        else if (apc->call.type == APC_ASYNC_IO)
+            async_set_result( apc->owner, apc->call.async_io.status, 0 );
+        release_object( apc->owner );
+    }
 }
 
 /* queue an async procedure call */
@@ -1649,11 +1657,6 @@ DECL_HANDLER(select)
             apc->result.create_thread.handle = handle;
             clear_error();  /* ignore errors from the above calls */
         }
-        else if (apc->result.type == APC_ASYNC_IO)
-        {
-            if (apc->owner)
-                async_set_result( apc->owner, apc->result.async_io.status, apc->result.async_io.total );
-        }
         wake_up( &apc->obj, 0 );
         close_handle( current->process, req->prev_apc );
         release_object( apc );
-- 
2.20.1




More information about the wine-devel mailing list