| 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 |