Chromium Code Reviews| 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 |