|
| 1 | +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| 2 | +From: Santiago Gimeno < [email protected]> |
| 3 | +Date: Tue, 5 Mar 2024 14:54:59 +0100 |
| 4 | +Subject: win: almost fix race detecting ESRCH in uv_kill |
| 5 | + |
| 6 | +It might happen that only using `WaitForSingleObject()` with timeout 0 |
| 7 | +could return WAIT_TIMEOUT as the process might not have been signaled |
| 8 | +yet. To improve things, first use `GetExitCodeProcess()` and check |
| 9 | +that `status` is not `STILL_ACTIVE`. Then, to cover for the case that the exit |
| 10 | +code was actually `STILL_ACTIVE` use `WaitForSingleObject()`. This could |
| 11 | +still be prone to the race condition but only for that case. |
| 12 | + |
| 13 | +diff --git a/deps/uv/src/win/process.c b/deps/uv/src/win/process.c |
| 14 | +index 4e94dee90e13eede63d8e97ddc9992726f874ea9..f46f34289e8e7d3a2af914d89e6164b751a3e47d 100644 |
| 15 | +--- a/deps/uv/src/win/process.c |
| 16 | ++++ b/deps/uv/src/win/process.c |
| 17 | +@@ -1308,16 +1308,34 @@ static int uv__kill(HANDLE process_handle, int signum) { |
| 18 | + /* Unconditionally terminate the process. On Windows, killed processes |
| 19 | + * normally return 1. */ |
| 20 | + int err; |
| 21 | ++ DWORD status; |
| 22 | + |
| 23 | + if (TerminateProcess(process_handle, 1)) |
| 24 | + return 0; |
| 25 | + |
| 26 | +- /* If the process already exited before TerminateProcess was called,. |
| 27 | ++ /* If the process already exited before TerminateProcess was called, |
| 28 | + * TerminateProcess will fail with ERROR_ACCESS_DENIED. */ |
| 29 | + err = GetLastError(); |
| 30 | +- if (err == ERROR_ACCESS_DENIED && |
| 31 | +- WaitForSingleObject(process_handle, 0) == WAIT_OBJECT_0) { |
| 32 | +- return UV_ESRCH; |
| 33 | ++ if (err == ERROR_ACCESS_DENIED) { |
| 34 | ++ /* First check using GetExitCodeProcess() with status different from |
| 35 | ++ * STILL_ACTIVE (259). This check can be set incorrectly by the process, |
| 36 | ++ * though that is uncommon. */ |
| 37 | ++ if (GetExitCodeProcess(process_handle, &status) && |
| 38 | ++ status != STILL_ACTIVE) { |
| 39 | ++ return UV_ESRCH; |
| 40 | ++ } |
| 41 | ++ |
| 42 | ++ /* But the process could have exited with code == STILL_ACTIVE, use then |
| 43 | ++ * WaitForSingleObject with timeout zero. This is prone to a race |
| 44 | ++ * condition as it could return WAIT_TIMEOUT because the handle might |
| 45 | ++ * not have been signaled yet.That would result in returning the wrong |
| 46 | ++ * error code here (UV_EACCES instead of UV_ESRCH), but we cannot fix |
| 47 | ++ * the kernel synchronization issue that TerminateProcess is |
| 48 | ++ * inconsistent with WaitForSingleObject with just the APIs available to |
| 49 | ++ * us in user space. */ |
| 50 | ++ if (WaitForSingleObject(process_handle, 0) == WAIT_OBJECT_0) { |
| 51 | ++ return UV_ESRCH; |
| 52 | ++ } |
| 53 | + } |
| 54 | + |
| 55 | + return uv_translate_sys_error(err); |
| 56 | +@@ -1325,6 +1343,14 @@ static int uv__kill(HANDLE process_handle, int signum) { |
| 57 | + |
| 58 | + case 0: { |
| 59 | + /* Health check: is the process still alive? */ |
| 60 | ++ DWORD status; |
| 61 | ++ |
| 62 | ++ if (!GetExitCodeProcess(process_handle, &status)) |
| 63 | ++ return uv_translate_sys_error(GetLastError()); |
| 64 | ++ |
| 65 | ++ if (status != STILL_ACTIVE) |
| 66 | ++ return UV_ESRCH; |
| 67 | ++ |
| 68 | + switch (WaitForSingleObject(process_handle, 0)) { |
| 69 | + case WAIT_OBJECT_0: |
| 70 | + return UV_ESRCH; |
0 commit comments