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 |