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 "util/mach/mach_message.h" | 15 #include "util/mach/mach_message.h" |
16 | 16 |
| 17 #include <limits> |
| 18 |
| 19 #include "base/basictypes.h" |
| 20 #include "util/misc/clock.h" |
| 21 |
17 namespace crashpad { | 22 namespace crashpad { |
18 | 23 |
| 24 namespace { |
| 25 |
| 26 const int kNanosecondsPerMillisecond = 1E6; |
| 27 |
| 28 // TimerRunning() determines whether |deadline| has passed. If |deadline| is |
| 29 // kMachMessageWaitIndefinitely, |*timeout_options| is set to |
| 30 // MACH_MSG_OPTION_NONE, |*remaining_ms| is set to MACH_MSG_TIMEOUT_NONE, and |
| 31 // this function returns true. When used with mach_msg(), this will cause |
| 32 // indefinite waiting. In any other case, |*timeout_options| is set to |
| 33 // MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT, so mach_msg() will enforce a timeout |
| 34 // specified by |*remaining_ms|. If |deadline| is in the future, |*remaining_ms| |
| 35 // is set to the number of milliseconds remaining, which will always be a |
| 36 // positive value, and this function returns true. If |deadline| is |
| 37 // kMachMessageNonblocking (indicating that no timer is in effect), |
| 38 // |*remaining_ms| is set to zero and this function returns true. Otherwise, |
| 39 // this function sets |*remaining_ms| to zero and returns false. |
| 40 bool TimerRunning(uint64_t deadline, |
| 41 mach_msg_timeout_t* remaining_ms, |
| 42 mach_msg_option_t* timeout_options) { |
| 43 if (deadline == kMachMessageWaitIndefinitely) { |
| 44 *remaining_ms = MACH_MSG_TIMEOUT_NONE; |
| 45 *timeout_options = MACH_MSG_OPTION_NONE; |
| 46 return true; |
| 47 } |
| 48 |
| 49 *timeout_options = MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT; |
| 50 |
| 51 if (deadline == kMachMessageNonblocking) { |
| 52 *remaining_ms = 0; |
| 53 return true; |
| 54 } |
| 55 |
| 56 uint64_t now = ClockMonotonicNanoseconds(); |
| 57 |
| 58 if (now >= deadline) { |
| 59 *remaining_ms = 0; |
| 60 } else { |
| 61 uint64_t remaining = deadline - now; |
| 62 |
| 63 // Round to the nearest millisecond, taking care not to overflow. |
| 64 const int kHalfMillisecondInNanoseconds = kNanosecondsPerMillisecond / 2; |
| 65 if (remaining <= |
| 66 std::numeric_limits<uint64_t>::max() - kHalfMillisecondInNanoseconds) { |
| 67 *remaining_ms = (remaining + kHalfMillisecondInNanoseconds) / |
| 68 kNanosecondsPerMillisecond; |
| 69 } else { |
| 70 *remaining_ms = remaining / kNanosecondsPerMillisecond; |
| 71 } |
| 72 } |
| 73 |
| 74 return *remaining_ms != 0; |
| 75 } |
| 76 |
| 77 // This is an internal implementation detail of MachMessageWithDeadline(). It |
| 78 // determines whether |deadline| has expired, and what timeout value and |
| 79 // timeout-related options to pass to mach_msg() based on the value of |
| 80 // |deadline|. mach_msg() will only be called if TimerRunning() returns true or |
| 81 // if run_even_if_expired is true. |
| 82 mach_msg_return_t MachMessageWithDeadlineInternal(mach_msg_header_t* message, |
| 83 mach_msg_option_t options, |
| 84 mach_msg_size_t receive_size, |
| 85 mach_port_name_t receive_port, |
| 86 MachMessageDeadline deadline, |
| 87 mach_port_name_t notify_port, |
| 88 bool run_even_if_expired) { |
| 89 mach_msg_timeout_t remaining_ms; |
| 90 mach_msg_option_t timeout_options; |
| 91 if (!TimerRunning(deadline, &remaining_ms, &timeout_options) && |
| 92 !run_even_if_expired) { |
| 93 // Simulate the timed-out return values from mach_msg(). |
| 94 if (options & MACH_SEND_MSG) { |
| 95 return MACH_SEND_TIMED_OUT; |
| 96 } |
| 97 if (options & MACH_RCV_MSG) { |
| 98 return MACH_RCV_TIMED_OUT; |
| 99 } |
| 100 return MACH_MSG_SUCCESS; |
| 101 } |
| 102 |
| 103 // Turn off the passed-in timeout bits and replace them with the ones from |
| 104 // TimerRunning(). Get the send_size value from message->msgh_size if sending |
| 105 // a message. |
| 106 return mach_msg( |
| 107 message, |
| 108 (options & ~(MACH_SEND_TIMEOUT | MACH_RCV_TIMEOUT)) | timeout_options, |
| 109 options & MACH_SEND_MSG ? message->msgh_size : 0, |
| 110 receive_size, |
| 111 receive_port, |
| 112 remaining_ms, |
| 113 notify_port); |
| 114 } |
| 115 |
| 116 } // namespace |
| 117 |
| 118 MachMessageDeadline MachMessageDeadlineFromTimeout( |
| 119 mach_msg_timeout_t timeout_ms) { |
| 120 if (timeout_ms == 0) { |
| 121 return kMachMessageNonblocking; |
| 122 } |
| 123 |
| 124 return ClockMonotonicNanoseconds() + |
| 125 implicit_cast<uint64_t>(timeout_ms) * kNanosecondsPerMillisecond; |
| 126 } |
| 127 |
| 128 mach_msg_return_t MachMessageWithDeadline(mach_msg_header_t* message, |
| 129 mach_msg_option_t options, |
| 130 mach_msg_size_t receive_size, |
| 131 mach_port_name_t receive_port, |
| 132 MachMessageDeadline deadline, |
| 133 mach_port_name_t notify_port, |
| 134 bool run_even_if_expired) { |
| 135 // mach_msg() actaully does return MACH_MSG_SUCCESS when not asked to send or |
| 136 // receive anything. See 10.9.5 xnu-1504.15.3/osfmk/ipc/mach_msg.c |
| 137 // mach_msg_overwrite_trap(). |
| 138 mach_msg_return_t mr = MACH_MSG_SUCCESS; |
| 139 |
| 140 // Break up the send and receive into separate operations, so that the timeout |
| 141 // can be recomputed from the deadline for each. Otherwise, the computed |
| 142 // timeout will apply individually to the send and then to the receive, and |
| 143 // the desired deadline could be exceeded. |
| 144 // |
| 145 // During sends, always set MACH_SEND_INTERRUPT, and during receives, always |
| 146 // set MACH_RCV_INTERRUPT. If the caller didn’t specify these options, the |
| 147 // calls will be retried with a recomputed deadline. If these bits weren’t |
| 148 // set, the libsyscall wrapper (10.9.5 |
| 149 // xnu-2422.115.4/libsyscall/mach/mach_msg.c mach_msg() would restart |
| 150 // interrupted calls with the original timeout value computed from the |
| 151 // deadline, which would no longer correspond to the actual deadline. If the |
| 152 // caller did specify these bits, don’t restart anything, because the caller |
| 153 // wants to be notified of any interrupted calls. |
| 154 |
| 155 if (options & MACH_SEND_MSG) { |
| 156 do { |
| 157 mr = MachMessageWithDeadlineInternal( |
| 158 message, |
| 159 (options & ~MACH_RCV_MSG) | MACH_SEND_INTERRUPT, |
| 160 0, |
| 161 MACH_PORT_NULL, |
| 162 deadline, |
| 163 notify_port, |
| 164 run_even_if_expired); |
| 165 } while (mr == MACH_SEND_INTERRUPTED && !(options & MACH_SEND_INTERRUPT)); |
| 166 |
| 167 if (mr != MACH_MSG_SUCCESS) { |
| 168 return mr; |
| 169 } |
| 170 } |
| 171 |
| 172 if (options & MACH_RCV_MSG) { |
| 173 do { |
| 174 mr = MachMessageWithDeadlineInternal( |
| 175 message, |
| 176 (options & ~MACH_SEND_MSG) | MACH_RCV_INTERRUPT, |
| 177 receive_size, |
| 178 receive_port, |
| 179 deadline, |
| 180 notify_port, |
| 181 run_even_if_expired); |
| 182 } while (mr == MACH_RCV_INTERRUPTED && !(options & MACH_RCV_INTERRUPT)); |
| 183 } |
| 184 |
| 185 return mr; |
| 186 } |
| 187 |
19 void PrepareMIGReplyFromRequest(const mach_msg_header_t* in_header, | 188 void PrepareMIGReplyFromRequest(const mach_msg_header_t* in_header, |
20 mach_msg_header_t* out_header) { | 189 mach_msg_header_t* out_header) { |
21 out_header->msgh_bits = | 190 out_header->msgh_bits = |
22 MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(in_header->msgh_bits), 0); | 191 MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(in_header->msgh_bits), 0); |
23 out_header->msgh_remote_port = in_header->msgh_remote_port; | 192 out_header->msgh_remote_port = in_header->msgh_remote_port; |
24 out_header->msgh_size = sizeof(mig_reply_error_t); | 193 out_header->msgh_size = sizeof(mig_reply_error_t); |
25 out_header->msgh_local_port = MACH_PORT_NULL; | 194 out_header->msgh_local_port = MACH_PORT_NULL; |
26 out_header->msgh_id = in_header->msgh_id + 100; | 195 out_header->msgh_id = in_header->msgh_id + 100; |
27 reinterpret_cast<mig_reply_error_t*>(out_header)->NDR = NDR_record; | 196 reinterpret_cast<mig_reply_error_t*>(out_header)->NDR = NDR_record; |
28 | 197 |
29 // MIG-generated dispatch routines don’t do this, but they should. | 198 // MIG-generated dispatch routines don’t do this, but they should. |
30 out_header->msgh_reserved = 0; | 199 out_header->msgh_reserved = 0; |
31 } | 200 } |
32 | 201 |
33 void SetMIGReplyError(mach_msg_header_t* out_header, kern_return_t error) { | 202 void SetMIGReplyError(mach_msg_header_t* out_header, kern_return_t error) { |
34 reinterpret_cast<mig_reply_error_t*>(out_header)->RetCode = error; | 203 reinterpret_cast<mig_reply_error_t*>(out_header)->RetCode = error; |
35 } | 204 } |
36 | 205 |
37 const mach_msg_trailer_t* MachMessageTrailerFromHeader( | 206 const mach_msg_trailer_t* MachMessageTrailerFromHeader( |
38 const mach_msg_header_t* header) { | 207 const mach_msg_header_t* header) { |
39 vm_address_t header_address = reinterpret_cast<vm_address_t>(header); | 208 vm_address_t header_address = reinterpret_cast<vm_address_t>(header); |
40 vm_address_t trailer_address = header_address + round_msg(header->msgh_size); | 209 vm_address_t trailer_address = header_address + round_msg(header->msgh_size); |
41 return reinterpret_cast<const mach_msg_trailer_t*>(trailer_address); | 210 return reinterpret_cast<const mach_msg_trailer_t*>(trailer_address); |
42 } | 211 } |
43 | 212 |
44 } // namespace crashpad | 213 } // namespace crashpad |
OLD | NEW |