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

Side by Side Diff: third_party/crashpad/crashpad/util/mach/notify_server_test.cc

Issue 1505213004: Copy Crashpad into the Chrome tree instead of importing it via DEPS (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address review comments, update README.chromium Created 5 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
OLDNEW
(Empty)
1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (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
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "util/mach/notify_server.h"
16
17 #include "base/compiler_specific.h"
18 #include "base/mac/scoped_mach_port.h"
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21 #include "test/mac/mach_errors.h"
22 #include "util/mach/mach_extensions.h"
23 #include "util/mach/mach_message.h"
24 #include "util/mach/mach_message_server.h"
25 #include "util/misc/implicit_cast.h"
26
27 namespace crashpad {
28 namespace test {
29 namespace {
30
31 using testing::AllOf;
32 using testing::Eq;
33 using testing::Invoke;
34 using testing::Pointee;
35 using testing::ResultOf;
36 using testing::Return;
37 using testing::SetArgPointee;
38 using testing::StrictMock;
39 using testing::WithArg;
40
41 //! \brief Adds a send right to an existing receive right.
42 //!
43 //! \param[in] receive_right The receive right to add a send right to.
44 //!
45 //! \return The send right, which will have the same name as the receive right.
46 //! On failure, `MACH_PORT_NULL` with a gtest failure added.
47 mach_port_t SendRightFromReceiveRight(mach_port_t receive_right) {
48 kern_return_t kr = mach_port_insert_right(
49 mach_task_self(), receive_right, receive_right, MACH_MSG_TYPE_MAKE_SEND);
50 if (kr != KERN_SUCCESS) {
51 EXPECT_EQ(KERN_SUCCESS, kr)
52 << MachErrorMessage(kr, "mach_port_insert_right");
53 return MACH_PORT_NULL;
54 }
55
56 return receive_right;
57 }
58
59 //! \brief Extracts a send-once right from a receive right.
60 //!
61 //! \param[in] receive_right The receive right to make a send-once right from.
62 //!
63 //! \return The send-once right. On failure, `MACH_PORT_NULL` with a gtest
64 //! failure added.
65 mach_port_t SendOnceRightFromReceiveRight(mach_port_t receive_right) {
66 mach_port_t send_once_right;
67 mach_msg_type_name_t acquired_type;
68 kern_return_t kr = mach_port_extract_right(mach_task_self(),
69 receive_right,
70 MACH_MSG_TYPE_MAKE_SEND_ONCE,
71 &send_once_right,
72 &acquired_type);
73 if (kr != KERN_SUCCESS) {
74 EXPECT_EQ(KERN_SUCCESS, kr)
75 << MachErrorMessage(kr, "mach_port_extract_right");
76 return MACH_PORT_NULL;
77 }
78
79 EXPECT_EQ(implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND_ONCE),
80 acquired_type);
81
82 return send_once_right;
83 }
84
85 //! \brief Deallocates a Mach port by calling `mach_port_deallocate()`.
86 //!
87 //! This function exists to adapt `mach_port_deallocate()` to a function that
88 //! accepts a single argument and has no return value. It can be used with the
89 //! testing::Invoke() gmock action.
90 //!
91 //! On failure, a gtest failure will be added.
92 void MachPortDeallocate(mach_port_t port) {
93 kern_return_t kr = mach_port_deallocate(mach_task_self(), port);
94 EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_deallocate");
95 }
96
97 //! \brief Determines whether a specific right is held for a Mach port.
98 //!
99 //! \param[in] port The port to check for a right.
100 //! \param[in] right The right to check for.
101 //!
102 //! \return `true` if \a port has \a right, `false` otherwise. On faliure,
103 //! `false` with a gtest failure added.
104 bool IsRight(mach_port_t port, mach_port_type_t right) {
105 mach_port_type_t type;
106 kern_return_t kr = mach_port_type(mach_task_self(), port, &type);
107 if (kr != KERN_SUCCESS) {
108 EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_type");
109 return false;
110 }
111
112 return type & right;
113 }
114
115 //! \brief Determines whether a receive right is held for a Mach port.
116 //!
117 //! This is a special single-argument form of IsRight() for ease of use in a
118 //! gmock matcher.
119 //!
120 //! \param[in] port The port to check for a receive right.
121 //!
122 //! \return `true` if a receive right is held, `false` otherwise. On faliure,
123 //! `false` with a gtest failure added.
124 bool IsReceiveRight(mach_port_t port) {
125 return IsRight(port, MACH_PORT_TYPE_RECEIVE);
126 }
127
128 //! \brief Returns the user reference count for port rights.
129 //!
130 //! \param[in] port The port whose user reference count should be returned.
131 //! \param[in] right The port right to return the user reference count for.
132 //!
133 //! \return The user reference count for the specified port and right. On
134 //! failure, `-1` with a gtest failure added.
135 mach_port_urefs_t RightRefCount(mach_port_t port, mach_port_right_t right) {
136 mach_port_urefs_t refs;
137 kern_return_t kr = mach_port_get_refs(mach_task_self(), port, right, &refs);
138 if (kr != KERN_SUCCESS) {
139 EXPECT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "mach_port_get_refs");
140 return -1;
141 }
142
143 return refs;
144 }
145
146 //! \brief Returns the user reference count for a port’s dead-name rights.
147 //!
148 //! This is a special single-argument form of RightRefCount() for ease of use in
149 //! a gmock matcher.
150 //!
151 //! \param[in] port The port whose dead-name user reference count should be
152 //! returned.
153 //!
154 //! \return The user reference count for the port’s dead-name rights. On
155 //! failure, `-1` with a gtest failure added.
156 mach_port_urefs_t DeadNameRightRefCount(mach_port_t port) {
157 return RightRefCount(port, MACH_PORT_RIGHT_DEAD_NAME);
158 }
159
160 class NotifyServerTestBase : public testing::Test,
161 public NotifyServer::Interface {
162 public:
163 // NotifyServer::Interface:
164
165 MOCK_METHOD3(DoMachNotifyPortDeleted,
166 kern_return_t(notify_port_t notify,
167 mach_port_name_t name,
168 const mach_msg_trailer_t* trailer));
169
170 MOCK_METHOD4(DoMachNotifyPortDestroyed,
171 kern_return_t(notify_port_t notify,
172 mach_port_t rights,
173 const mach_msg_trailer_t* trailer,
174 bool* destroy_request));
175
176 MOCK_METHOD3(DoMachNotifyNoSenders,
177 kern_return_t(notify_port_t notify,
178 mach_port_mscount_t mscount,
179 const mach_msg_trailer_t* trailer));
180
181 MOCK_METHOD2(DoMachNotifySendOnce,
182 kern_return_t(notify_port_t notify,
183 const mach_msg_trailer_t* trailer));
184
185 MOCK_METHOD3(DoMachNotifyDeadName,
186 kern_return_t(notify_port_t notify,
187 mach_port_name_t name,
188 const mach_msg_trailer_t* trailer));
189
190 protected:
191 NotifyServerTestBase() : testing::Test(), NotifyServer::Interface() {}
192
193 ~NotifyServerTestBase() override {}
194
195 //! \brief Requests a Mach port notification.
196 //!
197 //! \a name, \a variant, and \a sync are passed as-is to
198 //! `mach_port_request_notification()`. The notification will be sent to a
199 //! send-once right made from ServerPort(). Any previous send right for the
200 //! notification will be deallocated.
201 //!
202 //! \return `true` on success, `false` on failure with a gtest failure added.
203 bool RequestMachPortNotification(mach_port_t name,
204 mach_msg_id_t variant,
205 mach_port_mscount_t sync) {
206 mach_port_t previous;
207 kern_return_t kr =
208 mach_port_request_notification(mach_task_self(),
209 name,
210 variant,
211 sync,
212 ServerPort(),
213 MACH_MSG_TYPE_MAKE_SEND_ONCE,
214 &previous);
215 if (kr != KERN_SUCCESS) {
216 EXPECT_EQ(KERN_SUCCESS, kr)
217 << MachErrorMessage(kr, "mach_port_request_notification");
218 return false;
219 }
220
221 base::mac::ScopedMachSendRight previous_owner(previous);
222 EXPECT_EQ(kMachPortNull, previous);
223
224 return true;
225 }
226
227 //! \brief Runs a NotifyServer Mach message server.
228 //!
229 //! The server will listen on ServerPort() in persistent nonblocking mode, and
230 //! dispatch received messages to the appropriate NotifyServer::Interface
231 //! method. gmock expectations check that the proper method, if any, is called
232 //! exactly once, and that no undesired methods are called.
233 //!
234 //! MachMessageServer::Run() is expected to return `MACH_RCV_TIMED_OUT`,
235 //! because it runs in persistent nonblocking mode. If it returns anything
236 //! else, a gtest assertion is added.
237 void RunServer() {
238 NotifyServer notify_server(this);
239 mach_msg_return_t mr =
240 MachMessageServer::Run(&notify_server,
241 ServerPort(),
242 kMachMessageReceiveAuditTrailer,
243 MachMessageServer::kPersistent,
244 MachMessageServer::kReceiveLargeError,
245 kMachMessageTimeoutNonblocking);
246 ASSERT_EQ(MACH_RCV_TIMED_OUT, mr)
247 << MachErrorMessage(mr, "MachMessageServer::Run");
248 }
249
250 //! \brief Returns the receive right to be used for the server.
251 //!
252 //! This receive right is created lazily on a per-test basis. It is destroyed
253 //! by TearDown() at the conclusion of each test.
254 //!
255 //! \return The server port receive right, creating it if one has not yet been
256 //! established for the current test. On failure, returns `MACH_PORT_NULL`
257 //! with a gtest failure added.
258 mach_port_t ServerPort() {
259 if (!server_port_.is_valid()) {
260 server_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
261 EXPECT_TRUE(server_port_.is_valid());
262 }
263
264 return server_port_.get();
265 }
266
267 // testing::Test:
268 void TearDown() override {
269 server_port_.reset();
270 }
271
272 private:
273 base::mac::ScopedMachReceiveRight server_port_;
274
275 DISALLOW_COPY_AND_ASSIGN(NotifyServerTestBase);
276 };
277
278 using NotifyServerTest = StrictMock<NotifyServerTestBase>;
279
280 TEST_F(NotifyServerTest, Basic) {
281 NotifyServer server(this);
282
283 std::set<mach_msg_id_t> expect_request_ids;
284 expect_request_ids.insert(MACH_NOTIFY_PORT_DELETED);
285 expect_request_ids.insert(MACH_NOTIFY_PORT_DESTROYED);
286 expect_request_ids.insert(MACH_NOTIFY_NO_SENDERS);
287 expect_request_ids.insert(MACH_NOTIFY_SEND_ONCE);
288 expect_request_ids.insert(MACH_NOTIFY_DEAD_NAME);
289 EXPECT_EQ(expect_request_ids, server.MachMessageServerRequestIDs());
290
291 // The port-destroyed notification is the largest request message in the
292 // subsystem. <mach/notify.h> defines the same structure, but with a basic
293 // trailer, so use offsetof to get the size of the basic structure without any
294 // trailer.
295 EXPECT_EQ(offsetof(mach_port_destroyed_notification_t, trailer),
296 server.MachMessageServerRequestSize());
297
298 mig_reply_error_t reply;
299 EXPECT_EQ(sizeof(reply), server.MachMessageServerReplySize());
300 }
301
302 // When no notifications are requested, nothing should happen.
303 TEST_F(NotifyServerTest, NoNotification) {
304 RunServer();
305 }
306
307 // When a send-once right with a dead-name notification request is deallocated,
308 // a port-deleted notification should be generated.
309 TEST_F(NotifyServerTest, MachNotifyPortDeleted) {
310 base::mac::ScopedMachReceiveRight receive_right(
311 NewMachPort(MACH_PORT_RIGHT_RECEIVE));
312 ASSERT_TRUE(receive_right.is_valid());
313
314 base::mac::ScopedMachSendRight send_once_right(
315 SendOnceRightFromReceiveRight(receive_right.get()));
316 ASSERT_TRUE(send_once_right.is_valid());
317
318 ASSERT_TRUE(RequestMachPortNotification(
319 send_once_right.get(), MACH_NOTIFY_DEAD_NAME, 0));
320
321 EXPECT_CALL(
322 *this,
323 DoMachNotifyPortDeleted(ServerPort(),
324 send_once_right.get(),
325 ResultOf(AuditPIDFromMachMessageTrailer, 0)))
326 .WillOnce(Return(MIG_NO_REPLY))
327 .RetiresOnSaturation();
328
329 send_once_right.reset();
330
331 RunServer();
332 }
333
334 // When a receive right with a port-destroyed notification request is destroyed,
335 // a port-destroyed notification should be generated.
336 TEST_F(NotifyServerTest, MachNotifyPortDestroyed) {
337 base::mac::ScopedMachReceiveRight receive_right(
338 NewMachPort(MACH_PORT_RIGHT_RECEIVE));
339 ASSERT_TRUE(receive_right.is_valid());
340
341 ASSERT_TRUE(RequestMachPortNotification(
342 receive_right.get(), MACH_NOTIFY_PORT_DESTROYED, 0));
343
344 EXPECT_CALL(
345 *this,
346 DoMachNotifyPortDestroyed(ServerPort(),
347 ResultOf(IsReceiveRight, true),
348 ResultOf(AuditPIDFromMachMessageTrailer, 0),
349 Pointee(Eq(false))))
350 .WillOnce(DoAll(SetArgPointee<3>(true), Return(MIG_NO_REPLY)))
351 .RetiresOnSaturation();
352
353 receive_right.reset();
354
355 RunServer();
356 }
357
358 // When a receive right with a port-destroyed notification request is not
359 // destroyed, no port-destroyed notification should be generated.
360 TEST_F(NotifyServerTest, MachNotifyPortDestroyed_NoNotification) {
361 base::mac::ScopedMachReceiveRight receive_right(
362 NewMachPort(MACH_PORT_RIGHT_RECEIVE));
363 ASSERT_TRUE(receive_right.is_valid());
364
365 ASSERT_TRUE(RequestMachPortNotification(
366 receive_right.get(), MACH_NOTIFY_PORT_DESTROYED, 0));
367
368 RunServer();
369 }
370
371 // When a no-senders notification request is registered for a receive right with
372 // no senders, a no-senders notification should be generated.
373 TEST_F(NotifyServerTest, MachNotifyNoSenders_NoSendRight) {
374 base::mac::ScopedMachReceiveRight receive_right(
375 NewMachPort(MACH_PORT_RIGHT_RECEIVE));
376 ASSERT_TRUE(receive_right.is_valid());
377
378 ASSERT_TRUE(RequestMachPortNotification(
379 receive_right.get(), MACH_NOTIFY_NO_SENDERS, 0));
380
381 EXPECT_CALL(*this,
382 DoMachNotifyNoSenders(
383 ServerPort(), 0, ResultOf(AuditPIDFromMachMessageTrailer, 0)))
384 .WillOnce(Return(MIG_NO_REPLY))
385 .RetiresOnSaturation();
386
387 RunServer();
388 }
389
390 // When the last send right corresponding to a receive right with a no-senders
391 // notification request is deallocated, a no-senders notification should be
392 // generated.
393 TEST_F(NotifyServerTest, MachNotifyNoSenders_SendRightDeallocated) {
394 base::mac::ScopedMachReceiveRight receive_right(
395 NewMachPort(MACH_PORT_RIGHT_RECEIVE));
396 ASSERT_TRUE(receive_right.is_valid());
397
398 base::mac::ScopedMachSendRight send_right(
399 SendRightFromReceiveRight(receive_right.get()));
400 ASSERT_TRUE(send_right.is_valid());
401
402 ASSERT_TRUE(RequestMachPortNotification(
403 receive_right.get(), MACH_NOTIFY_NO_SENDERS, 1));
404
405 EXPECT_CALL(*this,
406 DoMachNotifyNoSenders(
407 ServerPort(), 1, ResultOf(AuditPIDFromMachMessageTrailer, 0)))
408 .WillOnce(Return(MIG_NO_REPLY))
409 .RetiresOnSaturation();
410
411 send_right.reset();
412
413 RunServer();
414 }
415
416 // When the a receive right with a no-senders notification request never loses
417 // all senders, no no-senders notification should be generated.
418 TEST_F(NotifyServerTest, MachNotifyNoSenders_NoNotification) {
419 base::mac::ScopedMachReceiveRight receive_right(
420 NewMachPort(MACH_PORT_RIGHT_RECEIVE));
421 ASSERT_TRUE(receive_right.is_valid());
422
423 base::mac::ScopedMachSendRight send_right_0(
424 SendRightFromReceiveRight(receive_right.get()));
425 ASSERT_TRUE(send_right_0.is_valid());
426
427 base::mac::ScopedMachSendRight send_right_1(
428 SendRightFromReceiveRight(receive_right.get()));
429 ASSERT_TRUE(send_right_1.is_valid());
430
431 ASSERT_TRUE(RequestMachPortNotification(
432 receive_right.get(), MACH_NOTIFY_NO_SENDERS, 1));
433
434 send_right_1.reset();
435
436 RunServer();
437
438 EXPECT_EQ(1u, RightRefCount(receive_right.get(), MACH_PORT_RIGHT_RECEIVE));
439 EXPECT_EQ(1u, RightRefCount(receive_right.get(), MACH_PORT_RIGHT_SEND));
440 }
441
442 // When a send-once right is deallocated without being used, a send-once
443 // notification notification should be sent via the send-once right.
444 TEST_F(NotifyServerTest, MachNotifySendOnce_ExplicitDeallocation) {
445 base::mac::ScopedMachSendRight send_once_right(
446 SendOnceRightFromReceiveRight(ServerPort()));
447 ASSERT_TRUE(send_once_right.is_valid());
448
449 EXPECT_CALL(*this,
450 DoMachNotifySendOnce(ServerPort(),
451 ResultOf(AuditPIDFromMachMessageTrailer, 0)))
452 .WillOnce(Return(MIG_NO_REPLY))
453 .RetiresOnSaturation();
454
455 send_once_right.reset();
456
457 RunServer();
458 }
459
460 // When a send-once right is sent to a receiver that never dequeues the message,
461 // the send-once right is destroyed, and a send-once notification should appear
462 // on the reply port.
463 TEST_F(NotifyServerTest, MachNotifySendOnce_ImplicitDeallocation) {
464 base::mac::ScopedMachReceiveRight receive_right(
465 NewMachPort(MACH_PORT_RIGHT_RECEIVE));
466 ASSERT_TRUE(receive_right.is_valid());
467
468 mach_msg_empty_send_t message = {};
469 message.header.msgh_bits =
470 MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
471 message.header.msgh_size = sizeof(message);
472 message.header.msgh_remote_port = receive_right.get();
473 message.header.msgh_local_port = ServerPort();
474 mach_msg_return_t mr = mach_msg(&message.header,
475 MACH_SEND_MSG | MACH_SEND_TIMEOUT,
476 message.header.msgh_size,
477 0,
478 MACH_PORT_NULL,
479 0,
480 MACH_PORT_NULL);
481 ASSERT_EQ(MACH_MSG_SUCCESS, mr) << MachErrorMessage(mr, "mach_msg");
482
483 EXPECT_CALL(*this,
484 DoMachNotifySendOnce(ServerPort(),
485 ResultOf(AuditPIDFromMachMessageTrailer, 0)))
486 .WillOnce(Return(MIG_NO_REPLY))
487 .RetiresOnSaturation();
488
489 receive_right.reset();
490
491 RunServer();
492 }
493
494 // When the receive right corresponding to a send-once right with a dead-name
495 // notification request is destroyed, a dead-name notification should be
496 // generated.
497 TEST_F(NotifyServerTest, MachNotifyDeadName) {
498 base::mac::ScopedMachReceiveRight receive_right(
499 NewMachPort(MACH_PORT_RIGHT_RECEIVE));
500 ASSERT_TRUE(receive_right.is_valid());
501
502 base::mac::ScopedMachSendRight send_once_right(
503 SendOnceRightFromReceiveRight(receive_right.get()));
504 ASSERT_TRUE(send_once_right.is_valid());
505
506 ASSERT_TRUE(RequestMachPortNotification(
507 send_once_right.get(), MACH_NOTIFY_DEAD_NAME, 0));
508
509 // send_once_right becomes a dead name with the send-once right’s original
510 // user reference count of 1, but the dead-name notification increments the
511 // dead-name reference count, so it becomes 2. Take care to deallocate that
512 // reference. The original reference is managed by send_once_right_owner.
513 EXPECT_CALL(*this,
514 DoMachNotifyDeadName(ServerPort(),
515 AllOf(send_once_right.get(),
516 ResultOf(DeadNameRightRefCount, 2)),
517 ResultOf(AuditPIDFromMachMessageTrailer, 0)))
518 .WillOnce(
519 DoAll(WithArg<1>(Invoke(MachPortDeallocate)), Return(MIG_NO_REPLY)))
520 .RetiresOnSaturation();
521
522 receive_right.reset();
523
524 RunServer();
525
526 EXPECT_TRUE(IsRight(send_once_right.get(), MACH_PORT_TYPE_DEAD_NAME));
527
528 EXPECT_EQ(0u,
529 RightRefCount(send_once_right.get(), MACH_PORT_RIGHT_SEND_ONCE));
530 EXPECT_EQ(1u, DeadNameRightRefCount(send_once_right.get()));
531 }
532
533 // When the receive right corresponding to a send-once right with a dead-name
534 // notification request is not destroyed, no dead-name notification should be
535 // generated.
536 TEST_F(NotifyServerTest, MachNotifyDeadName_NoNotification) {
537 base::mac::ScopedMachReceiveRight receive_right(
538 NewMachPort(MACH_PORT_RIGHT_RECEIVE));
539 ASSERT_TRUE(receive_right.is_valid());
540
541 base::mac::ScopedMachSendRight send_once_right(
542 SendOnceRightFromReceiveRight(receive_right.get()));
543 ASSERT_TRUE(send_once_right.is_valid());
544
545 ASSERT_TRUE(RequestMachPortNotification(
546 send_once_right.get(), MACH_NOTIFY_DEAD_NAME, 0));
547
548 RunServer();
549
550 EXPECT_FALSE(IsRight(send_once_right.get(), MACH_PORT_TYPE_DEAD_NAME));
551
552 EXPECT_EQ(1u,
553 RightRefCount(send_once_right.get(), MACH_PORT_RIGHT_SEND_ONCE));
554 EXPECT_EQ(0u, DeadNameRightRefCount(send_once_right.get()));
555 }
556
557 } // namespace
558 } // namespace test
559 } // namespace crashpad
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698