Signed-off-by: Zebediah Figura z.figura12@gmail.com --- dlls/kernel32/tests/pipe.c | 70 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+)
diff --git a/dlls/kernel32/tests/pipe.c b/dlls/kernel32/tests/pipe.c index 91f6df34f81..f1feb6c53f0 100644 --- a/dlls/kernel32/tests/pipe.c +++ b/dlls/kernel32/tests/pipe.c @@ -4116,6 +4116,66 @@ static void test_GetOverlappedResultEx(void) CloseHandle(server); }
+static void child_process_exit_process_async(DWORD parent_pid, HANDLE parent_pipe) +{ + OVERLAPPED overlapped = {0}; + static char buffer[1]; + HANDLE parent, pipe; + BOOL ret; + + parent = OpenProcess(PROCESS_DUP_HANDLE, FALSE, parent_pid); + ok(!!parent, "got parent %p\n", parent); + + ret = DuplicateHandle(parent, parent_pipe, GetCurrentProcess(), &pipe, 0, + FALSE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE); + ok(ret, "got error %u\n", GetLastError()); + + overlapped.hEvent = CreateEventW(NULL, TRUE, FALSE, NULL); + + ret = ReadFile(pipe, buffer, sizeof(buffer), NULL, &overlapped); + ok(!ret, "expected failure\n"); + ok(GetLastError() == ERROR_IO_PENDING, "got error %u\n", GetLastError()); + + /* exit without closing the pipe handle */ +} + +static void test_exit_process_async(void) +{ + HANDLE client, server, port; + OVERLAPPED *overlapped; + PROCESS_INFORMATION pi; + STARTUPINFOA si = {0}; + char cmdline[300]; + ULONG_PTR key; + char **argv; + DWORD size; + BOOL ret; + + winetest_get_mainargs(&argv); + + create_overlapped_pipe(PIPE_TYPE_BYTE, &client, &server); + port = CreateIoCompletionPort(client, NULL, 123, 0); + + sprintf(cmdline, "%s pipe exit_process_async %x %p", argv[0], GetCurrentProcessId(), client); + ret = CreateProcessA(NULL, cmdline, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); + ok(ret, "got error %u\n", GetLastError()); + ret = WaitForSingleObject(pi.hProcess, 1000); + ok(!ret, "wait timed out\n"); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + + key = 0xdeadbeef; + 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); + + CloseHandle(port); + CloseHandle(server); +} + START_TEST(pipe) { char **argv; @@ -4158,6 +4218,15 @@ START_TEST(pipe) child_process_check_session_id(id); return; } + if (!strcmp(argv[2], "exit_process_async")) + { + HANDLE handle; + DWORD pid; + sscanf(argv[3], "%x", &pid); + sscanf(argv[4], "%p", &handle); + child_process_exit_process_async(pid, handle); + return; + } }
if (test_DisconnectNamedPipe()) @@ -4186,4 +4255,5 @@ START_TEST(pipe) test_nowait(PIPE_TYPE_BYTE); test_nowait(PIPE_TYPE_MESSAGE); test_GetOverlappedResultEx(); + test_exit_process_async(); }
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@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 );