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

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

Issue 1408473002: ChildPortHandshake: allow receive rights to be received (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Created 5 years, 2 months 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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698