OLD | NEW |
---|---|
(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/exc_client_variants.h" | |
16 | |
17 #include <mach/mach.h> | |
18 #include <pthread.h> | |
19 #include <string.h> | |
20 | |
21 #include "base/basictypes.h" | |
22 #include "base/strings/stringprintf.h" | |
23 #include "gtest/gtest.h" | |
24 #include "util/mach/exc_server_variants.h" | |
25 #include "util/mach/mach_extensions.h" | |
26 #include "util/test/mac/mach_errors.h" | |
27 #include "util/test/mac/mach_multiprocess.h" | |
28 | |
29 namespace { | |
30 | |
31 using namespace crashpad; | |
32 using namespace crashpad::test; | |
33 | |
34 class TestExcClientVariants : public UniversalMachExcServer, | |
35 public MachMultiprocess { | |
36 public: | |
37 TestExcClientVariants(exception_behavior_t behavior, bool all_fields) | |
38 : UniversalMachExcServer(), | |
39 MachMultiprocess(), | |
40 behavior_(behavior), | |
41 all_fields_(all_fields), | |
42 handled_(false) { | |
43 ++exception_; | |
44 ++exception_code_; | |
45 ++exception_subcode_; | |
46 } | |
47 | |
48 // UniversalMachExcServer: | |
49 | |
50 virtual kern_return_t CatchMachException( | |
51 exception_behavior_t behavior, | |
52 exception_handler_t exception_port, | |
53 thread_t thread, | |
54 task_t task, | |
55 exception_type_t exception, | |
56 const mach_exception_data_type_t* code, | |
57 mach_msg_type_number_t code_count, | |
58 thread_state_flavor_t* flavor, | |
59 const natural_t* old_state, | |
60 mach_msg_type_number_t old_state_count, | |
61 thread_state_t new_state, | |
62 mach_msg_type_number_t* new_state_count, | |
63 bool* destroy_complex_request) override { | |
64 *destroy_complex_request = true; | |
65 | |
66 EXPECT_FALSE(handled_); | |
67 handled_ = true; | |
68 | |
69 EXPECT_EQ(behavior_, behavior); | |
70 EXPECT_EQ(LocalPort(), exception_port); | |
71 | |
72 if (HasIdentity()) { | |
73 EXPECT_NE(kMachPortNull, thread); | |
74 EXPECT_EQ(ChildTask(), task); | |
75 } else { | |
76 EXPECT_EQ(kMachPortNull, thread); | |
77 EXPECT_EQ(kMachPortNull, task); | |
78 } | |
79 | |
80 mach_exception_code_t expect_code = exception_code_; | |
81 mach_exception_subcode_t expect_subcode = exception_subcode_; | |
82 if ((behavior & MACH_EXCEPTION_CODES) == 0) { | |
83 expect_code = static_cast<exception_data_type_t>(expect_code); | |
84 expect_subcode = static_cast<exception_data_type_t>(expect_subcode); | |
85 } | |
86 | |
87 EXPECT_EQ(exception_, exception); | |
88 EXPECT_EQ(2u, code_count); | |
89 EXPECT_EQ(expect_code, code[0]); | |
90 EXPECT_EQ(expect_subcode, code[1]); | |
91 | |
92 if (HasState()) { | |
93 EXPECT_EQ(exception_ + 10, *flavor); | |
94 EXPECT_EQ(MACHINE_THREAD_STATE_COUNT, old_state_count); | |
95 EXPECT_NE(static_cast<const natural_t*>(NULL), old_state); | |
96 EXPECT_EQ(static_cast<mach_msg_type_number_t>(THREAD_STATE_MAX), | |
97 *new_state_count); | |
98 EXPECT_NE(static_cast<natural_t*>(NULL), new_state); | |
99 | |
100 for (size_t index = 0; index < old_state_count; ++index) { | |
101 EXPECT_EQ(index, old_state[index]); | |
102 } | |
103 | |
104 // Use a flavor known to be different from the incoming flavor, for a test | |
105 // of the “out” side of the inout flavor parameter. | |
106 *flavor = exception_ + 20; | |
107 *new_state_count = MACHINE_THREAD_STATE_COUNT; | |
108 | |
109 // Send a new state back to the client. | |
110 for (size_t index = 0; index < *new_state_count; ++index) { | |
111 EXPECT_EQ(0u, new_state[index]); | |
112 new_state[index] = MACHINE_THREAD_STATE_COUNT - index; | |
113 } | |
114 } else { | |
115 EXPECT_EQ(THREAD_STATE_NONE, *flavor); | |
116 EXPECT_EQ(0u, old_state_count); | |
117 EXPECT_EQ(NULL, old_state); | |
118 EXPECT_EQ(0u, *new_state_count); | |
119 EXPECT_EQ(NULL, new_state); | |
120 } | |
121 | |
122 return KERN_SUCCESS; | |
123 } | |
124 | |
125 private: | |
126 // MachMultiprocess: | |
127 | |
128 virtual void MachMultiprocessParent() override { | |
129 kern_return_t kr = MachMessageServer::Run(this, | |
130 LocalPort(), | |
131 MACH_MSG_OPTION_NONE, | |
132 MachMessageServer::kOneShot, | |
133 MachMessageServer::kBlocking, | |
134 0); | |
135 EXPECT_EQ(KERN_SUCCESS, kr) | |
136 << MachErrorMessage(kr, "MachMessageServer::Run"); | |
137 | |
138 EXPECT_TRUE(handled_); | |
139 } | |
140 | |
141 virtual void MachMultiprocessChild() override { | |
142 exception_type_t exception = exception_; | |
143 mach_exception_data_type_t code[] = { | |
Robert Sesek
2014/09/15 20:55:43
const?
| |
144 exception_code_, | |
145 exception_subcode_ | |
146 }; | |
147 | |
148 thread_t thread = MACH_PORT_NULL; | |
149 task_t task = MACH_PORT_NULL; | |
150 if (all_fields_ || HasIdentity()) { | |
151 thread = pthread_mach_thread_np(pthread_self()); | |
Robert Sesek
2014/09/15 20:55:43
Why do you do this instead of just mach_thread_sel
| |
152 task = mach_task_self(); | |
153 } | |
154 | |
155 thread_state_flavor_t flavor; | |
156 thread_state_flavor_t* flavor_p = NULL; | |
157 natural_t old_state[MACHINE_THREAD_STATE_COUNT]; | |
158 thread_state_t old_state_p = NULL; | |
159 mach_msg_type_number_t old_state_count = 0; | |
160 natural_t new_state[THREAD_STATE_MAX]; | |
161 thread_state_t new_state_p = NULL; | |
162 mach_msg_type_number_t new_state_count; | |
163 mach_msg_type_number_t* new_state_count_p = NULL; | |
164 if (all_fields_ || HasState()) { | |
165 // Pick a different flavor each time based on the value of exception_. | |
166 // These aren’t real flavors, it’s just for testing. | |
167 flavor = exception_ + 10; | |
168 flavor_p = &flavor; | |
169 for (size_t index = 0; index < arraysize(old_state); ++index) { | |
170 old_state[index] = index; | |
171 } | |
172 old_state_p = reinterpret_cast<thread_state_t>(&old_state); | |
173 old_state_count = arraysize(old_state); | |
174 | |
175 // new_state and new_state_count are out parameters that the server should | |
176 // never see or use, so set them to bogus values. The call to the server | |
177 // should overwrite these. | |
178 memset(new_state, 0xa5, sizeof(new_state)); | |
179 new_state_p = reinterpret_cast<thread_state_t>(&new_state); | |
180 new_state_count = 0x5a; | |
181 new_state_count_p = &new_state_count; | |
182 } | |
183 | |
184 EXPECT_EQ(KERN_SUCCESS, UniversalExceptionRaise(behavior_, | |
185 RemotePort(), | |
186 thread, | |
187 task, | |
188 exception, | |
189 code, | |
190 arraysize(code), | |
191 flavor_p, | |
192 old_state_p, | |
193 old_state_count, | |
194 new_state_p, | |
195 new_state_count_p)); | |
196 | |
197 if (HasState()) { | |
198 // Verify the out parameters. | |
199 | |
200 EXPECT_EQ(exception_ + 20, flavor); | |
201 EXPECT_EQ(MACHINE_THREAD_STATE_COUNT, new_state_count); | |
202 | |
203 for (size_t index = 0; index < new_state_count; ++index) { | |
204 EXPECT_EQ(MACHINE_THREAD_STATE_COUNT - index, new_state[index]); | |
205 } | |
206 } | |
207 } | |
208 | |
209 exception_behavior_t BasicBehavior() const { | |
210 return behavior_ & ~MACH_EXCEPTION_CODES; | |
211 } | |
212 | |
213 bool HasIdentity() const { | |
214 exception_behavior_t basic_behavior = BasicBehavior(); | |
215 return basic_behavior == EXCEPTION_DEFAULT || | |
216 basic_behavior == EXCEPTION_STATE_IDENTITY; | |
217 } | |
218 | |
219 bool HasState() const { | |
220 exception_behavior_t basic_behavior = BasicBehavior(); | |
221 return basic_behavior == EXCEPTION_STATE || | |
222 basic_behavior == EXCEPTION_STATE_IDENTITY; | |
223 } | |
224 | |
225 // The behavior to test. | |
226 exception_behavior_t behavior_; | |
227 | |
228 // If false, only fields required for the current value of behavior_ are set | |
229 // in a call to UniversalExceptionRaise(). The thread and task fields are only | |
230 // set for identity-carrying behaviors, and the flavor and state fields are | |
231 // only set for state-carrying behaviors. If true, all fields are set | |
232 // regardless of the behavior. Testing in both ways verifies that | |
233 // UniversalExceptionRaise() can tolerate the null arguments documented as | |
234 // usable when the behavior allows it, and that it ignores these arguments | |
235 // even when set when the behavior does not make use of them. | |
236 bool all_fields_; | |
237 | |
238 // true if an exception message was handled. | |
239 bool handled_; | |
240 | |
241 // These fields will increment for each instantiation of the test class. | |
242 static exception_type_t exception_; | |
243 static mach_exception_code_t exception_code_; | |
244 static mach_exception_subcode_t exception_subcode_; | |
245 | |
246 DISALLOW_COPY_AND_ASSIGN(TestExcClientVariants); | |
247 }; | |
248 | |
249 exception_type_t TestExcClientVariants::exception_ = 0; | |
250 | |
251 // exception_code_ and exception_subcode_ are always large enough to require | |
252 // 64 bits, so that when the 32-bit-only variants not using MACH_EXCEPITON_CODES | |
253 // are tested, the code and subcode fields can be checked for proper truncation. | |
254 mach_exception_code_t TestExcClientVariants::exception_code_ = 0x100000000; | |
255 mach_exception_subcode_t TestExcClientVariants::exception_subcode_ = | |
256 0xffffffff00000000; | |
257 | |
258 TEST(ExcClientVariants, UniversalExceptionRaise) { | |
259 const exception_behavior_t kBehaviors[] = { | |
260 EXCEPTION_DEFAULT, | |
261 EXCEPTION_STATE, | |
262 EXCEPTION_STATE_IDENTITY, | |
263 kMachExceptionCodes | EXCEPTION_DEFAULT, | |
264 kMachExceptionCodes | EXCEPTION_STATE, | |
265 kMachExceptionCodes | EXCEPTION_STATE_IDENTITY, | |
266 }; | |
267 | |
268 for (size_t index = 0; index < arraysize(kBehaviors); ++index) { | |
269 exception_behavior_t behavior = kBehaviors[index]; | |
270 SCOPED_TRACE(base::StringPrintf("index %zu, behavior %d", index, behavior)); | |
271 | |
272 { | |
273 SCOPED_TRACE("all_fields = false"); | |
274 | |
275 TestExcClientVariants test_exc_client_variants(behavior, false); | |
276 test_exc_client_variants.Run(); | |
277 } | |
278 | |
279 { | |
280 SCOPED_TRACE("all_fields = true"); | |
281 | |
282 TestExcClientVariants test_exc_client_variants(behavior, true); | |
283 test_exc_client_variants.Run(); | |
284 } | |
285 } | |
286 } | |
287 | |
288 } // namespace | |
OLD | NEW |