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