OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2014 The Crashpad Authors. All rights reserved. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 // See the License for the specific language governing permissions and | |
13 // limitations under the License. | |
14 | |
15 #include "handler/mac/exception_handler_server.h" | |
16 | |
17 #include "base/logging.h" | |
18 #include "base/mac/mach_logging.h" | |
19 #include "base/strings/stringprintf.h" | |
20 #include "util/mach/composite_mach_message_server.h" | |
21 #include "util/mach/exc_server_variants.h" | |
22 #include "util/mach/exception_behaviors.h" | |
23 #include "util/mach/mach_extensions.h" | |
24 #include "util/mach/mach_message.h" | |
25 #include "util/mach/mach_message_server.h" | |
26 #include "util/mach/notify_server.h" | |
27 | |
28 namespace crashpad { | |
29 | |
30 namespace { | |
31 | |
32 class ExceptionHandlerServerRun | |
33 : public UniversalMachExcServer::Interface, | |
34 public NotifyServer::Interface { | |
35 public: | |
36 explicit ExceptionHandlerServerRun(mach_port_t exception_port) | |
37 : UniversalMachExcServer::Interface(), | |
38 NotifyServer::Interface(), | |
39 mach_exc_server_(this), | |
40 notify_server_(this), | |
41 composite_mach_message_server_(), | |
42 exception_port_(exception_port), | |
43 notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)), | |
44 running_(true) { | |
45 CHECK_NE(notify_port_, kMachPortNull); | |
46 | |
47 composite_mach_message_server_.AddHandler(&mach_exc_server_); | |
48 composite_mach_message_server_.AddHandler(¬ify_server_); | |
49 } | |
50 | |
51 ~ExceptionHandlerServerRun() { | |
52 } | |
53 | |
54 void Run() { | |
55 DCHECK(running_); | |
Robert Sesek
2014/12/29 16:17:08
To ensure that this method is only called once, wh
Mark Mentovai
2014/12/29 17:08:07
Robert Sesek wrote:
| |
56 | |
57 // Request that a no-senders notification for exception_port_ be sent to | |
58 // notify_port_. | |
59 mach_port_t previous; | |
60 kern_return_t kr = | |
61 mach_port_request_notification(mach_task_self(), | |
62 exception_port_, | |
63 MACH_NOTIFY_NO_SENDERS, | |
64 0, | |
65 notify_port_, | |
66 MACH_MSG_TYPE_MAKE_SEND_ONCE, | |
67 &previous); | |
68 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_request_notification"; | |
69 | |
70 if (previous != MACH_PORT_NULL) { | |
71 kr = mach_port_deallocate(mach_task_self(), previous); | |
72 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_deallocate"; | |
73 } | |
74 | |
75 // A single CompositeMachMessageServer will dispatch both exception messages | |
76 // and the no-senders notification. Put both receive rights into a port set. | |
77 // | |
78 // A single receive right can’t be used because the notification request | |
79 // requires a send-once right, which would prevent the no-senders condition | |
80 // from ever existing. Using distinct receive rights also allows the handler | |
81 // methods to ensure that the messages they process were sent by a holder of | |
82 // the proper send right. | |
83 base::mac::ScopedMachPortSet server_port_set( | |
84 NewMachPort(MACH_PORT_RIGHT_PORT_SET)); | |
85 | |
86 kr = mach_port_insert_member( | |
87 mach_task_self(), exception_port_, server_port_set); | |
88 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member"; | |
89 | |
90 kr = mach_port_insert_member( | |
91 mach_task_self(), notify_port_, server_port_set); | |
92 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member"; | |
93 | |
94 // Run the server in kOneShot mode so that running_ can be reevaluated after | |
95 // each message. Receipt of a valid no-senders notification causes it to be | |
96 // set to false. | |
97 while (running_) { | |
98 // This will result in a call to CatchMachException() or | |
99 // DoMachNotifyNoSenders() as appropriate. | |
100 mach_msg_return_t mr = | |
101 MachMessageServer::Run(&composite_mach_message_server_, | |
102 server_port_set, | |
103 MACH_MSG_OPTION_NONE, | |
104 MachMessageServer::kOneShot, | |
105 MachMessageServer::kReceiveLargeIgnore, | |
106 kMachMessageTimeoutWaitIndefinitely); | |
107 MACH_CHECK(mr == MACH_MSG_SUCCESS, mr) << "MachMessageServer::Run"; | |
Robert Sesek
2014/12/29 16:17:08
Won't this abort if an error occurs, contrary to t
Mark Mentovai
2014/12/29 17:08:07
Robert Sesek wrote:
| |
108 } | |
109 } | |
110 | |
111 // UniversalMachExcServer::Interface: | |
112 | |
113 kern_return_t CatchMachException(exception_behavior_t behavior, | |
114 exception_handler_t exception_port, | |
115 thread_t thread, | |
116 task_t task, | |
117 exception_type_t exception, | |
118 const mach_exception_data_type_t* code, | |
119 mach_msg_type_number_t code_count, | |
120 thread_state_flavor_t* flavor, | |
121 const natural_t* old_state, | |
122 mach_msg_type_number_t old_state_count, | |
123 thread_state_t new_state, | |
124 mach_msg_type_number_t* new_state_count, | |
125 const mach_msg_trailer_t* trailer, | |
126 bool* destroy_complex_request) override { | |
127 *destroy_complex_request = true; | |
128 | |
129 if (exception_port != exception_port_) { | |
130 LOG(WARNING) << "exception port mismatch"; | |
131 return MIG_BAD_ID; | |
Robert Sesek
2014/12/29 16:17:08
MIG_BAD_ARGUMENTS instead?
Mark Mentovai
2014/12/29 17:08:07
Robert Sesek wrote:
| |
132 } | |
133 | |
134 // The expected behavior is EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, | |
135 // but it’s possible to deal with any exception behavior as long as it | |
136 // carries identity information (valid thread and task ports). | |
137 if (!ExceptionBehaviorHasIdentity(behavior)) { | |
138 LOG(WARNING) << base::StringPrintf( | |
139 "unexpected exception behavior 0x%x, rejecting", behavior); | |
140 return KERN_FAILURE; | |
141 } else if (behavior != (EXCEPTION_STATE_IDENTITY | kMachExceptionCodes)) { | |
142 LOG(WARNING) << base::StringPrintf( | |
143 "unexpected exception behavior 0x%x, proceeding", behavior); | |
144 } | |
145 | |
146 // TODO(mark): Implement. | |
147 | |
148 return ExcServerSuccessfulReturnValue(behavior, false); | |
149 } | |
150 | |
151 // NotifyServer::Interface: | |
152 | |
153 kern_return_t DoMachNotifyPortDeleted( | |
154 notify_port_t notify, | |
155 mach_port_name_t name, | |
156 const mach_msg_trailer_t* trailer) override { | |
157 return UnimplementedNotifyRoutine(notify); | |
158 } | |
159 | |
160 kern_return_t DoMachNotifyPortDestroyed(notify_port_t notify, | |
161 mach_port_t rights, | |
162 const mach_msg_trailer_t* trailer, | |
163 bool* destroy_request) override { | |
164 *destroy_request = true; | |
165 return UnimplementedNotifyRoutine(notify); | |
166 } | |
167 | |
168 kern_return_t DoMachNotifyNoSenders( | |
169 notify_port_t notify, | |
170 mach_port_mscount_t mscount, | |
171 const mach_msg_trailer_t* trailer) override { | |
172 if (notify != notify_port_) { | |
173 // The message was received as part of a port set. This check ensures that | |
174 // only the authorized sender of the no-senders notification is able to | |
175 // stop the exception server. Otherwise, a malicious client would be able | |
176 // to craft and send a no-senders notification via its exception port, and | |
177 // cause the handler to stop processing exceptions and exit. | |
178 LOG(WARNING) << "notify port mismatch"; | |
179 return MIG_BAD_ID; | |
180 } | |
181 | |
182 running_ = false; | |
183 | |
184 return KERN_SUCCESS; | |
185 } | |
186 | |
187 kern_return_t DoMachNotifySendOnce( | |
188 notify_port_t notify, | |
189 const mach_msg_trailer_t* trailer) override { | |
190 return UnimplementedNotifyRoutine(notify); | |
191 } | |
192 | |
193 kern_return_t DoMachNotifyDeadName( | |
194 notify_port_t notify, | |
195 mach_port_name_t name, | |
196 const mach_msg_trailer_t* trailer) override { | |
197 return UnimplementedNotifyRoutine(notify); | |
198 } | |
199 | |
200 private: | |
201 kern_return_t UnimplementedNotifyRoutine(notify_port_t notify) { | |
202 // Most of the routines in the notify subsystem are not expected to be | |
203 // called. | |
204 if (notify != notify_port_) { | |
205 LOG(WARNING) << "notify port mismatch"; | |
206 return MIG_BAD_ID; | |
207 } | |
208 | |
209 NOTREACHED(); | |
210 return KERN_FAILURE; | |
211 } | |
212 | |
213 UniversalMachExcServer mach_exc_server_; | |
214 NotifyServer notify_server_; | |
215 CompositeMachMessageServer composite_mach_message_server_; | |
216 mach_port_t exception_port_; // weak | |
217 base::mac::ScopedMachReceiveRight notify_port_; | |
218 bool running_; | |
219 | |
220 DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerRun); | |
221 }; | |
222 | |
223 } // namespace | |
224 | |
225 ExceptionHandlerServer::ExceptionHandlerServer() | |
226 : receive_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)) { | |
227 CHECK_NE(receive_port_, kMachPortNull); | |
228 } | |
229 | |
230 ExceptionHandlerServer::~ExceptionHandlerServer() { | |
231 } | |
232 | |
233 void ExceptionHandlerServer::Run() { | |
234 ExceptionHandlerServerRun run(receive_port_); | |
235 run.Run(); | |
236 } | |
237 | |
238 } // namespace crashpad | |
OLD | NEW |