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_server_variants.h" |
| 16 |
| 17 #include <mach/mach.h> |
| 18 #include <string.h> |
| 19 |
| 20 #include "base/basictypes.h" |
| 21 #include "base/strings/stringprintf.h" |
| 22 #include "gmock/gmock.h" |
| 23 #include "gtest/gtest.h" |
| 24 #include "util/mach/mach_extensions.h" |
| 25 #include "util/test/mac/mach_errors.h" |
| 26 #include "util/test/mac/mach_multiprocess.h" |
| 27 |
| 28 namespace { |
| 29 |
| 30 using namespace crashpad; |
| 31 using namespace crashpad::test; |
| 32 using namespace testing; |
| 33 |
| 34 // Fake Mach ports. These aren’t used as ports in these tests, they’re just used |
| 35 // as cookies to make sure that the correct values get passed to the correct |
| 36 // places. |
| 37 const mach_port_t kClientRemotePort = 0x01010101; |
| 38 const mach_port_t kServerLocalPort = 0x02020202; |
| 39 const mach_port_t kExceptionThreadPort = 0x03030303; |
| 40 const mach_port_t kExceptionTaskPort = 0x04040404; |
| 41 |
| 42 // Other fake exception values. |
| 43 const exception_type_t kExceptionType = EXC_BAD_ACCESS; |
| 44 |
| 45 // Test using an exception code with the high bit set to ensure that it gets |
| 46 // promoted to the wider mach_exception_data_type_t type as a signed quantity. |
| 47 const exception_data_type_t kExceptionCodes[] = { |
| 48 KERN_PROTECTION_FAILURE, |
| 49 static_cast<exception_data_type_t>(0xfedcba98), |
| 50 }; |
| 51 |
| 52 const exception_data_type_t kMachExceptionCodes[] = { |
| 53 KERN_PROTECTION_FAILURE, |
| 54 static_cast<exception_data_type_t>(0xfedcba9876543210), |
| 55 }; |
| 56 |
| 57 const thread_state_flavor_t kThreadStateFlavor = MACHINE_THREAD_STATE; |
| 58 const mach_msg_type_number_t kThreadStateFlavorCount = |
| 59 MACHINE_THREAD_STATE_COUNT; |
| 60 |
| 61 void InitializeMachMsgPortDescriptor(mach_msg_port_descriptor_t* descriptor, |
| 62 mach_port_t port) { |
| 63 descriptor->name = port; |
| 64 descriptor->disposition = MACH_MSG_TYPE_MOVE_SEND; |
| 65 descriptor->type = MACH_MSG_PORT_DESCRIPTOR; |
| 66 } |
| 67 |
| 68 // The definitions of the request and reply structures from mach_exc.h aren’t |
| 69 // available here. They need custom initialization code, and the reply |
| 70 // structures need verification code too, so duplicate the expected definitions |
| 71 // of the structures from both exc.h and mach_exc.h here in this file, and |
| 72 // provide the initialization and verification code as methods in true |
| 73 // object-oriented fashion. |
| 74 |
| 75 struct __attribute__((packed, aligned(4))) ExceptionRaiseRequest { |
| 76 mach_msg_header_t Head; |
| 77 mach_msg_body_t msgh_body; |
| 78 mach_msg_port_descriptor_t thread; |
| 79 mach_msg_port_descriptor_t task; |
| 80 NDR_record_t NDR; |
| 81 exception_type_t exception; |
| 82 mach_msg_type_number_t codeCnt; |
| 83 integer_t code[2]; |
| 84 |
| 85 void InitializeForTesting() { |
| 86 memset(this, 0xa5, sizeof(*this)); |
| 87 Head.msgh_bits = |
| 88 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) | |
| 89 MACH_MSGH_BITS_COMPLEX; |
| 90 Head.msgh_size = sizeof(*this); |
| 91 Head.msgh_remote_port = kClientRemotePort; |
| 92 Head.msgh_local_port = kServerLocalPort; |
| 93 Head.msgh_id = 2401; |
| 94 msgh_body.msgh_descriptor_count = 2; |
| 95 InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort); |
| 96 InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort); |
| 97 NDR = NDR_record; |
| 98 exception = kExceptionType; |
| 99 codeCnt = 2; |
| 100 code[0] = kExceptionCodes[0]; |
| 101 code[1] = kExceptionCodes[1]; |
| 102 } |
| 103 }; |
| 104 |
| 105 struct __attribute__((packed, aligned(4))) ExceptionRaiseReply { |
| 106 mach_msg_header_t Head; |
| 107 NDR_record_t NDR; |
| 108 kern_return_t RetCode; |
| 109 |
| 110 void InitializeForTesting() { |
| 111 memset(this, 0x5a, sizeof(*this)); |
| 112 RetCode = KERN_FAILURE; |
| 113 } |
| 114 |
| 115 // Verify accepts a |behavior| parameter because the same message format and |
| 116 // verification function is used for ExceptionRaiseReply and |
| 117 // MachExceptionRaiseReply. Knowing which behavior is expected allows the |
| 118 // message ID to be checked. |
| 119 void Verify(exception_behavior_t behavior) { |
| 120 EXPECT_EQ(static_cast<mach_msg_bits_t>( |
| 121 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0)), |
| 122 Head.msgh_bits); |
| 123 EXPECT_EQ(sizeof(*this), Head.msgh_size); |
| 124 EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port); |
| 125 EXPECT_EQ(kMachPortNull, Head.msgh_local_port); |
| 126 switch (behavior) { |
| 127 case EXCEPTION_DEFAULT: |
| 128 EXPECT_EQ(2501, Head.msgh_id); |
| 129 break; |
| 130 case static_cast<exception_behavior_t>(EXCEPTION_DEFAULT | |
| 131 MACH_EXCEPTION_CODES): |
| 132 EXPECT_EQ(2505, Head.msgh_id); |
| 133 break; |
| 134 default: |
| 135 ADD_FAILURE() << "behavior " << behavior << ", Head.msgh_id " |
| 136 << Head.msgh_id; |
| 137 break; |
| 138 } |
| 139 EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR))); |
| 140 EXPECT_EQ(KERN_SUCCESS, RetCode); |
| 141 } |
| 142 }; |
| 143 |
| 144 struct __attribute__((packed, aligned(4))) ExceptionRaiseStateRequest { |
| 145 mach_msg_header_t Head; |
| 146 NDR_record_t NDR; |
| 147 exception_type_t exception; |
| 148 mach_msg_type_number_t codeCnt; |
| 149 integer_t code[2]; |
| 150 int flavor; |
| 151 mach_msg_type_number_t old_stateCnt; |
| 152 natural_t old_state[THREAD_STATE_MAX]; |
| 153 |
| 154 void InitializeForTesting() { |
| 155 memset(this, 0xa5, sizeof(*this)); |
| 156 Head.msgh_bits = |
| 157 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); |
| 158 Head.msgh_size = sizeof(*this); |
| 159 Head.msgh_remote_port = kClientRemotePort; |
| 160 Head.msgh_local_port = kServerLocalPort; |
| 161 Head.msgh_id = 2402; |
| 162 NDR = NDR_record; |
| 163 exception = kExceptionType; |
| 164 codeCnt = 2; |
| 165 code[0] = kExceptionCodes[0]; |
| 166 code[1] = kExceptionCodes[1]; |
| 167 flavor = kThreadStateFlavor; |
| 168 old_stateCnt = kThreadStateFlavorCount; |
| 169 |
| 170 // Adjust the message size for the data that it’s actually carrying, which |
| 171 // may be smaller than the maximum that it can carry. |
| 172 Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state); |
| 173 } |
| 174 }; |
| 175 |
| 176 struct __attribute__((packed, aligned(4))) ExceptionRaiseStateReply { |
| 177 mach_msg_header_t Head; |
| 178 NDR_record_t NDR; |
| 179 kern_return_t RetCode; |
| 180 int flavor; |
| 181 mach_msg_type_number_t new_stateCnt; |
| 182 natural_t new_state[THREAD_STATE_MAX]; |
| 183 |
| 184 void InitializeForTesting() { |
| 185 memset(this, 0x5a, sizeof(*this)); |
| 186 RetCode = KERN_FAILURE; |
| 187 } |
| 188 |
| 189 // Verify accepts a |behavior| parameter because the same message format and |
| 190 // verification function is used for ExceptionRaiseStateReply, |
| 191 // ExceptionRaiseStateIdentityReply, MachExceptionRaiseStateReply, and |
| 192 // MachExceptionRaiseStateIdentityReply. Knowing which behavior is expected |
| 193 // allows the message ID to be checked. |
| 194 void Verify(exception_behavior_t behavior) { |
| 195 EXPECT_EQ(static_cast<mach_msg_bits_t>( |
| 196 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0)), |
| 197 Head.msgh_bits); |
| 198 EXPECT_EQ(sizeof(*this), Head.msgh_size); |
| 199 EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port); |
| 200 EXPECT_EQ(kMachPortNull, Head.msgh_local_port); |
| 201 switch (behavior) { |
| 202 case EXCEPTION_STATE: |
| 203 EXPECT_EQ(2502, Head.msgh_id); |
| 204 break; |
| 205 case EXCEPTION_STATE_IDENTITY: |
| 206 EXPECT_EQ(2503, Head.msgh_id); |
| 207 break; |
| 208 case static_cast<exception_behavior_t>(EXCEPTION_STATE | |
| 209 MACH_EXCEPTION_CODES): |
| 210 EXPECT_EQ(2506, Head.msgh_id); |
| 211 break; |
| 212 case static_cast<exception_behavior_t>(EXCEPTION_STATE_IDENTITY | |
| 213 MACH_EXCEPTION_CODES): |
| 214 EXPECT_EQ(2507, Head.msgh_id); |
| 215 break; |
| 216 default: |
| 217 ADD_FAILURE() << "behavior " << behavior << ", Head.msgh_id " |
| 218 << Head.msgh_id; |
| 219 break; |
| 220 } |
| 221 EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR))); |
| 222 EXPECT_EQ(KERN_SUCCESS, RetCode); |
| 223 EXPECT_EQ(kThreadStateFlavor, flavor); |
| 224 EXPECT_EQ(arraysize(new_state), new_stateCnt); |
| 225 } |
| 226 }; |
| 227 |
| 228 struct __attribute__((packed, aligned(4))) ExceptionRaiseStateIdentityRequest { |
| 229 mach_msg_header_t Head; |
| 230 mach_msg_body_t msgh_body; |
| 231 mach_msg_port_descriptor_t thread; |
| 232 mach_msg_port_descriptor_t task; |
| 233 NDR_record_t NDR; |
| 234 exception_type_t exception; |
| 235 mach_msg_type_number_t codeCnt; |
| 236 integer_t code[2]; |
| 237 int flavor; |
| 238 mach_msg_type_number_t old_stateCnt; |
| 239 natural_t old_state[THREAD_STATE_MAX]; |
| 240 |
| 241 void InitializeForTesting() { |
| 242 memset(this, 0xa5, sizeof(*this)); |
| 243 Head.msgh_bits = |
| 244 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) | |
| 245 MACH_MSGH_BITS_COMPLEX; |
| 246 Head.msgh_size = sizeof(*this); |
| 247 Head.msgh_remote_port = kClientRemotePort; |
| 248 Head.msgh_local_port = kServerLocalPort; |
| 249 Head.msgh_id = 2403; |
| 250 msgh_body.msgh_descriptor_count = 2; |
| 251 InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort); |
| 252 InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort); |
| 253 NDR = NDR_record; |
| 254 exception = kExceptionType; |
| 255 codeCnt = 2; |
| 256 code[0] = kExceptionCodes[0]; |
| 257 code[1] = kExceptionCodes[1]; |
| 258 flavor = kThreadStateFlavor; |
| 259 old_stateCnt = kThreadStateFlavorCount; |
| 260 |
| 261 // Adjust the message size for the data that it’s actually carrying, which |
| 262 // may be smaller than the maximum that it can carry. |
| 263 Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state); |
| 264 } |
| 265 }; |
| 266 |
| 267 // The reply messages for exception_raise_state and |
| 268 // exception_raise_state_identity are identical. |
| 269 typedef ExceptionRaiseStateReply ExceptionRaiseStateIdentityReply; |
| 270 |
| 271 struct __attribute__((packed, aligned(4))) MachExceptionRaiseRequest { |
| 272 mach_msg_header_t Head; |
| 273 mach_msg_body_t msgh_body; |
| 274 mach_msg_port_descriptor_t thread; |
| 275 mach_msg_port_descriptor_t task; |
| 276 NDR_record_t NDR; |
| 277 exception_type_t exception; |
| 278 mach_msg_type_number_t codeCnt; |
| 279 int64_t code[2]; |
| 280 |
| 281 void InitializeForTesting() { |
| 282 memset(this, 0xa5, sizeof(*this)); |
| 283 Head.msgh_bits = |
| 284 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) | |
| 285 MACH_MSGH_BITS_COMPLEX; |
| 286 Head.msgh_size = sizeof(*this); |
| 287 Head.msgh_remote_port = kClientRemotePort; |
| 288 Head.msgh_local_port = kServerLocalPort; |
| 289 Head.msgh_id = 2405; |
| 290 msgh_body.msgh_descriptor_count = 2; |
| 291 InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort); |
| 292 InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort); |
| 293 NDR = NDR_record; |
| 294 exception = kExceptionType; |
| 295 codeCnt = 2; |
| 296 code[0] = kMachExceptionCodes[0]; |
| 297 code[1] = kMachExceptionCodes[1]; |
| 298 } |
| 299 }; |
| 300 |
| 301 // The reply messages for exception_raise and mach_exception_raise are |
| 302 // identical. |
| 303 typedef ExceptionRaiseReply MachExceptionRaiseReply; |
| 304 |
| 305 struct __attribute__((packed, aligned(4))) MachExceptionRaiseStateRequest { |
| 306 mach_msg_header_t Head; |
| 307 NDR_record_t NDR; |
| 308 exception_type_t exception; |
| 309 mach_msg_type_number_t codeCnt; |
| 310 int64_t code[2]; |
| 311 int flavor; |
| 312 mach_msg_type_number_t old_stateCnt; |
| 313 natural_t old_state[THREAD_STATE_MAX]; |
| 314 |
| 315 void InitializeForTesting() { |
| 316 memset(this, 0xa5, sizeof(*this)); |
| 317 Head.msgh_bits = |
| 318 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); |
| 319 Head.msgh_size = sizeof(*this); |
| 320 Head.msgh_remote_port = kClientRemotePort; |
| 321 Head.msgh_local_port = kServerLocalPort; |
| 322 Head.msgh_id = 2406; |
| 323 NDR = NDR_record; |
| 324 exception = kExceptionType; |
| 325 codeCnt = 2; |
| 326 code[0] = kMachExceptionCodes[0]; |
| 327 code[1] = kMachExceptionCodes[1]; |
| 328 flavor = kThreadStateFlavor; |
| 329 old_stateCnt = kThreadStateFlavorCount; |
| 330 |
| 331 // Adjust the message size for the data that it’s actually carrying, which |
| 332 // may be smaller than the maximum that it can carry. |
| 333 Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state); |
| 334 } |
| 335 }; |
| 336 |
| 337 // The reply messages for exception_raise_state and mach_exception_raise_state |
| 338 // are identical. |
| 339 typedef ExceptionRaiseStateReply MachExceptionRaiseStateReply; |
| 340 |
| 341 struct __attribute__((packed, |
| 342 aligned(4))) MachExceptionRaiseStateIdentityRequest { |
| 343 mach_msg_header_t Head; |
| 344 mach_msg_body_t msgh_body; |
| 345 mach_msg_port_descriptor_t thread; |
| 346 mach_msg_port_descriptor_t task; |
| 347 NDR_record_t NDR; |
| 348 exception_type_t exception; |
| 349 mach_msg_type_number_t codeCnt; |
| 350 int64_t code[2]; |
| 351 int flavor; |
| 352 mach_msg_type_number_t old_stateCnt; |
| 353 natural_t old_state[THREAD_STATE_MAX]; |
| 354 |
| 355 void InitializeForTesting() { |
| 356 memset(this, 0xa5, sizeof(*this)); |
| 357 Head.msgh_bits = |
| 358 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) | |
| 359 MACH_MSGH_BITS_COMPLEX; |
| 360 Head.msgh_size = sizeof(*this); |
| 361 Head.msgh_remote_port = kClientRemotePort; |
| 362 Head.msgh_local_port = kServerLocalPort; |
| 363 Head.msgh_id = 2407; |
| 364 msgh_body.msgh_descriptor_count = 2; |
| 365 InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort); |
| 366 InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort); |
| 367 NDR = NDR_record; |
| 368 exception = kExceptionType; |
| 369 codeCnt = 2; |
| 370 code[0] = kMachExceptionCodes[0]; |
| 371 code[1] = kMachExceptionCodes[1]; |
| 372 flavor = kThreadStateFlavor; |
| 373 old_stateCnt = kThreadStateFlavorCount; |
| 374 |
| 375 // Adjust the message size for the data that it’s actually carrying, which |
| 376 // may be smaller than the maximum that it can carry. |
| 377 Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state); |
| 378 } |
| 379 }; |
| 380 |
| 381 // The reply messages for exception_raise_state_identity and |
| 382 // mach_exception_raise_state_identity are identical. |
| 383 typedef ExceptionRaiseStateIdentityReply MachExceptionRaiseStateIdentityReply; |
| 384 |
| 385 // InvalidRequest and BadIDErrorReply are used to test that |
| 386 // UniversalMachExcServer deals appropriately with messages that it does not |
| 387 // understand: messages with an unknown Head.msgh_id. |
| 388 |
| 389 struct __attribute__((packed, aligned(4))) InvalidRequest |
| 390 : public mach_msg_empty_send_t { |
| 391 void InitializeForTesting(mach_msg_id_t id) { |
| 392 memset(this, 0xa5, sizeof(*this)); |
| 393 header.msgh_bits = |
| 394 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE); |
| 395 header.msgh_size = sizeof(*this); |
| 396 header.msgh_remote_port = kClientRemotePort; |
| 397 header.msgh_local_port = kServerLocalPort; |
| 398 header.msgh_id = id; |
| 399 } |
| 400 }; |
| 401 |
| 402 struct __attribute__((packed, aligned(4))) BadIDErrorReply |
| 403 : public mig_reply_error_t { |
| 404 void InitializeForTesting() { |
| 405 memset(this, 0x5a, sizeof(*this)); |
| 406 RetCode = KERN_FAILURE; |
| 407 } |
| 408 |
| 409 void Verify(mach_msg_id_t id) { |
| 410 EXPECT_EQ(static_cast<mach_msg_bits_t>( |
| 411 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0)), |
| 412 Head.msgh_bits); |
| 413 EXPECT_EQ(sizeof(*this), Head.msgh_size); |
| 414 EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port); |
| 415 EXPECT_EQ(kMachPortNull, Head.msgh_local_port); |
| 416 EXPECT_EQ(id + 100, Head.msgh_id); |
| 417 EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR))); |
| 418 EXPECT_EQ(MIG_BAD_ID, RetCode); |
| 419 } |
| 420 }; |
| 421 |
| 422 class MockUniversalMachExcServer : public UniversalMachExcServer { |
| 423 public: |
| 424 struct ConstExceptionCodes { |
| 425 const mach_exception_data_type_t* code; |
| 426 mach_msg_type_number_t code_count; |
| 427 }; |
| 428 struct ThreadState { |
| 429 thread_state_t state; |
| 430 mach_msg_type_number_t* state_count; |
| 431 }; |
| 432 struct ConstThreadState { |
| 433 const natural_t* state; |
| 434 mach_msg_type_number_t* state_count; |
| 435 }; |
| 436 |
| 437 // CatchMachException is the method to mock, but it has 13 parameters, and |
| 438 // gmock can only mock methods with up to 10 parameters. Coalesce some related |
| 439 // parameters together into structs, and call a mocked method. |
| 440 virtual kern_return_t CatchMachException( |
| 441 exception_behavior_t behavior, |
| 442 exception_handler_t exception_port, |
| 443 thread_t thread, |
| 444 task_t task, |
| 445 exception_type_t exception, |
| 446 const mach_exception_data_type_t* code, |
| 447 mach_msg_type_number_t code_count, |
| 448 thread_state_flavor_t* flavor, |
| 449 const natural_t* old_state, |
| 450 mach_msg_type_number_t old_state_count, |
| 451 thread_state_t new_state, |
| 452 mach_msg_type_number_t* new_state_count, |
| 453 bool* destroy_complex_request) override { |
| 454 *destroy_complex_request = true; |
| 455 const ConstExceptionCodes exception_codes = {code, code_count}; |
| 456 const ConstThreadState old_thread_state = {old_state, &old_state_count}; |
| 457 ThreadState new_thread_state = {new_state, new_state_count}; |
| 458 return MockCatchMachException(behavior, |
| 459 exception_port, |
| 460 thread, |
| 461 task, |
| 462 exception, |
| 463 &exception_codes, |
| 464 flavor, |
| 465 &old_thread_state, |
| 466 &new_thread_state); |
| 467 } |
| 468 |
| 469 MOCK_METHOD9(MockCatchMachException, |
| 470 kern_return_t(exception_behavior_t behavior, |
| 471 exception_handler_t exception_port, |
| 472 thread_t thread, |
| 473 task_t task, |
| 474 exception_type_t exception, |
| 475 const ConstExceptionCodes* exception_codes, |
| 476 thread_state_flavor_t* flavor, |
| 477 const ConstThreadState* old_thread_state, |
| 478 ThreadState* new_thread_state)); |
| 479 }; |
| 480 |
| 481 // Matcher for ConstExceptionCodes, testing that it carries 2 codes matching |
| 482 // code_0 and code_1. |
| 483 MATCHER_P2(AreExceptionCodes, code_0, code_1, "") { |
| 484 if (!arg) { |
| 485 return false; |
| 486 } |
| 487 |
| 488 if (arg->code_count == 2 && arg->code[0] == code_0 && |
| 489 arg->code[1] == code_1) { |
| 490 return true; |
| 491 } |
| 492 |
| 493 *result_listener << "codes ("; |
| 494 for (size_t index = 0; index < arg->code_count; ++index) { |
| 495 *result_listener << arg->code[index]; |
| 496 if (index < arg->code_count - 1) { |
| 497 *result_listener << ", "; |
| 498 } |
| 499 } |
| 500 *result_listener << ")"; |
| 501 |
| 502 return false; |
| 503 } |
| 504 |
| 505 // Matcher for ThreadState and ConstThreadState, testing that *state_count is |
| 506 // present and matches the specified value. If 0 is specified for the count, |
| 507 // state must be NULL (not present), otherwise state must be non-NULL (present). |
| 508 MATCHER_P(IsThreadStateCount, state_count, "") { |
| 509 if (!arg) { |
| 510 return false; |
| 511 } |
| 512 if (!arg->state_count) { |
| 513 *result_listener << "state_count NULL"; |
| 514 return false; |
| 515 } |
| 516 if (*(arg->state_count) != state_count) { |
| 517 *result_listener << "*state_count " << *(arg->state_count); |
| 518 return false; |
| 519 } |
| 520 if (state_count) { |
| 521 if (!arg->state) { |
| 522 *result_listener << "*state_count " << state_count << ", state NULL"; |
| 523 return false; |
| 524 } |
| 525 } else { |
| 526 if (arg->state) { |
| 527 *result_listener << "*state_count 0, state non-NULL (" << arg->state |
| 528 << ")"; |
| 529 return false; |
| 530 } |
| 531 } |
| 532 return true; |
| 533 } |
| 534 |
| 535 template <typename T> |
| 536 class ScopedDefaultValue { |
| 537 public: |
| 538 explicit ScopedDefaultValue(const T& default_value) { |
| 539 DefaultValue<T>::Set(default_value); |
| 540 } |
| 541 |
| 542 ~ScopedDefaultValue() { DefaultValue<T>::Clear(); } |
| 543 }; |
| 544 |
| 545 TEST(ExcServerVariants, MockExceptionRaise) { |
| 546 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); |
| 547 |
| 548 MockUniversalMachExcServer server; |
| 549 |
| 550 ExceptionRaiseRequest request; |
| 551 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); |
| 552 request.InitializeForTesting(); |
| 553 |
| 554 ExceptionRaiseReply reply; |
| 555 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); |
| 556 reply.InitializeForTesting(); |
| 557 |
| 558 const exception_behavior_t kExceptionBehavior = EXCEPTION_DEFAULT; |
| 559 |
| 560 EXPECT_CALL(server, |
| 561 MockCatchMachException( |
| 562 kExceptionBehavior, |
| 563 kServerLocalPort, |
| 564 kExceptionThreadPort, |
| 565 kExceptionTaskPort, |
| 566 kExceptionType, |
| 567 AreExceptionCodes(kExceptionCodes[0], kExceptionCodes[1]), |
| 568 Pointee(Eq(THREAD_STATE_NONE)), |
| 569 IsThreadStateCount(0u), |
| 570 IsThreadStateCount(0u))) |
| 571 .WillOnce(Return(KERN_SUCCESS)) |
| 572 .RetiresOnSaturation(); |
| 573 |
| 574 bool destroy_complex_request = false; |
| 575 EXPECT_TRUE(server.MachMessageServerFunction( |
| 576 reinterpret_cast<mach_msg_header_t*>(&request), |
| 577 reinterpret_cast<mach_msg_header_t*>(&reply), |
| 578 &destroy_complex_request)); |
| 579 EXPECT_TRUE(destroy_complex_request); |
| 580 |
| 581 reply.Verify(kExceptionBehavior); |
| 582 } |
| 583 |
| 584 TEST(ExcServerVariants, MockExceptionRaiseState) { |
| 585 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); |
| 586 |
| 587 MockUniversalMachExcServer server; |
| 588 |
| 589 ExceptionRaiseStateRequest request; |
| 590 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); |
| 591 request.InitializeForTesting(); |
| 592 |
| 593 ExceptionRaiseStateReply reply; |
| 594 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); |
| 595 reply.InitializeForTesting(); |
| 596 |
| 597 const exception_behavior_t kExceptionBehavior = EXCEPTION_STATE; |
| 598 |
| 599 EXPECT_CALL(server, |
| 600 MockCatchMachException( |
| 601 kExceptionBehavior, |
| 602 kServerLocalPort, |
| 603 MACH_PORT_NULL, |
| 604 MACH_PORT_NULL, |
| 605 kExceptionType, |
| 606 AreExceptionCodes(kExceptionCodes[0], kExceptionCodes[1]), |
| 607 Pointee(Eq(kThreadStateFlavor)), |
| 608 IsThreadStateCount(kThreadStateFlavorCount), |
| 609 IsThreadStateCount(arraysize(reply.new_state)))) |
| 610 .WillOnce(Return(KERN_SUCCESS)) |
| 611 .RetiresOnSaturation(); |
| 612 |
| 613 bool destroy_complex_request = false; |
| 614 EXPECT_TRUE(server.MachMessageServerFunction( |
| 615 reinterpret_cast<mach_msg_header_t*>(&request), |
| 616 reinterpret_cast<mach_msg_header_t*>(&reply), |
| 617 &destroy_complex_request)); |
| 618 |
| 619 // The request wasn’t complex, so nothing got a chance to change the value of |
| 620 // this variable. |
| 621 EXPECT_FALSE(destroy_complex_request); |
| 622 |
| 623 reply.Verify(kExceptionBehavior); |
| 624 } |
| 625 |
| 626 TEST(ExcServerVariants, MockExceptionRaiseStateIdentity) { |
| 627 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); |
| 628 |
| 629 MockUniversalMachExcServer server; |
| 630 |
| 631 ExceptionRaiseStateIdentityRequest request; |
| 632 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); |
| 633 request.InitializeForTesting(); |
| 634 |
| 635 ExceptionRaiseStateIdentityReply reply; |
| 636 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); |
| 637 reply.InitializeForTesting(); |
| 638 |
| 639 const exception_behavior_t kExceptionBehavior = EXCEPTION_STATE_IDENTITY; |
| 640 |
| 641 EXPECT_CALL(server, |
| 642 MockCatchMachException( |
| 643 kExceptionBehavior, |
| 644 kServerLocalPort, |
| 645 kExceptionThreadPort, |
| 646 kExceptionTaskPort, |
| 647 kExceptionType, |
| 648 AreExceptionCodes(kExceptionCodes[0], kExceptionCodes[1]), |
| 649 Pointee(Eq(kThreadStateFlavor)), |
| 650 IsThreadStateCount(kThreadStateFlavorCount), |
| 651 IsThreadStateCount(arraysize(reply.new_state)))) |
| 652 .WillOnce(Return(KERN_SUCCESS)) |
| 653 .RetiresOnSaturation(); |
| 654 |
| 655 bool destroy_complex_request = false; |
| 656 EXPECT_TRUE(server.MachMessageServerFunction( |
| 657 reinterpret_cast<mach_msg_header_t*>(&request), |
| 658 reinterpret_cast<mach_msg_header_t*>(&reply), |
| 659 &destroy_complex_request)); |
| 660 EXPECT_TRUE(destroy_complex_request); |
| 661 |
| 662 reply.Verify(kExceptionBehavior); |
| 663 } |
| 664 |
| 665 TEST(ExcServerVariants, MockMachExceptionRaise) { |
| 666 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); |
| 667 |
| 668 MockUniversalMachExcServer server; |
| 669 |
| 670 MachExceptionRaiseRequest request; |
| 671 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); |
| 672 request.InitializeForTesting(); |
| 673 |
| 674 MachExceptionRaiseReply reply; |
| 675 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); |
| 676 reply.InitializeForTesting(); |
| 677 |
| 678 const exception_behavior_t kExceptionBehavior = |
| 679 EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES; |
| 680 |
| 681 EXPECT_CALL( |
| 682 server, |
| 683 MockCatchMachException( |
| 684 kExceptionBehavior, |
| 685 kServerLocalPort, |
| 686 kExceptionThreadPort, |
| 687 kExceptionTaskPort, |
| 688 kExceptionType, |
| 689 AreExceptionCodes(kMachExceptionCodes[0], kMachExceptionCodes[1]), |
| 690 Pointee(Eq(THREAD_STATE_NONE)), |
| 691 IsThreadStateCount(0u), |
| 692 IsThreadStateCount(0u))) |
| 693 .WillOnce(Return(KERN_SUCCESS)) |
| 694 .RetiresOnSaturation(); |
| 695 |
| 696 bool destroy_complex_request = false; |
| 697 EXPECT_TRUE(server.MachMessageServerFunction( |
| 698 reinterpret_cast<mach_msg_header_t*>(&request), |
| 699 reinterpret_cast<mach_msg_header_t*>(&reply), |
| 700 &destroy_complex_request)); |
| 701 EXPECT_TRUE(destroy_complex_request); |
| 702 |
| 703 reply.Verify(kExceptionBehavior); |
| 704 } |
| 705 |
| 706 TEST(ExcServerVariants, MockMachExceptionRaiseState) { |
| 707 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); |
| 708 |
| 709 MockUniversalMachExcServer server; |
| 710 |
| 711 MachExceptionRaiseStateRequest request; |
| 712 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); |
| 713 request.InitializeForTesting(); |
| 714 |
| 715 MachExceptionRaiseStateReply reply; |
| 716 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); |
| 717 reply.InitializeForTesting(); |
| 718 |
| 719 const exception_behavior_t kExceptionBehavior = |
| 720 EXCEPTION_STATE | MACH_EXCEPTION_CODES; |
| 721 |
| 722 EXPECT_CALL( |
| 723 server, |
| 724 MockCatchMachException( |
| 725 kExceptionBehavior, |
| 726 kServerLocalPort, |
| 727 MACH_PORT_NULL, |
| 728 MACH_PORT_NULL, |
| 729 kExceptionType, |
| 730 AreExceptionCodes(kMachExceptionCodes[0], kMachExceptionCodes[1]), |
| 731 Pointee(Eq(kThreadStateFlavor)), |
| 732 IsThreadStateCount(kThreadStateFlavorCount), |
| 733 IsThreadStateCount(arraysize(reply.new_state)))) |
| 734 .WillOnce(Return(KERN_SUCCESS)) |
| 735 .RetiresOnSaturation(); |
| 736 |
| 737 bool destroy_complex_request = false; |
| 738 EXPECT_TRUE(server.MachMessageServerFunction( |
| 739 reinterpret_cast<mach_msg_header_t*>(&request), |
| 740 reinterpret_cast<mach_msg_header_t*>(&reply), |
| 741 &destroy_complex_request)); |
| 742 |
| 743 // The request wasn’t complex, so nothing got a chance to change the value of |
| 744 // this variable. |
| 745 EXPECT_FALSE(destroy_complex_request); |
| 746 |
| 747 reply.Verify(kExceptionBehavior); |
| 748 } |
| 749 |
| 750 TEST(ExcServerVariants, MockMachExceptionRaiseStateIdentity) { |
| 751 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); |
| 752 |
| 753 MockUniversalMachExcServer server; |
| 754 |
| 755 MachExceptionRaiseStateIdentityRequest request; |
| 756 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); |
| 757 request.InitializeForTesting(); |
| 758 |
| 759 MachExceptionRaiseStateIdentityReply reply; |
| 760 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); |
| 761 reply.InitializeForTesting(); |
| 762 |
| 763 const exception_behavior_t kExceptionBehavior = |
| 764 EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES; |
| 765 |
| 766 EXPECT_CALL( |
| 767 server, |
| 768 MockCatchMachException( |
| 769 kExceptionBehavior, |
| 770 kServerLocalPort, |
| 771 kExceptionThreadPort, |
| 772 kExceptionTaskPort, |
| 773 kExceptionType, |
| 774 AreExceptionCodes(kMachExceptionCodes[0], kMachExceptionCodes[1]), |
| 775 Pointee(Eq(kThreadStateFlavor)), |
| 776 IsThreadStateCount(kThreadStateFlavorCount), |
| 777 IsThreadStateCount(arraysize(reply.new_state)))) |
| 778 .WillOnce(Return(KERN_SUCCESS)) |
| 779 .RetiresOnSaturation(); |
| 780 |
| 781 bool destroy_complex_request = false; |
| 782 EXPECT_TRUE(server.MachMessageServerFunction( |
| 783 reinterpret_cast<mach_msg_header_t*>(&request), |
| 784 reinterpret_cast<mach_msg_header_t*>(&reply), |
| 785 &destroy_complex_request)); |
| 786 EXPECT_TRUE(destroy_complex_request); |
| 787 |
| 788 reply.Verify(kExceptionBehavior); |
| 789 } |
| 790 |
| 791 TEST(ExcServerVariants, MockUnknownID) { |
| 792 ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE); |
| 793 |
| 794 MockUniversalMachExcServer server; |
| 795 |
| 796 // Make sure that a message with an unknown ID is handled appropriately. |
| 797 // UniversalMachExcServer should not dispatch the message to |
| 798 // MachMessageServerFunction, but should generate a MIG_BAD_ID error reply. |
| 799 |
| 800 const mach_msg_id_t unknown_ids[] = { |
| 801 // Reasonable things to check. |
| 802 -101, |
| 803 -100, |
| 804 -99, |
| 805 -1, |
| 806 0, |
| 807 1, |
| 808 99, |
| 809 100, |
| 810 101, |
| 811 |
| 812 // Invalid IDs right around valid ones. |
| 813 2400, |
| 814 2404, |
| 815 2408, |
| 816 |
| 817 // Valid and invalid IDs in the range used for replies, not requests. |
| 818 2500, |
| 819 2501, |
| 820 2502, |
| 821 2503, |
| 822 2504, |
| 823 2505, |
| 824 2506, |
| 825 2507, |
| 826 2508, |
| 827 }; |
| 828 |
| 829 for (size_t index = 0; index < arraysize(unknown_ids); ++index) { |
| 830 mach_msg_id_t id = unknown_ids[index]; |
| 831 |
| 832 SCOPED_TRACE(base::StringPrintf("unknown id %d", id)); |
| 833 |
| 834 InvalidRequest request; |
| 835 EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize()); |
| 836 request.InitializeForTesting(id); |
| 837 |
| 838 BadIDErrorReply reply; |
| 839 EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize()); |
| 840 reply.InitializeForTesting(); |
| 841 |
| 842 bool destroy_complex_request = false; |
| 843 EXPECT_FALSE(server.MachMessageServerFunction( |
| 844 reinterpret_cast<mach_msg_header_t*>(&request), |
| 845 reinterpret_cast<mach_msg_header_t*>(&reply), |
| 846 &destroy_complex_request)); |
| 847 |
| 848 // The request wasn’t handled, nothing got a chance to change the value of |
| 849 // this variable. MachMessageServer would destroy the request if it was |
| 850 // complex, regardless of what was done to this variable, because the |
| 851 // return code was not KERN_SUCCESS or MIG_NO_REPLY. |
| 852 EXPECT_FALSE(destroy_complex_request); |
| 853 |
| 854 reply.Verify(id); |
| 855 } |
| 856 } |
| 857 |
| 858 class TestExcServerVariants : public UniversalMachExcServer, |
| 859 public MachMultiprocess { |
| 860 public: |
| 861 TestExcServerVariants(exception_behavior_t behavior, |
| 862 thread_state_flavor_t flavor, |
| 863 mach_msg_type_number_t state_count) |
| 864 : UniversalMachExcServer(), |
| 865 MachMultiprocess(), |
| 866 behavior_(behavior), |
| 867 flavor_(flavor), |
| 868 state_count_(state_count), |
| 869 handled_(false) { |
| 870 } |
| 871 |
| 872 // UniversalMachExcServer: |
| 873 |
| 874 virtual kern_return_t CatchMachException( |
| 875 exception_behavior_t behavior, |
| 876 exception_handler_t exception_port, |
| 877 thread_t thread, |
| 878 task_t task, |
| 879 exception_type_t exception, |
| 880 const mach_exception_data_type_t* code, |
| 881 mach_msg_type_number_t code_count, |
| 882 thread_state_flavor_t* flavor, |
| 883 const natural_t* old_state, |
| 884 mach_msg_type_number_t old_state_count, |
| 885 thread_state_t new_state, |
| 886 mach_msg_type_number_t* new_state_count, |
| 887 bool* destroy_complex_request) override { |
| 888 *destroy_complex_request = true; |
| 889 |
| 890 EXPECT_FALSE(handled_); |
| 891 handled_ = true; |
| 892 |
| 893 EXPECT_EQ(behavior_, behavior); |
| 894 exception_behavior_t basic_behavior = behavior & ~MACH_EXCEPTION_CODES; |
| 895 const bool has_identity = basic_behavior == EXCEPTION_DEFAULT || |
| 896 basic_behavior == EXCEPTION_STATE_IDENTITY; |
| 897 const bool has_state = basic_behavior == EXCEPTION_STATE || |
| 898 basic_behavior == EXCEPTION_STATE_IDENTITY; |
| 899 |
| 900 EXPECT_EQ(LocalPort(), exception_port); |
| 901 |
| 902 if (has_identity) { |
| 903 EXPECT_NE(kMachPortNull, thread); |
| 904 EXPECT_EQ(ChildTask(), task); |
| 905 } else { |
| 906 EXPECT_EQ(kMachPortNull, thread); |
| 907 EXPECT_EQ(kMachPortNull, task); |
| 908 } |
| 909 |
| 910 EXPECT_EQ(EXC_CRASH, exception); |
| 911 EXPECT_EQ(2u, code_count); |
| 912 |
| 913 if (code_count > 1) { |
| 914 // The signal that terminated the process is stored in code[0] along with |
| 915 // some other data. See 10.9.4 xnu-2422.110.17/bsd/kern/kern_exit.c |
| 916 // proc_prepareexit(). |
| 917 int sig = (code[0] >> 24) & 0xff; |
| 918 SetExpectedChildTermination(kTerminationSignal, sig); |
| 919 } |
| 920 |
| 921 if (has_state) { |
| 922 EXPECT_EQ(flavor_, *flavor); |
| 923 EXPECT_EQ(state_count_, old_state_count); |
| 924 EXPECT_NE(static_cast<const natural_t*>(NULL), old_state); |
| 925 EXPECT_EQ(static_cast<mach_msg_type_number_t>(THREAD_STATE_MAX), |
| 926 *new_state_count); |
| 927 EXPECT_NE(static_cast<const natural_t*>(NULL), new_state); |
| 928 } else { |
| 929 EXPECT_EQ(THREAD_STATE_NONE, *flavor); |
| 930 EXPECT_EQ(0u, old_state_count); |
| 931 EXPECT_EQ(NULL, old_state); |
| 932 EXPECT_EQ(0u, *new_state_count); |
| 933 EXPECT_EQ(NULL, new_state); |
| 934 } |
| 935 |
| 936 // Even for an EXC_CRASH handler, returning KERN_SUCCESS with a |
| 937 // state-carrying reply will cause the kernel to try to set a new thread |
| 938 // state, leading to a perceptible waste of time. Returning |
| 939 // MACH_RCV_PORT_DIED is the only way to suppress this behavior while also |
| 940 // preventing the kernel from looking for another (host-level) EXC_CRASH |
| 941 // handler. See 10.9.4 xnu-2422.110.17/osfmk/kern/exception.c |
| 942 // exception_triage(). |
| 943 return has_state ? MACH_RCV_PORT_DIED : KERN_SUCCESS; |
| 944 } |
| 945 |
| 946 private: |
| 947 // MachMultiprocess: |
| 948 |
| 949 virtual void MachMultiprocessParent() override { |
| 950 kern_return_t kr = MachMessageServer::Run(this, |
| 951 LocalPort(), |
| 952 MACH_MSG_OPTION_NONE, |
| 953 MachMessageServer::kOneShot, |
| 954 MachMessageServer::kBlocking, |
| 955 0); |
| 956 EXPECT_EQ(KERN_SUCCESS, kr) |
| 957 << MachErrorMessage(kr, "MachMessageServer::Run"); |
| 958 |
| 959 EXPECT_TRUE(handled_); |
| 960 } |
| 961 |
| 962 virtual void MachMultiprocessChild() override { |
| 963 // Set the parent as the exception handler for EXC_CRASH. |
| 964 kern_return_t kr = task_set_exception_ports( |
| 965 mach_task_self(), EXC_MASK_CRASH, RemotePort(), behavior_, flavor_); |
| 966 ASSERT_EQ(KERN_SUCCESS, kr) |
| 967 << MachErrorMessage(kr, "task_set_exception_ports"); |
| 968 |
| 969 // Now crash. |
| 970 __builtin_trap(); |
| 971 } |
| 972 |
| 973 exception_behavior_t behavior_; |
| 974 thread_state_flavor_t flavor_; |
| 975 mach_msg_type_number_t state_count_; |
| 976 bool handled_; |
| 977 |
| 978 DISALLOW_COPY_AND_ASSIGN(TestExcServerVariants); |
| 979 }; |
| 980 |
| 981 TEST(ExcServerVariants, ExceptionRaise) { |
| 982 TestExcServerVariants test_exc_server_variants( |
| 983 EXCEPTION_DEFAULT, THREAD_STATE_NONE, 0); |
| 984 test_exc_server_variants.Run(); |
| 985 } |
| 986 |
| 987 TEST(ExcServerVariants, ExceptionRaiseState) { |
| 988 TestExcServerVariants test_exc_server_variants( |
| 989 EXCEPTION_STATE, MACHINE_THREAD_STATE, MACHINE_THREAD_STATE_COUNT); |
| 990 test_exc_server_variants.Run(); |
| 991 } |
| 992 |
| 993 TEST(ExcServerVariants, ExceptionRaiseStateIdentity) { |
| 994 TestExcServerVariants test_exc_server_variants(EXCEPTION_STATE_IDENTITY, |
| 995 MACHINE_THREAD_STATE, |
| 996 MACHINE_THREAD_STATE_COUNT); |
| 997 test_exc_server_variants.Run(); |
| 998 } |
| 999 |
| 1000 TEST(ExcServerVariants, MachExceptionRaise) { |
| 1001 TestExcServerVariants test_exc_server_variants( |
| 1002 MACH_EXCEPTION_CODES | EXCEPTION_DEFAULT, THREAD_STATE_NONE, 0); |
| 1003 test_exc_server_variants.Run(); |
| 1004 } |
| 1005 |
| 1006 TEST(ExcServerVariants, MachExceptionRaiseState) { |
| 1007 TestExcServerVariants test_exc_server_variants( |
| 1008 MACH_EXCEPTION_CODES | EXCEPTION_STATE, |
| 1009 MACHINE_THREAD_STATE, |
| 1010 MACHINE_THREAD_STATE_COUNT); |
| 1011 test_exc_server_variants.Run(); |
| 1012 } |
| 1013 |
| 1014 TEST(ExcServerVariants, MachExceptionRaiseStateIdentity) { |
| 1015 TestExcServerVariants test_exc_server_variants( |
| 1016 MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY, |
| 1017 MACHINE_THREAD_STATE, |
| 1018 MACHINE_THREAD_STATE_COUNT); |
| 1019 test_exc_server_variants.Run(); |
| 1020 } |
| 1021 |
| 1022 TEST(ExcServerVariants, ThreadStates) { |
| 1023 // So far, all of the tests worked with MACHINE_THREAD_STATE. Now try all of |
| 1024 // the other thread state flavors that are expected to work. |
| 1025 |
| 1026 struct TestData { |
| 1027 thread_state_flavor_t flavor; |
| 1028 mach_msg_type_number_t count; |
| 1029 }; |
| 1030 const TestData test_data[] = { |
| 1031 #if defined(ARCH_CPU_X86_FAMILY) |
| 1032 #if defined(ARCH_CPU_X86) |
| 1033 { x86_THREAD_STATE32, x86_THREAD_STATE32_COUNT }, |
| 1034 { x86_FLOAT_STATE32, x86_FLOAT_STATE32_COUNT }, |
| 1035 { x86_EXCEPTION_STATE32, x86_EXCEPTION_STATE32_COUNT }, |
| 1036 { x86_DEBUG_STATE32, x86_DEBUG_STATE32_COUNT }, |
| 1037 // Don’t test x86_AVX_STATE32 because it’s not available on all CPUs and |
| 1038 // OS versionns. |
| 1039 #endif |
| 1040 #if defined(ARCH_CPU_X86_64) |
| 1041 { x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT }, |
| 1042 { x86_FLOAT_STATE64, x86_FLOAT_STATE64_COUNT }, |
| 1043 { x86_EXCEPTION_STATE64, x86_EXCEPTION_STATE64_COUNT }, |
| 1044 { x86_DEBUG_STATE64, x86_DEBUG_STATE64_COUNT }, |
| 1045 // Don’t test x86_AVX_STATE64 because it’s not available on all CPUs and |
| 1046 // OS versions. |
| 1047 #endif |
| 1048 { x86_THREAD_STATE, x86_THREAD_STATE_COUNT }, |
| 1049 { x86_FLOAT_STATE, x86_FLOAT_STATE_COUNT }, |
| 1050 { x86_EXCEPTION_STATE, x86_EXCEPTION_STATE_COUNT }, |
| 1051 { x86_DEBUG_STATE, x86_DEBUG_STATE_COUNT }, |
| 1052 // Don’t test x86_AVX_STATE because it’s not available on all CPUs and OS |
| 1053 // versions. |
| 1054 #else |
| 1055 #error Port this test to your CPU architecture. |
| 1056 #endif |
| 1057 }; |
| 1058 |
| 1059 for (size_t index = 0; index < arraysize(test_data); ++index) { |
| 1060 const TestData& test = test_data[index]; |
| 1061 SCOPED_TRACE(base::StringPrintf( |
| 1062 "index %zu, flavor %d", index, test.flavor)); |
| 1063 |
| 1064 TestExcServerVariants test_exc_server_variants( |
| 1065 MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY, |
| 1066 test.flavor, |
| 1067 test.count); |
| 1068 test_exc_server_variants.Run(); |
| 1069 } |
| 1070 } |
| 1071 |
| 1072 } // namespace |
OLD | NEW |