aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohannes Schindelin <johannes.schindelin@gmx.de>2017-07-08 21:13:17 +0200
committerJohannes Schindelin <johannes.schindelin@gmx.de>2017-08-25 23:11:19 +0200
commitb122f9ae692eb5986f0e84826435aea8f2cf35e1 (patch)
tree4ca5c5c8763cf29d79598c61ae1297367345d423
parent7a4491e1f8c77a9825bb70fa4367549746eb90c0 (diff)
downloadbusybox-w32-b122f9ae692eb5986f0e84826435aea8f2cf35e1.tar.gz
busybox-w32-b122f9ae692eb5986f0e84826435aea8f2cf35e1.tar.bz2
busybox-w32-b122f9ae692eb5986f0e84826435aea8f2cf35e1.zip
kill: use gentler method than TerminateProcess()
As Git for Windows' source code recently learned, let's also avoid using TerminateProcess() in BusyBox-w32: it does not allow the killed processes' atexit() handlers to run. Instead, jump through a couple of hoops by injecting a remote thread that executes the ExitProcess() function. This allows the atexit() handlers to run, at which point the exit code of the process can already be queried via GetExitCodeProcess(), and appropriate action can be taken, such as killing child processes. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
-rw-r--r--win32/process.c162
1 files changed, 161 insertions, 1 deletions
diff --git a/win32/process.c b/win32/process.c
index 5eb52828b..8ecd14176 100644
--- a/win32/process.c
+++ b/win32/process.c
@@ -401,13 +401,173 @@ procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags UNUSED_PAR
401 return sp; 401 return sp;
402} 402}
403 403
404/*
405 * Terminates the process corresponding to the process ID and all of its
406 * directly and indirectly spawned subprocesses.
407 *
408 * This way of terminating the processes is not gentle: the processes get
409 * no chance of cleaning up after themselves (closing file handles, removing
410 * .lock files, terminating spawned processes (if any), etc).
411 */
412static int terminate_process_tree(HANDLE main_process, int exit_status)
413{
414 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
415 PROCESSENTRY32 entry;
416 DWORD pids[16384];
417 int max_len = sizeof(pids) / sizeof(*pids), i, len, ret = 0;
418 pid_t pid = GetProcessId(main_process);
419
420 pids[0] = (DWORD)pid;
421 len = 1;
422
423 /*
424 * Even if Process32First()/Process32Next() seem to traverse the
425 * processes in topological order (i.e. parent processes before
426 * child processes), there is nothing in the Win32 API documentation
427 * suggesting that this is guaranteed.
428 *
429 * Therefore, run through them at least twice and stop when no more
430 * process IDs were added to the list.
431 */
432 for (;;) {
433 int orig_len = len;
434
435 memset(&entry, 0, sizeof(entry));
436 entry.dwSize = sizeof(entry);
437
438 if (!Process32First(snapshot, &entry))
439 break;
440
441 do {
442 for (i = len - 1; i >= 0; i--) {
443 if (pids[i] == entry.th32ProcessID)
444 break;
445 if (pids[i] == entry.th32ParentProcessID)
446 pids[len++] = entry.th32ProcessID;
447 }
448 } while (len < max_len && Process32Next(snapshot, &entry));
449
450 if (orig_len == len || len >= max_len)
451 break;
452 }
453
454 for (i = len - 1; i > 0; i--) {
455 HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, pids[i]);
456
457 if (process) {
458 if (!TerminateProcess(process, exit_status))
459 ret = -1;
460 CloseHandle(process);
461 }
462 }
463 if (!TerminateProcess(main_process, exit_status))
464 ret = -1;
465 CloseHandle(main_process);
466
467 return ret;
468}
469
470/**
471 * Determine whether a process runs in the same architecture as the current
472 * one. That test is required before we assume that GetProcAddress() returns
473 * a valid address *for the target process*.
474 */
475static inline int process_architecture_matches_current(HANDLE process)
476{
477 static BOOL current_is_wow = -1;
478 BOOL is_wow;
479
480 if (current_is_wow == -1 &&
481 !IsWow64Process (GetCurrentProcess(), &current_is_wow))
482 current_is_wow = -2;
483 if (current_is_wow == -2)
484 return 0; /* could not determine current process' WoW-ness */
485 if (!IsWow64Process (process, &is_wow))
486 return 0; /* cannot determine */
487 return is_wow == current_is_wow;
488}
489
490/**
491 * This function tries to terminate a Win32 process, as gently as possible.
492 *
493 * At first, we will attempt to inject a thread that calls ExitProcess(). If
494 * that fails, we will fall back to terminating the entire process tree.
495 *
496 * Note: as kernel32.dll is loaded before any process, the other process and
497 * this process will have ExitProcess() at the same address.
498 *
499 * This function expects the process handle to have the access rights for
500 * CreateRemoteThread(): PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION,
501 * PROCESS_VM_OPERATION, PROCESS_VM_WRITE, and PROCESS_VM_READ.
502 *
503 * The idea comes from the Dr Dobb's article "A Safer Alternative to
504 * TerminateProcess()" by Andrew Tucker (July 1, 1999),
505 * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547
506 *
507 * If this method fails, we fall back to running terminate_process_tree().
508 */
509static int exit_process(HANDLE process, int exit_code)
510{
511 DWORD code;
512
513 if (GetExitCodeProcess(process, &code) && code == STILL_ACTIVE) {
514 static int initialized;
515 static LPTHREAD_START_ROUTINE exit_process_address;
516 PVOID arg = (PVOID)(intptr_t)exit_code;
517 DWORD thread_id;
518 HANDLE thread = NULL;
519
520 if (!initialized) {
521 HINSTANCE kernel32 = GetModuleHandle("kernel32");
522 if (!kernel32) {
523 fprintf(stderr, "BUG: cannot find kernel32");
524 return -1;
525 }
526 exit_process_address = (LPTHREAD_START_ROUTINE)
527 GetProcAddress(kernel32, "ExitProcess");
528 initialized = 1;
529 }
530 if (!exit_process_address ||
531 !process_architecture_matches_current(process))
532 return terminate_process_tree(process, exit_code);
533
534 thread = CreateRemoteThread(process, NULL, 0,
535 exit_process_address,
536 arg, 0, &thread_id);
537 if (thread) {
538 DWORD result;
539
540 CloseHandle(thread);
541 /*
542 * If the process survives for 10 seconds (a completely
543 * arbitrary value picked from thin air), fall back to
544 * killing the process tree via TerminateProcess().
545 */
546 result = WaitForSingleObject(process, 10000);
547 if (result == WAIT_OBJECT_0) {
548 CloseHandle(process);
549 return 0;
550 }
551 }
552
553 return terminate_process_tree(process, exit_code);
554 }
555
556 return 0;
557}
558
404int kill(pid_t pid, int sig) 559int kill(pid_t pid, int sig)
405{ 560{
406 HANDLE h; 561 HANDLE h;
407 562
408 if (pid > 0 && sig == SIGTERM) { 563 if (pid > 0 && sig == SIGTERM) {
564 if ((h = OpenProcess(SYNCHRONIZE | PROCESS_CREATE_THREAD |
565 PROCESS_QUERY_INFORMATION |
566 PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
567 PROCESS_VM_READ, FALSE, pid)))
568 return exit_process(h, 128 + sig);
409 if ((h=OpenProcess(PROCESS_TERMINATE, FALSE, pid)) != NULL && 569 if ((h=OpenProcess(PROCESS_TERMINATE, FALSE, pid)) != NULL &&
410 TerminateProcess(h, 0)) { 570 terminate_process_tree(h, 128 + sig)) {
411 CloseHandle(h); 571 CloseHandle(h);
412 return 0; 572 return 0;
413 } 573 }