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, |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
13 // limitations under the License. | 13 // limitations under the License. |
14 | 14 |
15 #include "util/mach/child_port_handshake.h" | 15 #include "util/mach/child_port_handshake.h" |
16 | 16 |
17 #include "base/mac/scoped_mach_port.h" | 17 #include "base/mac/scoped_mach_port.h" |
18 #include "gtest/gtest.h" | 18 #include "gtest/gtest.h" |
19 #include "test/multiprocess.h" | 19 #include "test/multiprocess.h" |
20 #include "util/mach/child_port_types.h" | 20 #include "util/mach/child_port_types.h" |
21 #include "util/mach/mach_extensions.h" | 21 #include "util/mach/mach_extensions.h" |
22 | 22 |
23 namespace crashpad { | 23 namespace crashpad { |
24 namespace test { | 24 namespace test { |
25 namespace { | 25 namespace { |
26 | 26 |
27 class ChildPortHandshakeTest : public Multiprocess { | 27 class ChildPortHandshakeTest : public Multiprocess { |
28 public: | 28 public: |
29 enum TestType { | 29 enum class ClientProcess { |
30 kTestTypeChildChecksIn = 0, | 30 // The child runs the client and the parent runs the server. |
31 kTestTypeChildDoesNotCheckIn_ReadsPipe, | 31 kChildClient = 0, |
32 kTestTypeChildDoesNotCheckIn, | 32 |
33 kTestTypeTokenIncorrect, | 33 // The parent runs the client and the child runs the server. |
34 kTestTypeTokenIncorrectThenCorrect, | 34 kParentClient, |
35 }; | 35 }; |
36 | 36 |
37 explicit ChildPortHandshakeTest(TestType test_type) | 37 enum class TestType { |
38 : Multiprocess(), child_port_handshake_(), test_type_(test_type) {} | 38 // The client checks in with the server, transferring a receive right. |
39 ~ChildPortHandshakeTest() {} | 39 kClientChecksIn_ReceiveRight = 0, |
40 | |
41 // In this test, the client checks in with the server normally. It sends a | |
42 // copy of its bootstrap port to the server, because both parent and child | |
43 // should have the same bootstrap port, allowing for verification. | |
44 kClientChecksIn_SendRight, | |
45 | |
46 // The client checks in with the server, transferring a send-once right. | |
47 kClientChecksIn_SendOnceRight, | |
48 | |
49 // In this test, the client reads from its pipe, and subsequently exits | |
50 // without checking in. This tests that the server properly detects that it | |
51 // has lost its client after sending instructions to it via the pipe, while | |
52 // waiting for a check-in message. | |
53 kClientDoesNotCheckIn, | |
Robert Sesek
2015/10/28 21:40:03
Worth having a test for if the server dies and the
| |
54 | |
55 // In this test, the client exits without checking in. This tests that the | |
56 // server properly detects that it has lost a client. Whether or not the | |
57 // client closes the pipe before the server writes to it is a race, and the | |
58 // server needs to be able to detect client loss in both cases, so the | |
59 // ClientDoesNotCheckIn_ReadsPipe and NoClient tests also exist to test | |
60 // these individual cases more deterministically. | |
61 kClientDoesNotCheckIn_ReadsPipe, | |
62 | |
63 // In this test, the client checks in with the server with an incorrect | |
64 // token value and a copy of its own task port. The server should reject the | |
65 // message because of the invalid token, and return MACH_PORT_NULL to its | |
66 // caller. | |
67 kTokenIncorrect, | |
68 | |
69 // In this test, the client checks in with the server with an incorrect | |
70 // token value and a copy of its own task port, and subsequently, the | |
71 // correct token value and a copy of its bootstrap port. The server should | |
72 // reject the first because of the invalid token, but it should continue | |
73 // waiting for a message with a valid token as long as the pipe remains | |
74 // open. It should wind wind up returning the bootstrap port, allowing for | |
75 // verification. | |
76 kTokenIncorrectThenCorrect, | |
77 }; | |
78 | |
79 ChildPortHandshakeTest(ClientProcess client_process, TestType test_type) | |
80 : Multiprocess(), | |
81 child_port_handshake_(), | |
82 client_process_(client_process), | |
83 test_type_(test_type) { | |
84 } | |
85 | |
86 ~ChildPortHandshakeTest() { | |
87 } | |
40 | 88 |
41 private: | 89 private: |
42 // Multiprocess: | 90 void RunServer() { |
43 | 91 base::mac::ScopedMachReceiveRight receive_right; |
44 void MultiprocessParent() override { | 92 base::mac::ScopedMachSendRight send_right; |
45 base::mac::ScopedMachSendRight child_port( | 93 if (test_type_ == TestType::kClientChecksIn_ReceiveRight) { |
46 child_port_handshake_.RunServer()); | 94 receive_right.reset(child_port_handshake_.RunServer( |
95 ChildPortHandshake::PortRightType::kReceiveRight)); | |
96 } else { | |
97 send_right.reset(child_port_handshake_.RunServer( | |
98 ChildPortHandshake::PortRightType::kSendRight)); | |
99 } | |
100 | |
47 switch (test_type_) { | 101 switch (test_type_) { |
48 case kTestTypeChildChecksIn: | 102 case TestType::kClientChecksIn_ReceiveRight: |
49 case kTestTypeTokenIncorrectThenCorrect: | 103 EXPECT_TRUE(receive_right.is_valid()); |
50 EXPECT_EQ(bootstrap_port, child_port); | 104 break; |
51 break; | 105 |
52 | 106 case TestType::kClientChecksIn_SendRight: |
53 case kTestTypeChildDoesNotCheckIn_ReadsPipe: | 107 case TestType::kTokenIncorrectThenCorrect: |
54 case kTestTypeChildDoesNotCheckIn: | 108 EXPECT_EQ(bootstrap_port, send_right); |
55 case kTestTypeTokenIncorrect: | 109 break; |
56 EXPECT_EQ(kMachPortNull, child_port); | 110 |
57 break; | 111 case TestType::kClientChecksIn_SendOnceRight: |
58 } | 112 EXPECT_TRUE(send_right.is_valid()); |
59 } | 113 EXPECT_NE(bootstrap_port, send_right); |
60 | 114 break; |
61 void MultiprocessChild() override { | 115 |
62 int read_pipe = child_port_handshake_.ReadPipeFD(); | 116 case TestType::kClientDoesNotCheckIn: |
117 case TestType::kClientDoesNotCheckIn_ReadsPipe: | |
118 case TestType::kTokenIncorrect: | |
119 EXPECT_FALSE(send_right.is_valid()); | |
120 break; | |
121 } | |
122 } | |
123 | |
124 void RunClient() { | |
63 switch (test_type_) { | 125 switch (test_type_) { |
64 case kTestTypeChildChecksIn: | 126 case TestType::kClientChecksIn_SendRight: { |
65 ChildPortHandshake::RunClient( | 127 ASSERT_TRUE(child_port_handshake_.RunClient(bootstrap_port, |
66 read_pipe, bootstrap_port, MACH_MSG_TYPE_COPY_SEND); | 128 MACH_MSG_TYPE_COPY_SEND)); |
67 break; | 129 break; |
68 | 130 } |
69 case kTestTypeChildDoesNotCheckIn_ReadsPipe: { | 131 |
132 case TestType::kClientChecksIn_ReceiveRight: { | |
133 mach_port_t receive_right = NewMachPort(MACH_PORT_RIGHT_RECEIVE); | |
134 ASSERT_TRUE(child_port_handshake_.RunClient( | |
135 receive_right, MACH_MSG_TYPE_MOVE_RECEIVE)); | |
136 break; | |
137 } | |
138 | |
139 case TestType::kClientChecksIn_SendOnceRight: { | |
140 base::mac::ScopedMachReceiveRight receive_right( | |
141 NewMachPort(MACH_PORT_RIGHT_RECEIVE)); | |
142 ASSERT_TRUE(child_port_handshake_.RunClient( | |
143 receive_right.get(), MACH_MSG_TYPE_MAKE_SEND_ONCE)); | |
144 break; | |
145 } | |
146 | |
147 case TestType::kClientDoesNotCheckIn: { | |
148 child_port_handshake_.ServerWriteFD().reset(); | |
149 child_port_handshake_.ClientReadFD().reset(); | |
150 break; | |
151 } | |
152 | |
153 case TestType::kClientDoesNotCheckIn_ReadsPipe: { | |
70 // Don’t run the standard client routine. Instead, drain the pipe, which | 154 // Don’t run the standard client routine. Instead, drain the pipe, which |
71 // will get the parent to the point that it begins waiting for a | 155 // will get the parent to the point that it begins waiting for a |
72 // check-in message. Then, exit. The pipe is drained using the same | 156 // check-in message. Then, exit. The pipe is drained using the same |
73 // implementation that the real client would use. | 157 // implementation that the real client would use. |
158 child_port_handshake_.ServerWriteFD().reset(); | |
159 base::ScopedFD client_read_fd = child_port_handshake_.ClientReadFD(); | |
74 child_port_token_t token; | 160 child_port_token_t token; |
75 std::string service_name; | 161 std::string service_name; |
76 ChildPortHandshake::RunClientInternal_ReadPipe( | 162 ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe( |
77 read_pipe, &token, &service_name); | 163 client_read_fd.get(), &token, &service_name)); |
78 break; | 164 break; |
79 } | 165 } |
80 | 166 |
81 case kTestTypeChildDoesNotCheckIn: | 167 case TestType::kTokenIncorrect: { |
82 break; | |
83 | |
84 case kTestTypeTokenIncorrect: { | |
85 // Don’t run the standard client routine. Instead, read the token and | 168 // Don’t run the standard client routine. Instead, read the token and |
86 // service name, mutate the token, and then check in with the bad token. | 169 // service name, mutate the token, and then check in with the bad token. |
87 // The parent should reject the message. | 170 // The parent should reject the message. |
171 child_port_handshake_.ServerWriteFD().reset(); | |
172 base::ScopedFD client_read_fd = child_port_handshake_.ClientReadFD(); | |
88 child_port_token_t token; | 173 child_port_token_t token; |
89 std::string service_name; | 174 std::string service_name; |
90 ChildPortHandshake::RunClientInternal_ReadPipe( | 175 ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe( |
91 read_pipe, &token, &service_name); | 176 client_read_fd.get(), &token, &service_name)); |
92 child_port_token_t bad_token = ~token; | 177 child_port_token_t bad_token = ~token; |
93 ChildPortHandshake::RunClientInternal_SendCheckIn( | 178 ASSERT_TRUE(ChildPortHandshake::RunClientInternal_SendCheckIn( |
94 service_name, bad_token, mach_task_self(), MACH_MSG_TYPE_COPY_SEND); | 179 service_name, |
95 break; | 180 bad_token, |
96 } | 181 mach_task_self(), |
97 | 182 MACH_MSG_TYPE_COPY_SEND)); |
98 case kTestTypeTokenIncorrectThenCorrect: { | 183 break; |
184 } | |
185 | |
186 case TestType::kTokenIncorrectThenCorrect: { | |
99 // Don’t run the standard client routine. Instead, read the token and | 187 // Don’t run the standard client routine. Instead, read the token and |
100 // service name. Mutate the token, and check in with the bad token, | 188 // service name. Mutate the token, and check in with the bad token, |
101 // expecting the parent to reject the message. Then, check in with the | 189 // expecting the parent to reject the message. Then, check in with the |
102 // correct token, expecting the parent to accept it. | 190 // correct token, expecting the parent to accept it. |
191 child_port_handshake_.ServerWriteFD().reset(); | |
192 base::ScopedFD client_read_fd = child_port_handshake_.ClientReadFD(); | |
103 child_port_token_t token; | 193 child_port_token_t token; |
104 std::string service_name; | 194 std::string service_name; |
105 ChildPortHandshake::RunClientInternal_ReadPipe( | 195 ASSERT_TRUE(ChildPortHandshake::RunClientInternal_ReadPipe( |
106 read_pipe, &token, &service_name); | 196 client_read_fd.release(), &token, &service_name)); |
107 child_port_token_t bad_token = ~token; | 197 child_port_token_t bad_token = ~token; |
108 ChildPortHandshake::RunClientInternal_SendCheckIn( | 198 ASSERT_TRUE(ChildPortHandshake::RunClientInternal_SendCheckIn( |
109 service_name, bad_token, mach_task_self(), MACH_MSG_TYPE_COPY_SEND); | 199 service_name, |
110 ChildPortHandshake::RunClientInternal_SendCheckIn( | 200 bad_token, |
111 service_name, token, bootstrap_port, MACH_MSG_TYPE_COPY_SEND); | 201 mach_task_self(), |
112 break; | 202 MACH_MSG_TYPE_COPY_SEND)); |
113 } | 203 ASSERT_TRUE(ChildPortHandshake::RunClientInternal_SendCheckIn( |
204 service_name, token, bootstrap_port, MACH_MSG_TYPE_COPY_SEND)); | |
205 break; | |
206 } | |
207 } | |
208 } | |
209 | |
210 // Multiprocess: | |
211 | |
212 void MultiprocessParent() override { | |
213 switch (client_process_) { | |
214 case ClientProcess::kChildClient: | |
215 RunServer(); | |
216 break; | |
217 case ClientProcess::kParentClient: | |
218 RunClient(); | |
219 break; | |
220 } | |
221 } | |
222 | |
223 void MultiprocessChild() override { | |
224 switch (client_process_) { | |
225 case ClientProcess::kChildClient: | |
226 RunClient(); | |
227 break; | |
228 case ClientProcess::kParentClient: | |
229 RunServer(); | |
230 break; | |
114 } | 231 } |
115 } | 232 } |
116 | 233 |
117 private: | 234 private: |
118 ChildPortHandshake child_port_handshake_; | 235 ChildPortHandshake child_port_handshake_; |
236 ClientProcess client_process_; | |
119 TestType test_type_; | 237 TestType test_type_; |
120 | 238 |
121 DISALLOW_COPY_AND_ASSIGN(ChildPortHandshakeTest); | 239 DISALLOW_COPY_AND_ASSIGN(ChildPortHandshakeTest); |
122 }; | 240 }; |
123 | 241 |
124 TEST(ChildPortHandshake, ChildChecksIn) { | 242 TEST(ChildPortHandshake, ChildClientChecksIn_ReceiveRight) { |
125 // In this test, the client checks in with the server normally. It sends a | 243 ChildPortHandshakeTest test( |
126 // copy of its bootstrap port to the server, because both parent and child | 244 ChildPortHandshakeTest::ClientProcess::kChildClient, |
127 // should have the same bootstrap port, allowing for verification. | 245 ChildPortHandshakeTest::TestType::kClientChecksIn_ReceiveRight); |
128 ChildPortHandshakeTest test(ChildPortHandshakeTest::kTestTypeChildChecksIn); | 246 test.Run(); |
129 test.Run(); | 247 } |
130 } | 248 |
131 | 249 TEST(ChildPortHandshake, ChildClientChecksIn_SendRight) { |
132 TEST(ChildPortHandshake, ChildDoesNotCheckIn) { | 250 ChildPortHandshakeTest test( |
133 // In this test, the client exits without checking in. This tests that the | 251 ChildPortHandshakeTest::ClientProcess::kChildClient, |
134 // server properly detects that it has lost a client. Whether or not the | 252 ChildPortHandshakeTest::TestType::kClientChecksIn_SendRight); |
135 // client closes the pipe before the server writes to it is a race, and the | 253 test.Run(); |
136 // server needs to be able to detect client loss in both cases, so the | 254 } |
137 // ChildDoesNotCheckIn_ReadsPipe and NoChild tests also exist to test these | 255 |
138 // individual cases more deterministically. | 256 TEST(ChildPortHandshake, ChildClientChecksIn_SendOnceRight) { |
139 ChildPortHandshakeTest test( | 257 ChildPortHandshakeTest test( |
140 ChildPortHandshakeTest::kTestTypeChildDoesNotCheckIn); | 258 ChildPortHandshakeTest::ClientProcess::kChildClient, |
141 test.Run(); | 259 ChildPortHandshakeTest::TestType::kClientChecksIn_SendOnceRight); |
142 } | 260 test.Run(); |
143 | 261 } |
144 TEST(ChildPortHandshake, ChildDoesNotCheckIn_ReadsPipe) { | 262 |
145 // In this test, the client reads from its pipe, and subsequently exits | 263 TEST(ChildPortHandshake, ChildClientDoesNotCheckIn) { |
146 // without checking in. This tests that the server properly detects that it | 264 ChildPortHandshakeTest test( |
147 // has lost its client after sending instructions to it via the pipe, while | 265 ChildPortHandshakeTest::ClientProcess::kChildClient, |
148 // waiting for a check-in message. | 266 ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn); |
149 ChildPortHandshakeTest test( | 267 test.Run(); |
150 ChildPortHandshakeTest::kTestTypeChildDoesNotCheckIn_ReadsPipe); | 268 } |
151 test.Run(); | 269 |
152 } | 270 TEST(ChildPortHandshake, ChildClientDoesNotCheckIn_ReadsPipe) { |
153 | 271 ChildPortHandshakeTest test( |
154 TEST(ChildPortHandshake, TokenIncorrect) { | 272 ChildPortHandshakeTest::ClientProcess::kChildClient, |
155 // In this test, the client checks in with the server with an incorrect token | 273 ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn_ReadsPipe); |
156 // value and a copy of its own task port. The server should reject the message | 274 test.Run(); |
157 // because of the invalid token, and return MACH_PORT_NULL to its caller. | 275 } |
158 ChildPortHandshakeTest test(ChildPortHandshakeTest::kTestTypeTokenIncorrect); | 276 |
159 test.Run(); | 277 TEST(ChildPortHandshake, ChildClientTokenIncorrect) { |
160 } | 278 ChildPortHandshakeTest test( |
161 | 279 ChildPortHandshakeTest::ClientProcess::kChildClient, |
162 TEST(ChildPortHandshake, TokenIncorrectThenCorrect) { | 280 ChildPortHandshakeTest::TestType::kTokenIncorrect); |
163 // In this test, the client checks in with the server with an incorrect token | 281 test.Run(); |
164 // value and a copy of its own task port, and subsequently, the correct token | 282 } |
165 // value and a copy of its bootstrap port. The server should reject the first | 283 |
166 // because of the invalid token, but it should continue waiting for a message | 284 TEST(ChildPortHandshake, ChildClientTokenIncorrectThenCorrect) { |
167 // with a valid token as long as the pipe remains open. It should wind wind up | 285 ChildPortHandshakeTest test( |
168 // returning the bootstrap port, allowing for verification. | 286 ChildPortHandshakeTest::ClientProcess::kChildClient, |
169 ChildPortHandshakeTest test( | 287 ChildPortHandshakeTest::TestType::kTokenIncorrectThenCorrect); |
170 ChildPortHandshakeTest::kTestTypeTokenIncorrectThenCorrect); | 288 test.Run(); |
171 test.Run(); | 289 } |
172 } | 290 |
173 | 291 TEST(ChildPortHandshake, ParentClientChecksIn_ReceiveRight) { |
174 TEST(ChildPortHandshake, NoChild) { | 292 ChildPortHandshakeTest test( |
175 // In this test, the client never checks in with the parent because the child | 293 ChildPortHandshakeTest::ClientProcess::kParentClient, |
176 // never even runs. This tests that the server properly detects that it has | 294 ChildPortHandshakeTest::TestType::kClientChecksIn_ReceiveRight); |
177 // no client at all, and does not terminate execution with an error such as | 295 test.Run(); |
296 } | |
297 | |
298 TEST(ChildPortHandshake, ParentClientChecksIn_SendRight) { | |
299 ChildPortHandshakeTest test( | |
300 ChildPortHandshakeTest::ClientProcess::kParentClient, | |
301 ChildPortHandshakeTest::TestType::kClientChecksIn_SendRight); | |
302 test.Run(); | |
303 } | |
304 | |
305 TEST(ChildPortHandshake, ParentClientChecksIn_SendOnceRight) { | |
306 ChildPortHandshakeTest test( | |
307 ChildPortHandshakeTest::ClientProcess::kParentClient, | |
308 ChildPortHandshakeTest::TestType::kClientChecksIn_SendOnceRight); | |
309 test.Run(); | |
310 } | |
311 | |
312 TEST(ChildPortHandshake, ParentClientDoesNotCheckIn) { | |
313 ChildPortHandshakeTest test( | |
314 ChildPortHandshakeTest::ClientProcess::kParentClient, | |
315 ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn); | |
316 test.Run(); | |
317 } | |
318 | |
319 TEST(ChildPortHandshake, ParentClientDoesNotCheckIn_ReadsPipe) { | |
320 ChildPortHandshakeTest test( | |
321 ChildPortHandshakeTest::ClientProcess::kParentClient, | |
322 ChildPortHandshakeTest::TestType::kClientDoesNotCheckIn_ReadsPipe); | |
323 test.Run(); | |
324 } | |
325 | |
326 TEST(ChildPortHandshake, ParentClientTokenIncorrect) { | |
327 ChildPortHandshakeTest test( | |
328 ChildPortHandshakeTest::ClientProcess::kParentClient, | |
329 ChildPortHandshakeTest::TestType::kTokenIncorrect); | |
330 test.Run(); | |
331 } | |
332 | |
333 TEST(ChildPortHandshake, ParentClientTokenIncorrectThenCorrect) { | |
334 ChildPortHandshakeTest test( | |
335 ChildPortHandshakeTest::ClientProcess::kParentClient, | |
336 ChildPortHandshakeTest::TestType::kTokenIncorrectThenCorrect); | |
337 test.Run(); | |
338 } | |
339 | |
340 TEST(ChildPortHandshake, NoClient) { | |
341 // In this test, the client never checks in with the server because it never | |
342 // even runs. This tests that the server properly detects that it has no | |
343 // client at all, and does not terminate execution with an error such as | |
178 // “broken pipe” when attempting to send instructions to the client. This test | 344 // “broken pipe” when attempting to send instructions to the client. This test |
179 // is similar to ChildDoesNotCheckIn, but because there’s no child at all, the | 345 // is similar to kClientDoesNotCheckIn, but because there’s no client at all, |
180 // server is guaranteed to see that its pipe partner is gone. | 346 // the server is guaranteed to see that its pipe partner is gone. |
181 ChildPortHandshake child_port_handshake; | 347 ChildPortHandshake child_port_handshake; |
182 base::mac::ScopedMachSendRight child_port(child_port_handshake.RunServer()); | 348 base::mac::ScopedMachSendRight child_port(child_port_handshake.RunServer( |
183 EXPECT_EQ(kMachPortNull, child_port); | 349 ChildPortHandshake::PortRightType::kSendRight)); |
350 EXPECT_FALSE(child_port.is_valid()); | |
184 } | 351 } |
185 | 352 |
186 } // namespace | 353 } // namespace |
187 } // namespace test | 354 } // namespace test |
188 } // namespace crashpad | 355 } // namespace crashpad |
OLD | NEW |