summaryrefslogtreecommitdiffstats
path: root/chrome/app/hard_error_handler_win.cc
blob: 07c1bfc604beebfd78eb6c1648aed1869c546879 (plain)
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
124
125
126
127
128
129
130
131
132
133
134
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/app/hard_error_handler_win.h"

#if defined(_WIN32_WINNT_WIN8) && _MSC_VER < 1700
// The Windows 8 SDK defines FACILITY_VISUALCPP in winerror.h, and in
// delayimp.h previous to VS2012.
#undef FACILITY_VISUALCPP
#endif
#include <DelayIMP.h>

#include <ntsecapi.h>
#include <string>

#include "base/basictypes.h"
#include "base/logging.h"
#include "base/string_piece.h"
#include "base/sys_string_conversions.h"

namespace {

const DWORD kExceptionModuleNotFound = VcppException(ERROR_SEVERITY_ERROR,
                                                     ERROR_MOD_NOT_FOUND);
const DWORD kExceptionEntryPtNotFound = VcppException(ERROR_SEVERITY_ERROR,
                                                      ERROR_PROC_NOT_FOUND);

const int32 NT_STATUS_ENTRYPOINT_NOT_FOUND = 0xC0000139;
const int32 NT_STATUS_DLL_NOT_FOUND = 0xC0000135;

bool MakeNTUnicodeString(const std::wstring& str,
                         UNICODE_STRING* nt_string) {
  if (str.empty())
    return false;
  size_t str_size_bytes = str.size() * sizeof(wchar_t);
  if (kuint16max < str_size_bytes) {
    // The string is too long - nt_string->Length is USHORT
    NOTREACHED() << "The string is too long";
    return false;
  }
  nt_string->Length = static_cast<USHORT>(str_size_bytes);
  nt_string->MaximumLength = static_cast<USHORT>(str_size_bytes);
  nt_string->Buffer = const_cast<wchar_t*>(str.c_str());
  return true;
}

// NT-level function (not a win32 api) used to tell CSRSS of a critical error
// in the program which results in a message box dialog.
// The |exception| parameter is a standard exception code, the |param_count|
// indicates the number of items in |payload_params|. |payload_params| is
// dependent on the |exception| type but is typically an array to pointers to
// strings. |error_mode| indicates the kind of dialog buttons to show.
typedef LONG (WINAPI *NtRaiseHardErrorPF)(LONG exception,
                                          ULONG param_count,
                                          ULONG undocumented,
                                          PVOID payload_params,
                                          UINT error_mode,
                                          PULONG response);

// Helper function to call NtRaiseHardError(). It takes the exception code
// and one or two strings which are dependent on the exception code. No
// effort is done to validate that they match.
void RaiseHardErrorMsg(int32 exception, const std::wstring& text1,
                                        const std::wstring& text2) {
  // Bind the entry point. We can do it here since this function is really
  // called at most once per session. Usually never called.
  NtRaiseHardErrorPF NtRaiseHardError =
      reinterpret_cast<NtRaiseHardErrorPF>(
      ::GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtRaiseHardError"));
  if (!NtRaiseHardError)
    return;

  UNICODE_STRING uni_str1;
  UNICODE_STRING uni_str2;
  // A message needs to be displayed or else it would be confusing to the user.
  if (!MakeNTUnicodeString(text1, &uni_str1))
    return;
  int num_params = 1;
  // The second string is optional.
  if (MakeNTUnicodeString(text2, &uni_str2))
    num_params = 2;

  UNICODE_STRING* args[] = {&uni_str1, &uni_str2};
  uint32 undoc_value = 3;   // Display message to user.
  uint32 error_mode = 1;    // Display OK button only.
  ULONG response;           // What user clicked in the dialog. Discarded.
  NtRaiseHardError(exception, num_params, 3, args, error_mode, &response);
}

}  // namespace.

// Using RaiseHardErrorMsg(), it generates the same message box that is seen
// when the loader cannot find a DLL that a module depends on. |module| is the
// DLL name and it cannot be empty. The Message box only has an 'ok' button.
void ModuleNotFoundHardError(const char* module) {
  if (!module)
    return;
  std::wstring mod_name(base::SysMultiByteToWide(module, CP_ACP));
  RaiseHardErrorMsg(NT_STATUS_DLL_NOT_FOUND, mod_name, std::wstring());
}

// Using RaiseHardErrorMsg(), it generates the same message box that seen
// when the loader cannot find an import a module depends on. |module| is the
// DLL name and it cannot be empty. |entry| is the name of the method that
// could not be found. The Message box only has an 'ok' button.
void EntryPointNotFoundHardError(const char* entry, const char* module) {
  if (!module || !entry)
    return;
  std::wstring entry_point(base::SysMultiByteToWide(entry, CP_ACP));
  std::wstring mod_name(base::SysMultiByteToWide(module, CP_ACP));
  RaiseHardErrorMsg(NT_STATUS_ENTRYPOINT_NOT_FOUND, entry_point, mod_name);
}

bool DelayLoadFailureExceptionMessageBox(EXCEPTION_POINTERS* ex_info) {
  if (!ex_info)
    return false;
  DelayLoadInfo* dli = reinterpret_cast<DelayLoadInfo*>(
      ex_info->ExceptionRecord->ExceptionInformation[0]);
  if (!dli)
    return false;
  if (ex_info->ExceptionRecord->ExceptionCode == kExceptionModuleNotFound) {
    ModuleNotFoundHardError(dli->szDll);
    return true;
  }
  if (ex_info->ExceptionRecord->ExceptionCode == kExceptionEntryPtNotFound) {
    if (dli->dlp.fImportByName) {
      EntryPointNotFoundHardError(dli->dlp.szProcName, dli->szDll);
      return true;
    }
  }
  return false;
}