1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123

base / process / kill_win.cc [blame]

// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/process/kill.h"

#include <windows.h>

#include <io.h>
#include <stdint.h>

#include <algorithm>

#include "base/logging.h"
#include "base/notreached.h"
#include "base/process/memory.h"
#include "base/process/process_iterator.h"

namespace base {

TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) {
  DCHECK(exit_code);

  DWORD tmp_exit_code = 0;

  if (!::GetExitCodeProcess(handle, &tmp_exit_code)) {
    DPLOG(FATAL) << "GetExitCodeProcess() failed";

    // This really is a random number.  We haven't received any
    // information about the exit code, presumably because this
    // process doesn't have permission to get the exit code, or
    // because of some other cause for GetExitCodeProcess to fail
    // (MSDN docs don't give the possible failure error codes for
    // this function, so it could be anything).  But we don't want
    // to leave exit_code uninitialized, since that could cause
    // random interpretations of the exit code.  So we assume it
    // terminated "normally" in this case.
    *exit_code = win::kNormalTerminationExitCode;

    // Assume the child has exited normally if we can't get the exit
    // code.
    return TERMINATION_STATUS_NORMAL_TERMINATION;
  }
  if (tmp_exit_code == STILL_ACTIVE) {
    DWORD wait_result = WaitForSingleObject(handle, 0);
    if (wait_result == WAIT_TIMEOUT) {
      *exit_code = static_cast<int>(wait_result);
      return TERMINATION_STATUS_STILL_RUNNING;
    }

    if (wait_result == WAIT_FAILED) {
      DPLOG(ERROR) << "WaitForSingleObject() failed";
      *exit_code = static_cast<int>(wait_result);
    } else {
      DCHECK_EQ(WAIT_OBJECT_0, wait_result);
      DLOG(ERROR) << "The process used 0x103 (STILL_ACTIVE) as exit code.";
      *exit_code = static_cast<int>(tmp_exit_code);
    }

    return TERMINATION_STATUS_ABNORMAL_TERMINATION;
  }

  *exit_code = static_cast<int>(tmp_exit_code);

  // clang-format off
  switch (tmp_exit_code) {
    case win::kNormalTerminationExitCode:
      return TERMINATION_STATUS_NORMAL_TERMINATION;
    case win::kDebuggerInactiveExitCode:    // STATUS_DEBUGGER_INACTIVE.
    case win::kKeyboardInterruptExitCode:   // Control-C/end session.
    case win::kDebuggerTerminatedExitCode:  // Debugger terminated process.
    case win::kProcessKilledExitCode:       // Task manager kill.
      return TERMINATION_STATUS_PROCESS_WAS_KILLED;
    case win::kSandboxFatalMemoryExceeded:  // Terminated process due to
                                            // exceeding the sandbox job
                                            // object memory limits.
    case win::kOomExceptionCode:            // Ran out of memory.
      return TERMINATION_STATUS_OOM;
    // This exit code means the process failed an OS integrity check.
    // This is tested in ProcessMitigationsTest.* in sandbox.
    case win::kStatusInvalidImageHashExitCode:
      return TERMINATION_STATUS_INTEGRITY_FAILURE;
    default:
      // All other exit codes indicate crashes.
      return TERMINATION_STATUS_PROCESS_CRASHED;
  }
  // clang-format on
}

bool WaitForProcessesToExit(const FilePath::StringType& executable_name,
                            TimeDelta wait,
                            const ProcessFilter* filter) {
  bool result = true;
  DWORD start_time = GetTickCount();

  NamedProcessIterator iter(executable_name, filter);
  for (const ProcessEntry* entry = iter.NextProcessEntry(); entry;
       entry = iter.NextProcessEntry()) {
    DWORD remaining_wait = static_cast<DWORD>(
        std::max(static_cast<int64_t>(0),
                 wait.InMilliseconds() - (GetTickCount() - start_time)));
    HANDLE process = OpenProcess(SYNCHRONIZE,
                                 FALSE,
                                 entry->th32ProcessID);
    DWORD wait_result = WaitForSingleObject(process, remaining_wait);
    CloseHandle(process);
    result &= (wait_result == WAIT_OBJECT_0);
  }

  return result;
}

bool CleanupProcesses(const FilePath::StringType& executable_name,
                      TimeDelta wait,
                      int exit_code,
                      const ProcessFilter* filter) {
  if (WaitForProcessesToExit(executable_name, wait, filter))
    return true;
  KillProcesses(executable_name, exit_code, filter);
  return false;
}

}  // namespace base