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/mach_extensions.h"
16 
17 #include "base/mac/scoped_mach_port.h"
18 #include "gtest/gtest.h"
19 #include "test/mac/mach_errors.h"
20 #include "util/mac/mac_util.h"
21 #include "util/misc/random_string.h"
22 
23 namespace crashpad {
24 namespace test {
25 namespace {
26 
TEST(MachExtensions,MachThreadSelf)27 TEST(MachExtensions, MachThreadSelf) {
28   base::mac::ScopedMachSendRight thread_self(mach_thread_self());
29   EXPECT_EQ(MachThreadSelf(), thread_self);
30 }
31 
TEST(MachExtensions,NewMachPort_Receive)32 TEST(MachExtensions, NewMachPort_Receive) {
33   base::mac::ScopedMachReceiveRight port(NewMachPort(MACH_PORT_RIGHT_RECEIVE));
34   ASSERT_NE(port, kMachPortNull);
35 
36   mach_port_type_t type;
37   kern_return_t kr = mach_port_type(mach_task_self(), port.get(), &type);
38   ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "mach_port_get_type");
39 
40   EXPECT_EQ(type, MACH_PORT_TYPE_RECEIVE);
41 }
42 
TEST(MachExtensions,NewMachPort_PortSet)43 TEST(MachExtensions, NewMachPort_PortSet) {
44   base::mac::ScopedMachPortSet port(NewMachPort(MACH_PORT_RIGHT_PORT_SET));
45   ASSERT_NE(port, kMachPortNull);
46 
47   mach_port_type_t type;
48   kern_return_t kr = mach_port_type(mach_task_self(), port.get(), &type);
49   ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "mach_port_get_type");
50 
51   EXPECT_EQ(type, MACH_PORT_TYPE_PORT_SET);
52 }
53 
TEST(MachExtensions,NewMachPort_DeadName)54 TEST(MachExtensions, NewMachPort_DeadName) {
55   base::mac::ScopedMachSendRight port(NewMachPort(MACH_PORT_RIGHT_DEAD_NAME));
56   ASSERT_NE(port, kMachPortNull);
57 
58   mach_port_type_t type;
59   kern_return_t kr = mach_port_type(mach_task_self(), port.get(), &type);
60   ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "mach_port_get_type");
61 
62   EXPECT_EQ(type, MACH_PORT_TYPE_DEAD_NAME);
63 }
64 
65 constexpr exception_mask_t kExcMaskBasic =
66     EXC_MASK_BAD_ACCESS |
67     EXC_MASK_BAD_INSTRUCTION |
68     EXC_MASK_ARITHMETIC |
69     EXC_MASK_EMULATION |
70     EXC_MASK_SOFTWARE |
71     EXC_MASK_BREAKPOINT |
72     EXC_MASK_SYSCALL |
73     EXC_MASK_MACH_SYSCALL |
74     EXC_MASK_RPC_ALERT;
75 
TEST(MachExtensions,ExcMaskAll)76 TEST(MachExtensions, ExcMaskAll) {
77   const exception_mask_t exc_mask_all = ExcMaskAll();
78   EXPECT_EQ(exc_mask_all & kExcMaskBasic, kExcMaskBasic);
79 
80   EXPECT_FALSE(exc_mask_all & EXC_MASK_CRASH);
81   EXPECT_FALSE(exc_mask_all & EXC_MASK_CORPSE_NOTIFY);
82 
83   const int mac_os_x_minor_version = MacOSXMinorVersion();
84   if (mac_os_x_minor_version >= 8) {
85     EXPECT_TRUE(exc_mask_all & EXC_MASK_RESOURCE);
86   } else {
87     EXPECT_FALSE(exc_mask_all & EXC_MASK_RESOURCE);
88   }
89 
90   if (mac_os_x_minor_version >= 9) {
91     EXPECT_TRUE(exc_mask_all & EXC_MASK_GUARD);
92   } else {
93     EXPECT_FALSE(exc_mask_all & EXC_MASK_GUARD);
94   }
95 
96   // Bit 0 should not be set.
97   EXPECT_FALSE(ExcMaskAll() & 1);
98 
99   // Every bit set in ExcMaskAll() must also be set in ExcMaskValid().
100   EXPECT_EQ(ExcMaskAll() & ExcMaskValid(), ExcMaskAll());
101 }
102 
TEST(MachExtensions,ExcMaskValid)103 TEST(MachExtensions, ExcMaskValid) {
104   const exception_mask_t exc_mask_valid = ExcMaskValid();
105   EXPECT_EQ(exc_mask_valid & kExcMaskBasic, kExcMaskBasic);
106 
107   EXPECT_TRUE(exc_mask_valid & EXC_MASK_CRASH);
108 
109   const int mac_os_x_minor_version = MacOSXMinorVersion();
110   if (mac_os_x_minor_version >= 8) {
111     EXPECT_TRUE(exc_mask_valid & EXC_MASK_RESOURCE);
112   } else {
113     EXPECT_FALSE(exc_mask_valid & EXC_MASK_RESOURCE);
114   }
115 
116   if (mac_os_x_minor_version >= 9) {
117     EXPECT_TRUE(exc_mask_valid & EXC_MASK_GUARD);
118   } else {
119     EXPECT_FALSE(exc_mask_valid & EXC_MASK_GUARD);
120   }
121 
122   if (mac_os_x_minor_version >= 11) {
123     EXPECT_TRUE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY);
124   } else {
125     EXPECT_FALSE(exc_mask_valid & EXC_MASK_CORPSE_NOTIFY);
126   }
127 
128   // Bit 0 should not be set.
129   EXPECT_FALSE(ExcMaskValid() & 1);
130 
131   // There must be bits set in ExcMaskValid() that are not set in ExcMaskAll().
132   EXPECT_TRUE(ExcMaskValid() & ~ExcMaskAll());
133 }
134 
TEST(MachExtensions,BootstrapCheckInAndLookUp)135 TEST(MachExtensions, BootstrapCheckInAndLookUp) {
136   // This should always exist.
137   base::mac::ScopedMachSendRight
138       report_crash(BootstrapLookUp("com.apple.ReportCrash"));
139   EXPECT_NE(report_crash, kMachPortNull);
140 
141   std::string service_name = "org.chromium.crashpad.test.bootstrap_check_in.";
142   service_name.append(RandomString());
143 
144   {
145     // The new service hasn’t checked in yet, so this should fail.
146     base::mac::ScopedMachSendRight send(BootstrapLookUp(service_name));
147     EXPECT_EQ(send, kMachPortNull);
148 
149     // Check it in.
150     base::mac::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name));
151     EXPECT_NE(receive, kMachPortNull);
152 
153     // Now it should be possible to look up the new service.
154     send = BootstrapLookUp(service_name);
155     EXPECT_NE(send, kMachPortNull);
156 
157     // It shouldn’t be possible to check the service in while it’s active.
158     base::mac::ScopedMachReceiveRight receive_2(BootstrapCheckIn(service_name));
159     EXPECT_EQ(receive_2, kMachPortNull);
160   }
161 
162   // The new service should be gone now.
163   base::mac::ScopedMachSendRight send(BootstrapLookUp(service_name));
164   EXPECT_EQ(send, kMachPortNull);
165 
166   // It should be possible to check it in again.
167   base::mac::ScopedMachReceiveRight receive(BootstrapCheckIn(service_name));
168   EXPECT_NE(receive, kMachPortNull);
169 }
170 
TEST(MachExtensions,SystemCrashReporterHandler)171 TEST(MachExtensions, SystemCrashReporterHandler) {
172   base::mac::ScopedMachSendRight
173       system_crash_reporter_handler(SystemCrashReporterHandler());
174   EXPECT_TRUE(system_crash_reporter_handler.is_valid());
175 }
176 
177 }  // namespace
178 }  // namespace test
179 }  // namespace crashpad
180