[PATCH 0/1] MR10424: services: Cancel overlapped IO after timeout.
Though WaitForSingleObject timed out, the IO operation is still running in the background. After the function process_send_command returns, the OVERLAPPED object referenced by this IO operation becomes invalid. Later, when this IO finally completes, server_select will use invalid stack memory. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10424
From: Yuxuan Shui <yshui@codeweavers.com> Though WaitForSingleObject timed out, the IO operation is still running in the background. After the function process_send_command returns, the OVERLAPPED object referenced by this IO operation becomes invalid. Later, when this IO finally completes, server_select will use invalid stack memory. --- programs/services/rpc.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/programs/services/rpc.c b/programs/services/rpc.c index 1f36b783765..a5bfe3cc603 100644 --- a/programs/services/rpc.c +++ b/programs/services/rpc.c @@ -1200,16 +1200,22 @@ static BOOL process_send_command(struct process_entry *process, const void *data if (ret == WAIT_TIMEOUT) { WINE_ERR("receiving command result timed out\n"); - *result = ERROR_SERVICE_REQUEST_TIMEOUT; - return FALSE; + if (!CancelIoEx(process->control_pipe, &overlapped)) + WINE_ERR("Failed to cancel IO, %#lx\n", GetLastError()); } r = GetOverlappedResult(process->control_pipe, &overlapped, &count, FALSE); } if (!r || count != sizeof *result) { - WINE_ERR("service protocol error - failed to read pipe " - "r = %d count = %ld!\n", r, count); - *result = (!r ? GetLastError() : ERROR_READ_FAULT); + DWORD error = GetLastError(); + if (error == ERROR_OPERATION_ABORTED) + *result = ERROR_SERVICE_REQUEST_TIMEOUT; + else + { + WINE_ERR("service protocol error - failed to read pipe " + "r = %d count = %ld!\n", r, count); + *result = (!r ? GetLastError() : ERROR_READ_FAULT); + } return FALSE; } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10424
Cancelling doesn't terminate the I/O immediately. You should either allocate the OVERLAPPED structure in heap (so it persists after the function returns), or terminate the process (so the function doesn't return in the first place). -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10424#note_133270
Or simply wait for the I/O to finish after cancelling it. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10424#note_133387
On Mon Mar 23 18:58:58 2026 +0000, Elizabeth Figura wrote:
Or simply wait for the I/O to finish after cancelling it. If you send a command but don't receive a reply, the control pipe's state is already corrupted in the sense that your next command will either get no response of a stale reply to your previous command.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10424#note_133400
On Mon Mar 23 18:59:07 2026 +0000, Jinoh Kang wrote:
If you send a command but don't receive a reply, the control pipe's state is already corrupted in the sense that your next command will either get no response of a stale reply to your previous command. Sure, I didn't look at the details of what the I/O is actually used for. If we're that corrupted it probably doesn't make sense to allocate the overlapped in heap either...
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10424#note_133409
On Mon Mar 23 20:27:19 2026 +0000, Elizabeth Figura wrote:
Sure, I didn't look at the details of what the I/O is actually used for. If we're that corrupted it probably doesn't make sense to allocate the overlapped in heap either... Indeed.
Honestly I'd prefer just terminating immediately if possible. The heap allocation suggestion was more of a hack for the case when you cannot abort immediately and still need to run all other cleanup logic. (Meanwhile waiting for completion can report successful I/O even after CancelIoEx(), which I believe would complicate the logic for not much gain here.) Sorry for not being clear enough! -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10424#note_133501
waiting for completion can report successful I/O even after CancelIoEx()
In that case the IO completed normally, right? So it should be able to proceed as normal. * * * I think I just need to pass `wait = TRUE` to `GetOverlappedResult`? Allocating on heap is not necessary. Is the suggestion here that we abort services.exe if any commands time out? -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10424#note_133703
participants (4)
-
Elizabeth Figura (@zfigura) -
Jinoh Kang (@iamahuman) -
Yuxuan Shui -
Yuxuan Shui (@yshui)