summaryrefslogtreecommitdiffstats
path: root/chrome_frame/exception_barrier.h
blob: e3e5350262dcb45e28034848c0bf8184796a7737 (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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// 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.
//
// A class to make it easy to tag exception propagation boundaries and
// get crash reports of exceptions that pass over same.
//
// An exception barrier is used to report exceptions that pass through
// a boundary where exceptions shouldn't pass, such as e.g. COM interface
// boundaries.
// This is handy for any kind of plugin code, where if the exception passes
// through unhindered, it'll either be swallowed by an SEH exception handler
// above us on the stack, or be reported as an unhandled exception for
// the application hosting the plugin code.
//
// IMPORTANT NOTE: This class has crash_reporting disabled by default. To
// enable crash reporting call:
//
// @code
// ExceptionBarrierBase::set_crash_handling(true)
// @endcode
//
// somewhere in your initialization code.
//
// Then, to use this class, simply instantiate an ExceptionBarrier just inside
// the code boundary, like this:
// @code
// HRESULT SomeObject::SomeCOMMethod(...) {
//   ExceptionBarrier report_crashes;
//
//   ... other code here ...
// }
// @endcode
//
// There are three ExceptionBarrier types defined here:
//  1) ExceptionBarrier which reports all crashes it sees.
//  2) ExceptionBarrierReportOnlyModule which reports only crashes occurring
//     in this module.
//  3) ExceptionBarrierCustomHandler which calls the handler set by a call
//     to ExceptionBarrierCallCustomHandler::set_custom_handler(). Note that
//     there is one custom handler for all ExceptionBarrierCallCustomHandler
//     instances. If set_custom_handler() is never called, this places an
//     SEH in the chain that just returns ExceptionContinueSearch.

#ifndef CHROME_FRAME_EXCEPTION_BARRIER_H_
#define CHROME_FRAME_EXCEPTION_BARRIER_H_

#include <windows.h>

extern "C" IMAGE_DOS_HEADER __ImageBase;

// This is the type dictated for an exception handler by the platform ABI
// @see _except_handler in excpt.h
typedef EXCEPTION_DISPOSITION (__cdecl* ExceptionHandlerFunc)(
                                struct _EXCEPTION_RECORD* exception_record,
                                void*  establisher_frame,
                                struct _CONTEXT* context,
                                void*  reserved);

// The type of an exception record in the exception handler chain
struct EXCEPTION_REGISTRATION {
  EXCEPTION_REGISTRATION* prev;
  ExceptionHandlerFunc  handler;
};

// This is our raw exception handler, it must be declared extern "C" to
// match up with the SAFESEH declaration in our corresponding ASM file.
extern "C" EXCEPTION_DISPOSITION __cdecl
ExceptionBarrierHandler(struct _EXCEPTION_RECORD* exception_record,
                        void* establisher_frame,
                        struct _CONTEXT* context,
                        void* reserved);

// An alternate raw exception handler that reports crashes only for the current
// module. It must be declared extern "C" to match up with the SAFESEH
// declaration in our corresponding ASM file.
extern "C" EXCEPTION_DISPOSITION __cdecl
ExceptionBarrierReportOnlyModuleHandler(
    struct _EXCEPTION_RECORD* exception_record,
    void* establisher_frame,
    struct _CONTEXT* context,
    void* reserved);

// An alternate raw exception handler that calls out to a custom handler.
// It must be declared extern "C" to match up with the SAFESEH declaration in
// our corresponding ASM file.
extern "C" EXCEPTION_DISPOSITION __cdecl
ExceptionBarrierCallCustomHandler(
    struct _EXCEPTION_RECORD* exception_record,
    void* establisher_frame,
    struct _CONTEXT* context,
    void* reserved);


// @name These are implemented in the associated .asm file
// @{
extern "C" void WINAPI RegisterExceptionRecord(
                          EXCEPTION_REGISTRATION* registration,
                          ExceptionHandlerFunc func);
extern "C" void WINAPI UnregisterExceptionRecord(
                          EXCEPTION_REGISTRATION* registration);
// @}


// Traits classes for ExceptionBarrierT.
class EBTraitsBase {
 public:
  static void UnregisterException(EXCEPTION_REGISTRATION* registration) {
    UnregisterExceptionRecord(registration);
  }
};

class EBReportAllTraits : public EBTraitsBase {
 public:
  static void RegisterException(EXCEPTION_REGISTRATION* registration) {
    RegisterExceptionRecord(registration, ExceptionBarrierHandler);
  }
};

class EBReportOnlyThisModuleTraits : public EBTraitsBase {
 public:
  static void RegisterException(EXCEPTION_REGISTRATION* registration) {
    RegisterExceptionRecord(registration,
                            ExceptionBarrierReportOnlyModuleHandler);
  }
};

class EBCustomHandlerTraits : public EBTraitsBase {
 public:
  static void RegisterException(EXCEPTION_REGISTRATION* registration) {
    RegisterExceptionRecord(registration,
                            ExceptionBarrierCallCustomHandler);
  }
};

class ExceptionBarrierConfig {
 public:
  // Used to globally enable or disable crash handling by ExceptionBarrierBase
  // instances.
  static void set_enabled(bool enabled) {
    s_enabled_ = enabled;
  }
  static bool enabled() { return s_enabled_; }

  // Whether crash reports are enabled.
  static bool s_enabled_;
};

template <typename RegistrarTraits>
class ExceptionBarrierT {
 public:
  // Register the barrier in the SEH chain
  ExceptionBarrierT() {
    RegistrarTraits::RegisterException(&registration_);
  }

  // Unregister on destruction
  virtual ~ExceptionBarrierT() {
    RegistrarTraits::UnregisterException(&registration_);
  }

 protected:
  // Our SEH frame
  EXCEPTION_REGISTRATION registration_;
};

// This class allows for setting a custom exception handler function. The
// handler is shared among all instances. This class is intended to enable
// testing of the SEH registration.
template <typename RegistrarTraits>
class ExceptionBarrierCustomHandlerT :
    public ExceptionBarrierT<typename RegistrarTraits> {
 public:
  // Signature of the handler function which gets notified when
  // an exception propagates through a barrier.
  typedef void (CALLBACK* CustomExceptionHandler)(EXCEPTION_POINTERS* ptrs);

  // Used to set a global custom handler used by all
  // ExceptionBarrierCustomHandler instances.
  static void set_custom_handler(CustomExceptionHandler handler) {
    s_custom_handler_ = handler;
  }
  static CustomExceptionHandler custom_handler() { return s_custom_handler_; }

 private:
  static CustomExceptionHandler s_custom_handler_;
};

// Convenience typedefs for the ExceptionBarrierT specializations.
typedef ExceptionBarrierT<EBReportAllTraits> ExceptionBarrier;
typedef ExceptionBarrierT<EBReportOnlyThisModuleTraits>
    ExceptionBarrierReportOnlyModule;
typedef ExceptionBarrierCustomHandlerT<EBCustomHandlerTraits>
    ExceptionBarrierCustomHandler;

#endif  // CHROME_FRAME_EXCEPTION_BARRIER_H_