| 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, |
| (...skipping 12 matching lines...) Expand all Loading... |
| 23 namespace crashpad { | 23 namespace crashpad { |
| 24 | 24 |
| 25 namespace { | 25 namespace { |
| 26 | 26 |
| 27 const int kNanosecondsPerMillisecond = 1E6; | 27 const int kNanosecondsPerMillisecond = 1E6; |
| 28 | 28 |
| 29 // TimerRunning determines whether |deadline| has passed. If |deadline| is in | 29 // TimerRunning determines whether |deadline| has passed. If |deadline| is in |
| 30 // the future, |*remaining_ms| is set to the number of milliseconds remaining, | 30 // the future, |*remaining_ms| is set to the number of milliseconds remaining, |
| 31 // which will always be a positive value, and this function returns true. If | 31 // which will always be a positive value, and this function returns true. If |
| 32 // |deadline| is zero (indicating that no timer is in effect), |*remaining_ms| | 32 // |deadline| is zero (indicating that no timer is in effect), |*remaining_ms| |
| 33 // is set to zero and this function returns true. Otherwise, this function | 33 // is set to zero and this function returns true. Otherwise, this function sets |
| 34 // returns false. |deadline| is specified on the same time base as is returned | 34 // |*remaining_ms| to zero and returns false. |deadline| is specified on the |
| 35 // by ClockMonotonicNanoseconds(). | 35 // same time base as is returned by ClockMonotonicNanoseconds(). |
| 36 bool TimerRunning(uint64_t deadline, mach_msg_timeout_t* remaining_ms) { | 36 bool TimerRunning(uint64_t deadline, mach_msg_timeout_t* remaining_ms) { |
| 37 if (!deadline) { | 37 if (!deadline) { |
| 38 *remaining_ms = MACH_MSG_TIMEOUT_NONE; | 38 *remaining_ms = MACH_MSG_TIMEOUT_NONE; |
| 39 return true; | 39 return true; |
| 40 } | 40 } |
| 41 | 41 |
| 42 uint64_t now = ClockMonotonicNanoseconds(); | 42 uint64_t now = ClockMonotonicNanoseconds(); |
| 43 | 43 |
| 44 if (now >= deadline) { | 44 if (now >= deadline) { |
| 45 *remaining_ms = 0; |
| 45 return false; | 46 return false; |
| 46 } | 47 } |
| 47 | 48 |
| 48 uint64_t remaining = deadline - now; | 49 uint64_t remaining = deadline - now; |
| 49 | 50 |
| 50 // Round to the nearest millisecond, taking care not to overflow. | 51 // Round to the nearest millisecond, taking care not to overflow. |
| 51 const int kHalfMillisecondInNanoseconds = kNanosecondsPerMillisecond / 2; | 52 const int kHalfMillisecondInNanoseconds = kNanosecondsPerMillisecond / 2; |
| 52 mach_msg_timeout_t remaining_mach; | 53 mach_msg_timeout_t remaining_mach; |
| 53 if (remaining <= | 54 if (remaining <= |
| 54 std::numeric_limits<uint64_t>::max() - kHalfMillisecondInNanoseconds) { | 55 std::numeric_limits<uint64_t>::max() - kHalfMillisecondInNanoseconds) { |
| 55 remaining_mach = (remaining + kHalfMillisecondInNanoseconds) / | 56 remaining_mach = (remaining + kHalfMillisecondInNanoseconds) / |
| 56 kNanosecondsPerMillisecond; | 57 kNanosecondsPerMillisecond; |
| 57 } else { | 58 } else { |
| 58 remaining_mach = remaining / kNanosecondsPerMillisecond; | 59 remaining_mach = remaining / kNanosecondsPerMillisecond; |
| 59 } | 60 } |
| 60 | 61 |
| 61 if (remaining_mach == MACH_MSG_TIMEOUT_NONE) { | 62 if (remaining_mach == MACH_MSG_TIMEOUT_NONE) { |
| 63 *remaining_ms = 0; |
| 62 return false; | 64 return false; |
| 63 } | 65 } |
| 64 | 66 |
| 65 *remaining_ms = remaining_mach; | 67 *remaining_ms = remaining_mach; |
| 66 return true; | 68 return true; |
| 67 } | 69 } |
| 68 | 70 |
| 69 } // namespace | 71 } // namespace |
| 70 | 72 |
| 71 // This implementation is based on 10.9.4 | 73 // This implementation is based on 10.9.4 |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 124 ? max_request_size + trailer_alloc | 126 ? max_request_size + trailer_alloc |
| 125 : request_alloc; | 127 : request_alloc; |
| 126 | 128 |
| 127 mach_msg_size_t max_reply_size = interface->MachMessageServerReplySize(); | 129 mach_msg_size_t max_reply_size = interface->MachMessageServerReplySize(); |
| 128 | 130 |
| 129 // mach_msg_server() and mach_msg_server_once() would consider whether | 131 // mach_msg_server() and mach_msg_server_once() would consider whether |
| 130 // |options| contains MACH_SEND_TRAILER and include MAX_TRAILER_SIZE in this | 132 // |options| contains MACH_SEND_TRAILER and include MAX_TRAILER_SIZE in this |
| 131 // computation if it does, but that option is ineffective on OS X. | 133 // computation if it does, but that option is ineffective on OS X. |
| 132 mach_msg_size_t reply_alloc = round_page(max_reply_size); | 134 mach_msg_size_t reply_alloc = round_page(max_reply_size); |
| 133 | 135 |
| 136 base::mac::ScopedMachVM request_scoper; |
| 137 base::mac::ScopedMachVM reply_scoper; |
| 138 bool received_any_request = false; |
| 139 |
| 134 kern_return_t kr; | 140 kern_return_t kr; |
| 135 | 141 |
| 136 do { | 142 do { |
| 137 mach_msg_size_t this_request_alloc = request_alloc; | 143 mach_msg_size_t this_request_alloc = request_alloc; |
| 138 mach_msg_size_t this_request_size = request_size; | 144 mach_msg_size_t this_request_size = request_size; |
| 139 | 145 |
| 140 base::mac::ScopedMachVM request_scoper; | |
| 141 mach_msg_header_t* request_header = nullptr; | 146 mach_msg_header_t* request_header = nullptr; |
| 142 | 147 |
| 143 while (!request_scoper.address()) { | 148 do { |
| 144 vm_address_t request_addr; | 149 // This test uses != instead of < so that a large reallocation to receive |
| 145 kr = vm_allocate(mach_task_self(), | 150 // a large message doesn’t cause permanent memory bloat. |
| 146 &request_addr, | 151 if (request_scoper.size() != this_request_alloc) { |
| 147 this_request_alloc, | 152 // reset() first, so that two allocations don’t exist simultaneously. |
| 148 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); | 153 request_scoper.reset(); |
| 149 if (kr != KERN_SUCCESS) { | 154 |
| 150 return kr; | 155 vm_address_t request_addr; |
| 156 kr = vm_allocate(mach_task_self(), |
| 157 &request_addr, |
| 158 this_request_alloc, |
| 159 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); |
| 160 if (kr != KERN_SUCCESS) { |
| 161 return kr; |
| 162 } |
| 163 |
| 164 request_scoper.reset(request_addr, this_request_alloc); |
| 151 } | 165 } |
| 152 base::mac::ScopedMachVM trial_request_scoper(request_addr, | |
| 153 this_request_alloc); | |
| 154 request_header = reinterpret_cast<mach_msg_header_t*>(request_addr); | |
| 155 | 166 |
| 156 bool run_mach_msg_receive = false; | 167 request_header = |
| 168 reinterpret_cast<mach_msg_header_t*>(request_scoper.address()); |
| 169 |
| 157 do { | 170 do { |
| 158 // If |options| contains MACH_RCV_INTERRUPT, retry mach_msg() in a loop | 171 // If |options| contains MACH_RCV_INTERRUPT, retry mach_msg() in a loop |
| 159 // when it returns MACH_RCV_INTERRUPTED to recompute |remaining_ms| | 172 // when it returns MACH_RCV_INTERRUPTED to recompute |remaining_ms| |
| 160 // rather than allowing mach_msg() to retry using the original timeout | 173 // 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 | 174 // value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c |
| 162 // mach_msg(). | 175 // mach_msg(). Don’t return early here if nothing has ever been |
| 176 // received: this method should always attempt to dequeue at least one |
| 177 // message. |
| 163 mach_msg_timeout_t remaining_ms; | 178 mach_msg_timeout_t remaining_ms; |
| 164 if (!TimerRunning(deadline, &remaining_ms)) { | 179 if (!TimerRunning(deadline, &remaining_ms) && received_any_request) { |
| 165 return MACH_RCV_TIMED_OUT; | 180 return MACH_RCV_TIMED_OUT; |
| 166 } | 181 } |
| 167 | 182 |
| 168 kr = mach_msg(request_header, | 183 kr = mach_msg(request_header, |
| 169 options | MACH_RCV_MSG, | 184 options | MACH_RCV_MSG, |
| 170 0, | 185 0, |
| 171 this_request_size, | 186 this_request_size, |
| 172 receive_port, | 187 receive_port, |
| 173 remaining_ms, | 188 remaining_ms, |
| 174 MACH_PORT_NULL); | 189 MACH_PORT_NULL); |
| 175 | 190 |
| 176 if (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeIgnore) { | 191 if (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeIgnore) { |
| 177 MACH_LOG(WARNING, kr) << "mach_msg: ignoring large message"; | 192 MACH_LOG(WARNING, kr) << "mach_msg: ignoring large message"; |
| 178 run_mach_msg_receive = true; | |
| 179 } else if (kr == MACH_RCV_INTERRUPTED) { | |
| 180 run_mach_msg_receive = true; | |
| 181 } | 193 } |
| 182 } while (run_mach_msg_receive); | 194 } while ( |
| 195 (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeIgnore) || |
| 196 kr == MACH_RCV_INTERRUPTED); |
| 183 | 197 |
| 184 if (kr == MACH_MSG_SUCCESS) { | 198 if (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeResize) { |
| 185 request_scoper.swap(trial_request_scoper); | |
| 186 } else if (kr == MACH_RCV_TOO_LARGE && | |
| 187 receive_large == kReceiveLargeResize) { | |
| 188 this_request_size = | 199 this_request_size = |
| 189 round_page(request_header->msgh_size + trailer_alloc); | 200 round_page(request_header->msgh_size + trailer_alloc); |
| 190 this_request_alloc = this_request_size; | 201 this_request_alloc = this_request_size; |
| 191 } else { | 202 } else if (kr != MACH_MSG_SUCCESS) { |
| 192 return kr; | 203 return kr; |
| 193 } | 204 } |
| 205 } while (kr != MACH_MSG_SUCCESS); |
| 206 |
| 207 received_any_request = true; |
| 208 |
| 209 if (reply_scoper.size() != reply_alloc) { |
| 210 vm_address_t reply_addr; |
| 211 kr = vm_allocate(mach_task_self(), |
| 212 &reply_addr, |
| 213 reply_alloc, |
| 214 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); |
| 215 if (kr != KERN_SUCCESS) { |
| 216 return kr; |
| 217 } |
| 218 |
| 219 reply_scoper.reset(reply_addr, reply_alloc); |
| 194 } | 220 } |
| 195 | 221 |
| 196 vm_address_t reply_addr; | |
| 197 kr = vm_allocate(mach_task_self(), | |
| 198 &reply_addr, | |
| 199 reply_alloc, | |
| 200 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); | |
| 201 if (kr != KERN_SUCCESS) { | |
| 202 return kr; | |
| 203 } | |
| 204 | |
| 205 base::mac::ScopedMachVM reply_scoper(reply_addr, reply_alloc); | |
| 206 | |
| 207 mach_msg_header_t* reply_header = | 222 mach_msg_header_t* reply_header = |
| 208 reinterpret_cast<mach_msg_header_t*>(reply_addr); | 223 reinterpret_cast<mach_msg_header_t*>(reply_scoper.address()); |
| 209 bool destroy_complex_request = false; | 224 bool destroy_complex_request = false; |
| 210 interface->MachMessageServerFunction( | 225 interface->MachMessageServerFunction( |
| 211 request_header, reply_header, &destroy_complex_request); | 226 request_header, reply_header, &destroy_complex_request); |
| 212 | 227 |
| 213 if (!(reply_header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) { | 228 if (!(reply_header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) { |
| 214 // This only works if the reply message is not complex, because otherwise, | 229 // This only works if the reply message is not complex, because otherwise, |
| 215 // the location of the RetCode field is not known. It should be possible | 230 // the location of the RetCode field is not known. It should be possible |
| 216 // to locate the RetCode field by looking beyond the descriptors in a | 231 // to locate the RetCode field by looking beyond the descriptors in a |
| 217 // complex reply message, but this is not currently done. This behavior | 232 // complex reply message, but this is not currently done. This behavior |
| 218 // has not proven itself necessary in practice, and it’s not done by | 233 // has not proven itself necessary in practice, and it’s not done by |
| (...skipping 30 matching lines...) Expand all Loading... |
| 249 | 264 |
| 250 bool running; | 265 bool running; |
| 251 do { | 266 do { |
| 252 // If |options| contains MACH_SEND_INTERRUPT, retry mach_msg() in a loop | 267 // If |options| contains MACH_SEND_INTERRUPT, retry mach_msg() in a loop |
| 253 // when it returns MACH_SEND_INTERRUPTED to recompute |remaining_ms| | 268 // when it returns MACH_SEND_INTERRUPTED to recompute |remaining_ms| |
| 254 // rather than allowing mach_msg() to retry using the original timeout | 269 // rather than allowing mach_msg() to retry using the original timeout |
| 255 // value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c | 270 // value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c |
| 256 // mach_msg(). | 271 // mach_msg(). |
| 257 mach_msg_timeout_t remaining_ms; | 272 mach_msg_timeout_t remaining_ms; |
| 258 running = TimerRunning(deadline, &remaining_ms); | 273 running = TimerRunning(deadline, &remaining_ms); |
| 259 if (!running) { | 274 |
| 260 // Don’t return just yet. If the timer ran out in between the time the | 275 // Don’t return just yet even if |running| is false. If the timer ran |
| 261 // request was received and now, at least try to send the response. | 276 // out in between the time the request was received and now, at least |
| 262 remaining_ms = 0; | 277 // try to send the response. |
| 263 } | |
| 264 | 278 |
| 265 kr = mach_msg(reply_header, | 279 kr = mach_msg(reply_header, |
| 266 send_options, | 280 send_options, |
| 267 reply_header->msgh_size, | 281 reply_header->msgh_size, |
| 268 0, | 282 0, |
| 269 MACH_PORT_NULL, | 283 MACH_PORT_NULL, |
| 270 remaining_ms, | 284 remaining_ms, |
| 271 MACH_PORT_NULL); | 285 MACH_PORT_NULL); |
| 272 } while (kr == MACH_SEND_INTERRUPTED); | 286 } while (kr == MACH_SEND_INTERRUPTED); |
| 273 | 287 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 284 // persistent mode, and just return success when not in persistent mode. | 298 // persistent mode, and just return success when not in persistent mode. |
| 285 return (persistent == kPersistent) ? MACH_RCV_TIMED_OUT : kr; | 299 return (persistent == kPersistent) ? MACH_RCV_TIMED_OUT : kr; |
| 286 } | 300 } |
| 287 } | 301 } |
| 288 } while (persistent == kPersistent); | 302 } while (persistent == kPersistent); |
| 289 | 303 |
| 290 return kr; | 304 return kr; |
| 291 } | 305 } |
| 292 | 306 |
| 293 } // namespace crashpad | 307 } // namespace crashpad |
| OLD | NEW |