-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix in ash: do DuplicateHandle() after calling mingw_spawn_proc() before calling WaitForMultipleObjects() #2
Conversation
Great contribution, and a very detailed explanation, thank you so much! I wonder whether you want to open this PR at https://github.com/rmyorston/busybox-w32, too? That's the actual BusyBox-w32 project... |
|
Do `DuplicateHandle()` after calling `mingw_spawn_proc()` before calling `WaitForMultipleObjects()` in case the process handle is probable to be closed in `cull_exited_processes()` in process.c. Note that in `spawn_forkshell()`, `ret` is an `intptr_t` returned from `mingw_spawn_proc()`, and can be cast to a `HANDLE`, which is exactly the process handle. Later in the function it is passed to `forkparent()`, where it is stored in a `struct procstat` as `ps->ps_proc`. After that, the handle is used in `WaitForMultipleObjects()` in `waitpid_child()`. Consider the situation below: 1. `evalpipe()` is called so that two `spawn_forkshell()` is to be done 2. The first child process is created by `mingw_spawn_proc()` in `spawn_forkshell()`, and its process handle is returned 3. The first child process gets its timeslice and finishes running quickly (though normally this is not likely to happen) 4. The second child process is created by `mingw_spawn_proc()`, in which there are several chances to call `cull_exited_processes()`, where the first child's handle is closed 5. Backing to ash, in `waitpid_child()`, `WaitForMultipleObjects()` is called with handles of the two child processes. But the first one's handle is now invalid 6. The return value(`idx`) is thus invalid, then `waitpid_child()` returns -1 and `dowait()` returns -1 7. Because of the lack of error handling of `dowait()`, it is called repeatedly inside a while loop in `waitforjob()`. 8. Busybox gets into an endless loop. This issue is found during the development of a tool that injects a DLL into one process's all descandant processes. This DLL works by hooking `CreateProcessW` call, forcing the hooked process to also inject the child process it just created. This will change the thread scheduling behaviour a little bit among the hooked process and its child processes, which uncovers this problem. To resolve, let's `DuplicateHandle()` in `spawn_forkshell()`, after `mingw_spawn_proc()`; and properly close the duplicated handle.
063eb6a
to
761768b
Compare
I integrated those patches at long last (but it's still March 2020, right?), which required a bit of adjusting to the current code base. Range-diff to the versions I cherry-picked:
Where a339bfe was already applied in the form of 96c9c00:
|
Consider the situation below:
evalpipe()
is called so that twospawn_forkshell()
is to be donemingw_spawn_proc()
inspawn_forkshell()
, and its process handle is returnedmingw_spawn_proc()
, in which there are several chances to callcull_exited_processes()
, where the first child's handle is closedwaitpid_child()
,WaitForMultipleObjects()
is called with handles of the two child processes. But the first one's handle is now invalididx
) is thus invalid, thenwaitpid_child()
returns -1 anddowait()
returns -1dowait()
, it is called repeatedly inside a while loop inwaitforjob()
.Recently I am working on proxychains-windows. It hooks
CreateProcessW()
, changing the thread scheduling order a little bit, and that causes this problem.We should
DuplicateHandle()
inspawn_forkshell()
, and properly close the duplicated handle.Please cherry-pick the earliest commit.