Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(263)

Side by Side Diff: util/mach/mach_message_server.cc

Issue 759023004: MachMessageServer: handle allocations more reasonably (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Address review feedback Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698