OLD | NEW |
1 // Copyright 2014 The Crashpad Authors. All rights reserved. | 1 // Copyright 2014 The Crashpad Authors. All rights reserved. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with 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 | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
13 // limitations under the License. | 13 // limitations under the License. |
14 | 14 |
15 #include "handler/mac/exception_handler_server.h" | 15 #include "handler/mac/exception_handler_server.h" |
16 | 16 |
| 17 #include <signal.h> |
| 18 |
| 19 #include "base/auto_reset.h" |
17 #include "base/logging.h" | 20 #include "base/logging.h" |
| 21 #include "base/memory/scoped_ptr.h" |
| 22 #include "base/scoped_generic.h" |
18 #include "base/mac/mach_logging.h" | 23 #include "base/mac/mach_logging.h" |
19 #include "util/mach/composite_mach_message_server.h" | 24 #include "util/mach/composite_mach_message_server.h" |
20 #include "util/mach/mach_extensions.h" | 25 #include "util/mach/mach_extensions.h" |
21 #include "util/mach/mach_message.h" | 26 #include "util/mach/mach_message.h" |
22 #include "util/mach/mach_message_server.h" | 27 #include "util/mach/mach_message_server.h" |
23 #include "util/mach/notify_server.h" | 28 #include "util/mach/notify_server.h" |
24 | 29 |
25 namespace crashpad { | 30 namespace crashpad { |
26 | 31 |
27 namespace { | 32 namespace { |
28 | 33 |
| 34 struct ResetSIGTERMTraits { |
| 35 static struct sigaction* InvalidValue() { |
| 36 return nullptr; |
| 37 } |
| 38 |
| 39 static void Free(struct sigaction* sa) { |
| 40 int rv = sigaction(SIGTERM, sa, nullptr); |
| 41 PLOG_IF(ERROR, rv != 0) << "sigaction"; |
| 42 } |
| 43 }; |
| 44 using ScopedResetSIGTERM = |
| 45 base::ScopedGeneric<struct sigaction*, ResetSIGTERMTraits>; |
| 46 |
| 47 mach_port_t g_signal_notify_port; |
| 48 |
| 49 // This signal handler is only operative when being run from launchd. It causes |
| 50 // the exception handler server to stop running by sending it a synthesized |
| 51 // no-senders notification. |
| 52 void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) { |
| 53 DCHECK(g_signal_notify_port); |
| 54 |
| 55 // mach_no_senders_notification_t defines the receive side of this structure, |
| 56 // with a trailer element that’s undesirable for the send side. |
| 57 struct { |
| 58 mach_msg_header_t header; |
| 59 NDR_record_t ndr; |
| 60 mach_msg_type_number_t mscount; |
| 61 } no_senders_notification = {}; |
| 62 no_senders_notification.header.msgh_bits = |
| 63 MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0); |
| 64 no_senders_notification.header.msgh_size = sizeof(no_senders_notification); |
| 65 no_senders_notification.header.msgh_remote_port = g_signal_notify_port; |
| 66 no_senders_notification.header.msgh_local_port = MACH_PORT_NULL; |
| 67 no_senders_notification.header.msgh_id = MACH_NOTIFY_NO_SENDERS; |
| 68 no_senders_notification.ndr = NDR_record; |
| 69 no_senders_notification.mscount = 0; |
| 70 |
| 71 kern_return_t kr = mach_msg(&no_senders_notification.header, |
| 72 MACH_SEND_MSG, |
| 73 sizeof(no_senders_notification), |
| 74 0, |
| 75 MACH_PORT_NULL, |
| 76 MACH_MSG_TIMEOUT_NONE, |
| 77 MACH_PORT_NULL); |
| 78 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg"; |
| 79 } |
| 80 |
29 class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface, | 81 class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface, |
30 public NotifyServer::DefaultInterface { | 82 public NotifyServer::DefaultInterface { |
31 public: | 83 public: |
32 ExceptionHandlerServerRun( | 84 ExceptionHandlerServerRun( |
33 mach_port_t exception_port, | 85 mach_port_t exception_port, |
| 86 bool launchd, |
34 UniversalMachExcServer::Interface* exception_interface) | 87 UniversalMachExcServer::Interface* exception_interface) |
35 : UniversalMachExcServer::Interface(), | 88 : UniversalMachExcServer::Interface(), |
36 NotifyServer::DefaultInterface(), | 89 NotifyServer::DefaultInterface(), |
37 mach_exc_server_(this), | 90 mach_exc_server_(this), |
38 notify_server_(this), | 91 notify_server_(this), |
39 composite_mach_message_server_(), | 92 composite_mach_message_server_(), |
40 exception_interface_(exception_interface), | 93 exception_interface_(exception_interface), |
41 exception_port_(exception_port), | 94 exception_port_(exception_port), |
42 notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)), | 95 notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)), |
43 running_(true) { | 96 running_(true), |
| 97 launchd_(launchd) { |
44 CHECK(notify_port_.is_valid()); | 98 CHECK(notify_port_.is_valid()); |
45 | 99 |
46 composite_mach_message_server_.AddHandler(&mach_exc_server_); | 100 composite_mach_message_server_.AddHandler(&mach_exc_server_); |
47 composite_mach_message_server_.AddHandler(¬ify_server_); | 101 composite_mach_message_server_.AddHandler(¬ify_server_); |
48 } | 102 } |
49 | 103 |
50 ~ExceptionHandlerServerRun() { | 104 ~ExceptionHandlerServerRun() { |
51 } | 105 } |
52 | 106 |
53 void Run() { | 107 void Run() { |
54 DCHECK(running_); | 108 DCHECK(running_); |
55 | 109 |
56 // Request that a no-senders notification for exception_port_ be sent to | 110 kern_return_t kr; |
57 // notify_port_. | 111 scoped_ptr<base::AutoReset<mach_port_t>> reset_signal_notify_port; |
58 mach_port_t previous; | 112 struct sigaction old_sa; |
59 kern_return_t kr = | 113 ScopedResetSIGTERM reset_sigterm; |
60 mach_port_request_notification(mach_task_self(), | 114 if (!launchd_) { |
61 exception_port_, | 115 // Request that a no-senders notification for exception_port_ be sent to |
62 MACH_NOTIFY_NO_SENDERS, | 116 // notify_port_. |
63 0, | 117 mach_port_t previous; |
64 notify_port_.get(), | 118 kr = mach_port_request_notification(mach_task_self(), |
65 MACH_MSG_TYPE_MAKE_SEND_ONCE, | 119 exception_port_, |
66 &previous); | 120 MACH_NOTIFY_NO_SENDERS, |
67 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_request_notification"; | 121 0, |
| 122 notify_port_.get(), |
| 123 MACH_MSG_TYPE_MAKE_SEND_ONCE, |
| 124 &previous); |
| 125 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_request_notification"; |
68 | 126 |
69 if (previous != MACH_PORT_NULL) { | 127 if (previous != MACH_PORT_NULL) { |
70 kr = mach_port_deallocate(mach_task_self(), previous); | 128 kr = mach_port_deallocate(mach_task_self(), previous); |
71 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_deallocate"; | 129 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_deallocate"; |
| 130 } |
| 131 } else { |
| 132 // A real no-senders notification would never be triggered, because |
| 133 // launchd maintains a send right to the service. When launchd wants the |
| 134 // job to exit, it will send a SIGTERM. See launchd.plist(5). |
| 135 // |
| 136 // Set up a SIGTERM handler that will cause Run() to return (incidentally, |
| 137 // by sending a synthetic no-senders notification). |
| 138 struct sigaction sa = {}; |
| 139 sigemptyset(&sa.sa_mask); |
| 140 sa.sa_flags = SA_SIGINFO; |
| 141 sa.sa_sigaction = HandleSIGTERM; |
| 142 int rv = sigaction(SIGTERM, &sa, &old_sa); |
| 143 PCHECK(rv == 0) << "sigaction"; |
| 144 reset_sigterm.reset(&old_sa); |
| 145 |
| 146 DCHECK(!g_signal_notify_port); |
| 147 reset_signal_notify_port.reset(new base::AutoReset<mach_port_t>( |
| 148 &g_signal_notify_port, notify_port_.get())); |
72 } | 149 } |
73 | 150 |
74 // A single CompositeMachMessageServer will dispatch both exception messages | 151 // A single CompositeMachMessageServer will dispatch both exception messages |
75 // and the no-senders notification. Put both receive rights into a port set. | 152 // and the no-senders notification. Put both receive rights into a port set. |
76 // | 153 // |
77 // A single receive right can’t be used because the notification request | 154 // A single receive right can’t be used because the notification request |
78 // requires a send-once right, which would prevent the no-senders condition | 155 // requires a send-once right, which would prevent the no-senders condition |
79 // from ever existing. Using distinct receive rights also allows the handler | 156 // from ever existing. Using distinct receive rights also allows the handler |
80 // methods to ensure that the messages they process were sent by a holder of | 157 // methods to ensure that the messages they process were sent by a holder of |
81 // the proper send right. | 158 // the proper send right. |
82 base::mac::ScopedMachPortSet server_port_set( | 159 base::mac::ScopedMachPortSet server_port_set( |
83 NewMachPort(MACH_PORT_RIGHT_PORT_SET)); | 160 NewMachPort(MACH_PORT_RIGHT_PORT_SET)); |
| 161 CHECK(server_port_set.is_valid()); |
84 | 162 |
85 kr = mach_port_insert_member( | 163 kr = mach_port_insert_member( |
86 mach_task_self(), exception_port_, server_port_set.get()); | 164 mach_task_self(), exception_port_, server_port_set.get()); |
87 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member"; | 165 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member"; |
88 | 166 |
89 kr = mach_port_insert_member( | 167 kr = mach_port_insert_member( |
90 mach_task_self(), notify_port_.get(), server_port_set.get()); | 168 mach_task_self(), notify_port_.get(), server_port_set.get()); |
91 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member"; | 169 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member"; |
92 | 170 |
93 // Run the server in kOneShot mode so that running_ can be reevaluated after | 171 // Run the server in kOneShot mode so that running_ can be reevaluated after |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
166 } | 244 } |
167 | 245 |
168 private: | 246 private: |
169 UniversalMachExcServer mach_exc_server_; | 247 UniversalMachExcServer mach_exc_server_; |
170 NotifyServer notify_server_; | 248 NotifyServer notify_server_; |
171 CompositeMachMessageServer composite_mach_message_server_; | 249 CompositeMachMessageServer composite_mach_message_server_; |
172 UniversalMachExcServer::Interface* exception_interface_; // weak | 250 UniversalMachExcServer::Interface* exception_interface_; // weak |
173 mach_port_t exception_port_; // weak | 251 mach_port_t exception_port_; // weak |
174 base::mac::ScopedMachReceiveRight notify_port_; | 252 base::mac::ScopedMachReceiveRight notify_port_; |
175 bool running_; | 253 bool running_; |
| 254 bool launchd_; |
176 | 255 |
177 DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerRun); | 256 DISALLOW_COPY_AND_ASSIGN(ExceptionHandlerServerRun); |
178 }; | 257 }; |
179 | 258 |
180 } // namespace | 259 } // namespace |
181 | 260 |
182 ExceptionHandlerServer::ExceptionHandlerServer( | 261 ExceptionHandlerServer::ExceptionHandlerServer( |
183 base::mac::ScopedMachReceiveRight receive_port) | 262 base::mac::ScopedMachReceiveRight receive_port, |
184 : receive_port_(receive_port.Pass()) { | 263 bool launchd) |
| 264 : receive_port_(receive_port.Pass()), |
| 265 launchd_(launchd) { |
185 CHECK(receive_port_.is_valid()); | 266 CHECK(receive_port_.is_valid()); |
186 } | 267 } |
187 | 268 |
188 ExceptionHandlerServer::~ExceptionHandlerServer() { | 269 ExceptionHandlerServer::~ExceptionHandlerServer() { |
189 } | 270 } |
190 | 271 |
191 void ExceptionHandlerServer::Run( | 272 void ExceptionHandlerServer::Run( |
192 UniversalMachExcServer::Interface* exception_interface) { | 273 UniversalMachExcServer::Interface* exception_interface) { |
193 ExceptionHandlerServerRun run(receive_port_.get(), exception_interface); | 274 ExceptionHandlerServerRun run( |
| 275 receive_port_.get(), launchd_, exception_interface); |
194 run.Run(); | 276 run.Run(); |
195 } | 277 } |
196 | 278 |
197 } // namespace crashpad | 279 } // namespace crashpad |
OLD | NEW |