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