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 if (request_scoper.size() != this_request_alloc) { |
Robert Sesek
2014/12/01 20:59:17
Rather than equality, why not just if this_request
Mark Mentovai
2014/12/01 21:04:09
Robert Sesek wrote:
| |
145 kr = vm_allocate(mach_task_self(), | 150 // reset() first, so that two allocations don’t exist simultaneously. |
146 &request_addr, | 151 request_scoper.reset(); |
147 this_request_alloc, | 152 |
148 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); | 153 vm_address_t request_addr; |
149 if (kr != KERN_SUCCESS) { | 154 kr = vm_allocate(mach_task_self(), |
150 return kr; | 155 &request_addr, |
156 this_request_alloc, | |
157 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); | |
158 if (kr != KERN_SUCCESS) { | |
159 return kr; | |
160 } | |
161 | |
162 request_scoper.reset(request_addr, this_request_alloc); | |
151 } | 163 } |
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 | 164 |
156 bool run_mach_msg_receive = false; | 165 request_header = |
166 reinterpret_cast<mach_msg_header_t*>(request_scoper.address()); | |
167 | |
157 do { | 168 do { |
158 // If |options| contains MACH_RCV_INTERRUPT, retry mach_msg() in a loop | 169 // If |options| contains MACH_RCV_INTERRUPT, retry mach_msg() in a loop |
159 // when it returns MACH_RCV_INTERRUPTED to recompute |remaining_ms| | 170 // when it returns MACH_RCV_INTERRUPTED to recompute |remaining_ms| |
160 // rather than allowing mach_msg() to retry using the original timeout | 171 // 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 | 172 // value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c |
162 // mach_msg(). | 173 // mach_msg(). Don’t return early here if nothing has ever been |
174 // received: this method should always attempt to dequeue at least one | |
175 // message. | |
163 mach_msg_timeout_t remaining_ms; | 176 mach_msg_timeout_t remaining_ms; |
164 if (!TimerRunning(deadline, &remaining_ms)) { | 177 if (!TimerRunning(deadline, &remaining_ms) && received_any_request) { |
165 return MACH_RCV_TIMED_OUT; | 178 return MACH_RCV_TIMED_OUT; |
166 } | 179 } |
167 | 180 |
168 kr = mach_msg(request_header, | 181 kr = mach_msg(request_header, |
169 options | MACH_RCV_MSG, | 182 options | MACH_RCV_MSG, |
170 0, | 183 0, |
171 this_request_size, | 184 this_request_size, |
172 receive_port, | 185 receive_port, |
173 remaining_ms, | 186 remaining_ms, |
174 MACH_PORT_NULL); | 187 MACH_PORT_NULL); |
175 | 188 |
176 if (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeIgnore) { | 189 if (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeIgnore) { |
177 MACH_LOG(WARNING, kr) << "mach_msg: ignoring large message"; | 190 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 } | 191 } |
182 } while (run_mach_msg_receive); | 192 } while ( |
193 (kr == MACH_RCV_TOO_LARGE && receive_large == kReceiveLargeIgnore) || | |
194 kr == MACH_RCV_INTERRUPTED); | |
183 | 195 |
184 if (kr == MACH_MSG_SUCCESS) { | 196 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 = | 197 this_request_size = |
189 round_page(request_header->msgh_size + trailer_alloc); | 198 round_page(request_header->msgh_size + trailer_alloc); |
190 this_request_alloc = this_request_size; | 199 this_request_alloc = this_request_size; |
191 } else { | 200 } else if (kr != MACH_MSG_SUCCESS) { |
192 return kr; | 201 return kr; |
193 } | 202 } |
203 } while (kr != MACH_MSG_SUCCESS); | |
204 | |
205 received_any_request = true; | |
206 | |
207 if (reply_scoper.size() == 0) { | |
208 vm_address_t reply_addr; | |
209 kr = vm_allocate(mach_task_self(), | |
210 &reply_addr, | |
211 reply_alloc, | |
212 VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); | |
213 if (kr != KERN_SUCCESS) { | |
214 return kr; | |
215 } | |
216 | |
217 reply_scoper.reset(reply_addr, reply_alloc); | |
194 } | 218 } |
195 | 219 |
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 = | 220 mach_msg_header_t* reply_header = |
208 reinterpret_cast<mach_msg_header_t*>(reply_addr); | 221 reinterpret_cast<mach_msg_header_t*>(reply_scoper.address()); |
209 bool destroy_complex_request = false; | 222 bool destroy_complex_request = false; |
210 interface->MachMessageServerFunction( | 223 interface->MachMessageServerFunction( |
211 request_header, reply_header, &destroy_complex_request); | 224 request_header, reply_header, &destroy_complex_request); |
212 | 225 |
213 if (!(reply_header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) { | 226 if (!(reply_header->msgh_bits & MACH_MSGH_BITS_COMPLEX)) { |
214 // This only works if the reply message is not complex, because otherwise, | 227 // 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 | 228 // 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 | 229 // to locate the RetCode field by looking beyond the descriptors in a |
217 // complex reply message, but this is not currently done. This behavior | 230 // 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 | 231 // has not proven itself necessary in practice, and it’s not done by |
(...skipping 30 matching lines...) Expand all Loading... | |
249 | 262 |
250 bool running; | 263 bool running; |
251 do { | 264 do { |
252 // If |options| contains MACH_SEND_INTERRUPT, retry mach_msg() in a loop | 265 // If |options| contains MACH_SEND_INTERRUPT, retry mach_msg() in a loop |
253 // when it returns MACH_SEND_INTERRUPTED to recompute |remaining_ms| | 266 // when it returns MACH_SEND_INTERRUPTED to recompute |remaining_ms| |
254 // rather than allowing mach_msg() to retry using the original timeout | 267 // 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 | 268 // value. See 10.9.4 xnu-2422.110.17/libsyscall/mach/mach_msg.c |
256 // mach_msg(). | 269 // mach_msg(). |
257 mach_msg_timeout_t remaining_ms; | 270 mach_msg_timeout_t remaining_ms; |
258 running = TimerRunning(deadline, &remaining_ms); | 271 running = TimerRunning(deadline, &remaining_ms); |
259 if (!running) { | 272 |
260 // Don’t return just yet. If the timer ran out in between the time the | 273 // 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. | 274 // out in between the time the request was received and now, at least |
262 remaining_ms = 0; | 275 // try to send the response. |
263 } | |
264 | 276 |
265 kr = mach_msg(reply_header, | 277 kr = mach_msg(reply_header, |
266 send_options, | 278 send_options, |
267 reply_header->msgh_size, | 279 reply_header->msgh_size, |
268 0, | 280 0, |
269 MACH_PORT_NULL, | 281 MACH_PORT_NULL, |
270 remaining_ms, | 282 remaining_ms, |
271 MACH_PORT_NULL); | 283 MACH_PORT_NULL); |
272 } while (kr == MACH_SEND_INTERRUPTED); | 284 } while (kr == MACH_SEND_INTERRUPTED); |
273 | 285 |
(...skipping 10 matching lines...) Expand all Loading... | |
284 // persistent mode, and just return success when not in persistent mode. | 296 // persistent mode, and just return success when not in persistent mode. |
285 return (persistent == kPersistent) ? MACH_RCV_TIMED_OUT : kr; | 297 return (persistent == kPersistent) ? MACH_RCV_TIMED_OUT : kr; |
286 } | 298 } |
287 } | 299 } |
288 } while (persistent == kPersistent); | 300 } while (persistent == kPersistent); |
289 | 301 |
290 return kr; | 302 return kr; |
291 } | 303 } |
292 | 304 |
293 } // namespace crashpad | 305 } // namespace crashpad |
OLD | NEW |