Chromium Code Reviews| 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 "util/mach/mach_message_server.h" | |
| 16 | |
| 17 #include <mach/mach_time.h> | |
| 18 | |
| 19 #include <limits> | |
| 20 | |
| 21 #include "base/mac/mach_logging.h" | |
| 22 #include "base/mac/scoped_mach_vm.h" | |
| 23 | |
| 24 namespace crashpad { | |
| 25 | |
| 26 namespace { | |
| 27 | |
| 28 mach_timebase_info_data_t* TimebaseInternal() { | |
| 29 mach_timebase_info_data_t* timebase_info = new mach_timebase_info_data_t; | |
| 30 kern_return_t kr = mach_timebase_info(timebase_info); | |
| 31 MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_timebase_info"; | |
| 32 return timebase_info; | |
| 33 } | |
| 34 | |
| 35 mach_timebase_info_data_t* Timebase() { | |
| 36 static mach_timebase_info_data_t* timebase_info = TimebaseInternal(); | |
| 37 return timebase_info; | |
| 38 } | |
| 39 | |
| 40 uint64_t NanosecondTicks() { | |
| 41 uint64_t absolute_time = mach_absolute_time(); | |
| 42 mach_timebase_info_data_t* timebase_info = Timebase(); | |
| 43 return absolute_time * timebase_info->numer / timebase_info->denom; | |
| 44 } | |
| 45 | |
| 46 const int kNanosecondsPerMillisecond = 1E6; | |
| 47 | |
| 48 // TimerRunning determines whether |deadline| has passed. If |deadline| is in | |
| 49 // the future, |*remaining_ms| is set to the number of milliseconds remaining, | |
| 50 // which will always be a positive value, and this function returns true. If | |
| 51 // |deadline| is zero (indicating that no timer is in effect), |*remaining_ms| | |
| 52 // is set to zero and this function returns true. Otherwise, this function | |
| 53 // returns false. |deadline| is specified on the same time base as is returned | |
| 54 // by NanosecondTicks(). | |
| 55 bool TimerRunning(uint64_t deadline, mach_msg_timeout_t* remaining_ms) { | |
| 56 if (!deadline) { | |
| 57 *remaining_ms = MACH_MSG_TIMEOUT_NONE; | |
| 58 return true; | |
| 59 } | |
| 60 | |
| 61 uint64_t now = NanosecondTicks(); | |
| 62 | |
| 63 if (now >= deadline) { | |
| 64 return false; | |
| 65 } | |
| 66 | |
| 67 uint64_t remaining = deadline - now; | |
| 68 | |
| 69 // Round to the nearest millisecond, taking care not to overflow. | |
| 70 const int kHalfMillisecondInNanoseconds = kNanosecondsPerMillisecond / 2; | |
| 71 mach_msg_timeout_t remaining_mach; | |
| 72 if (remaining <= | |
| 73 std::numeric_limits<uint64_t>::max() - kHalfMillisecondInNanoseconds) { | |
| 74 remaining_mach = (remaining + kHalfMillisecondInNanoseconds) / | |
| 75 kNanosecondsPerMillisecond; | |
| 76 } else { | |
| 77 remaining_mach = remaining / kNanosecondsPerMillisecond; | |
| 78 } | |
| 79 | |
| 80 if (remaining_mach == MACH_MSG_TIMEOUT_NONE) { | |
| 81 return false; | |
| 82 } | |
| 83 | |
| 84 *remaining_ms = remaining_mach; | |
| 85 return true; | |
| 86 } | |
| 87 | |
| 88 } // namespace | |
| 89 | |
| 90 // This implementation is based on 10.9.4 | |
| 91 // xnu-2422.110.17/libsyscall/mach/mach_msg.c mach_msg_server_once(), but | |
| 92 // adapted to local style using scopers, replacing the server callback function | |
| 93 // and |max_size| parameter with a C++ interface, and with the addition of the | |
| 94 // the |persistent| parameter allowing this function to serve as a stand-in for | |
| 95 // mach_msg_server(), the |nonblocking| parameter to control blocking directly, | |
| 96 // and the |timeout| parameter allowing this function to not block indefinitely. | |
| 97 mach_msg_return_t MachMessageServer(MachMessageServerInterface* interface, | |
| 98 mach_port_t receive_port, | |
| 99 mach_msg_options_t options, | |
| 100 bool persistent, | |
| 101 bool nonblocking, | |
| 102 mach_msg_timeout_t timeout) { | |
| 103 options &= ~(MACH_RCV_MSG | MACH_SEND_MSG); | |
| 104 | |
| 105 mach_msg_options_t timeout_options = MACH_RCV_TIMEOUT | MACH_SEND_TIMEOUT | | |
| 106 MACH_RCV_INTERRUPT | MACH_SEND_INTERRUPT; | |
| 107 | |
| 108 uint64_t deadline; | |
| 109 if (nonblocking) { | |
| 110 options |= timeout_options; | |
| 111 deadline = 0; | |
| 112 } else if (timeout != MACH_MSG_TIMEOUT_NONE) { | |
| 113 options |= timeout_options; | |
| 114 deadline = NanosecondTicks() + | |
| 115 static_cast<uint64_t>(timeout) * kNanosecondsPerMillisecond; | |
| 116 } else { | |
| 117 options &= ~timeout_options; | |
| 118 deadline = 0; | |
| 119 } | |
| 120 | |
| 121 mach_msg_size_t trailer_alloc = REQUESTED_TRAILER_SIZE(options); | |
| 122 mach_msg_size_t max_request_size = interface->MachMessageServerRequestSize(); | |
| 123 mach_msg_size_t request_alloc = round_page(max_request_size + trailer_alloc); | |
| 124 mach_msg_size_t request_size = (options & MACH_RCV_LARGE) | |
| 125 ? request_alloc | |
| 126 : max_request_size + trailer_alloc; | |
| 127 | |
| 128 mach_msg_size_t max_reply_size = interface->MachMessageServerReplySize(); | |
| 129 mach_msg_size_t reply_alloc = round_page( | |
| 130 (options & MACH_SEND_TRAILER) ? (max_reply_size + MAX_TRAILER_SIZE) | |
| 131 : max_reply_size); | |
| 132 | |
| 133 mach_port_t self = mach_task_self(); | |
| 134 | |
| 135 kern_return_t kr; | |
| 136 | |
| 137 do { | |
| 138 mach_msg_size_t this_request_alloc = request_alloc; | |
| 139 mach_msg_size_t this_request_size = request_size; | |
| 140 | |
| 141 base::mac::ScopedMachVM request_scoper; | |
| 142 mach_msg_header_t* request_header = NULL; | |
| 143 | |
| 144 while (!request_scoper.address()) { | |
| 145 vm_address_t request_addr; | |
| 146 kr = vm_allocate(self, | |
| 147 &request_addr, | |
| 148 this_request_alloc, | |
| 149 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); | |
| 150 if (kr != KERN_SUCCESS) { | |
| 151 return kr; | |
| 152 } | |
| 153 base::mac::ScopedMachVM trial_request_scoper(request_addr, | |
| 154 this_request_alloc); | |
| 155 request_header = reinterpret_cast<mach_msg_header_t*>(request_addr); | |
| 156 | |
| 157 do { | |
| 158 // If |options| contains MACH_RCV_INTERRUPT, retry mach_msg in a loop | |
| 159 // when it returns MACH_RCV_INTERRUPTED to recompute |remaining_ms| | |
| 160 // rather than allowing mach_msg to retry using the original timeout | |
| 161 // value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c | |
| 162 // mach_msg(). | |
| 163 mach_msg_timeout_t remaining_ms; | |
| 164 if (!TimerRunning(deadline, &remaining_ms)) { | |
| 165 return MACH_RCV_TIMED_OUT; | |
| 166 } | |
| 167 | |
| 168 kr = mach_msg(request_header, | |
| 169 options | MACH_RCV_MSG, | |
| 170 0, | |
| 171 this_request_size, | |
| 172 receive_port, | |
| 173 remaining_ms, | |
| 174 MACH_PORT_NULL); | |
| 175 } while (kr == MACH_RCV_INTERRUPTED); | |
| 176 | |
| 177 if (kr == MACH_MSG_SUCCESS) { | |
| 178 request_scoper.swap(trial_request_scoper); | |
| 179 } else if (kr == MACH_RCV_TOO_LARGE && options & MACH_RCV_LARGE) { | |
| 180 this_request_size = | |
| 181 round_page(request_header->msgh_size + trailer_alloc); | |
| 182 this_request_alloc = this_request_size; | |
| 183 } else { | |
| 184 return kr; | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 vm_address_t reply_addr; | |
| 189 kr = vm_allocate(self, | |
| 190 &reply_addr, | |
| 191 reply_alloc, | |
| 192 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); | |
| 193 if (kr != KERN_SUCCESS) { | |
| 194 return kr; | |
| 195 } | |
| 196 | |
| 197 base::mac::ScopedMachVM reply_scoper(reply_addr, reply_alloc); | |
| 198 | |
| 199 mach_msg_header_t* reply_header = | |
| 200 reinterpret_cast<mach_msg_header_t*>(reply_addr); | |
| 201 bool destroy_complex_request = false; | |
| 202 interface->MachMessageServerFunction( | |
| 203 request_header, reply_header, &destroy_complex_request); | |
| 204 | |
| 205 if (!(reply_header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) { | |
| 206 mig_reply_error_t* reply_mig = | |
| 207 reinterpret_cast<mig_reply_error_t*>(reply_header); | |
| 208 if (reply_mig->RetCode == MIG_NO_REPLY) { | |
| 209 reply_header->msgh_remote_port = MACH_PORT_NULL; | |
| 210 } else if (reply_mig->RetCode != KERN_SUCCESS && | |
| 211 request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) { | |
| 212 destroy_complex_request = true; | |
| 213 } | |
| 214 } | |
| 215 | |
| 216 if (destroy_complex_request && | |
| 217 request_header->msgh_bits & MACH_MSGH_BITS_COMPLEX) { | |
| 218 request_header->msgh_remote_port = MACH_PORT_NULL; | |
| 219 mach_msg_destroy(request_header); | |
| 220 } | |
| 221 | |
| 222 if (reply_header->msgh_remote_port != MACH_PORT_NULL) { | |
| 223 mach_msg_option_t send_options = | |
| 224 options | MACH_SEND_MSG | | |
| 225 (MACH_MSGH_BITS_REMOTE(reply_header->msgh_bits) == | |
| 226 MACH_MSG_TYPE_MOVE_SEND_ONCE | |
| 227 ? 0 | |
| 228 : MACH_SEND_TIMEOUT); | |
|
Robert Sesek
2014/09/08 16:28:47
Why is SEND_ONCE a gate for SEND_TIMEOUT?
| |
| 229 | |
| 230 bool running; | |
| 231 do { | |
| 232 // If |options| contains MACH_SEND_INTERRUPT, retry mach_msg in a loop | |
| 233 // when it returns MACH_SEND_INTERRUPTED to recompute |remaining_ms| | |
| 234 // rather than allowing mach_msg to retry using the original timeout | |
| 235 // value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c | |
| 236 // mach_msg(). | |
| 237 mach_msg_timeout_t remaining_ms; | |
| 238 running = TimerRunning(deadline, &remaining_ms); | |
| 239 if (!running) { | |
| 240 // Don’t return just yet. If the timer ran out in between the time the | |
| 241 // request was received and now, at least try to send the response. | |
| 242 remaining_ms = 0; | |
| 243 } | |
| 244 | |
| 245 kr = mach_msg(reply_header, | |
| 246 send_options, | |
| 247 reply_header->msgh_size, | |
| 248 0, | |
| 249 MACH_PORT_NULL, | |
| 250 remaining_ms, | |
| 251 MACH_PORT_NULL); | |
| 252 } while (kr == MACH_SEND_INTERRUPTED); | |
| 253 | |
| 254 if (kr != KERN_SUCCESS) { | |
| 255 if (kr == MACH_SEND_INVALID_DEST || kr == MACH_SEND_TIMED_OUT) { | |
| 256 mach_msg_destroy(reply_header); | |
| 257 } | |
| 258 return kr; | |
| 259 } | |
| 260 | |
| 261 if (!running) { | |
| 262 // The reply message was sent successfuly, so act as though the deadline | |
| 263 // was reached before or during the subsequent receive operation when in | |
| 264 // persistent mode, and just return success when not in persistent mode. | |
| 265 return persistent ? MACH_RCV_TIMED_OUT : kr; | |
| 266 } | |
| 267 } | |
| 268 } while (persistent); | |
| 269 | |
| 270 return kr; | |
| 271 } | |
| 272 | |
| 273 } // namespace crashpad | |
| OLD | NEW |