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

Unified Diff: util/mach/exc_server_variants_test.cc

Issue 545053003: Add exc_server_variants including UniversalMachExcServer and its test (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Address review feedback Created 6 years, 3 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « util/mach/exc_server_variants.cc ('k') | util/mach/mach_extensions.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: util/mach/exc_server_variants_test.cc
diff --git a/util/mach/exc_server_variants_test.cc b/util/mach/exc_server_variants_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a942bb8aee82111b61b550f925f04e4fdf0f668f
--- /dev/null
+++ b/util/mach/exc_server_variants_test.cc
@@ -0,0 +1,1072 @@
+// Copyright 2014 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "util/mach/exc_server_variants.h"
+
+#include <mach/mach.h>
+#include <string.h>
+
+#include "base/basictypes.h"
+#include "base/strings/stringprintf.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "util/mach/mach_extensions.h"
+#include "util/test/mac/mach_errors.h"
+#include "util/test/mac/mach_multiprocess.h"
+
+namespace {
+
+using namespace crashpad;
+using namespace crashpad::test;
+using namespace testing;
+
+// Fake Mach ports. These aren’t used as ports in these tests, they’re just used
+// as cookies to make sure that the correct values get passed to the correct
+// places.
+const mach_port_t kClientRemotePort = 0x01010101;
+const mach_port_t kServerLocalPort = 0x02020202;
+const mach_port_t kExceptionThreadPort = 0x03030303;
+const mach_port_t kExceptionTaskPort = 0x04040404;
+
+// Other fake exception values.
+const exception_type_t kExceptionType = EXC_BAD_ACCESS;
+
+// Test using an exception code with the high bit set to ensure that it gets
+// promoted to the wider mach_exception_data_type_t type as a signed quantity.
+const exception_data_type_t kExceptionCodes[] = {
+ KERN_PROTECTION_FAILURE,
+ static_cast<exception_data_type_t>(0xfedcba98),
+};
+
+const exception_data_type_t kMachExceptionCodes[] = {
+ KERN_PROTECTION_FAILURE,
+ static_cast<exception_data_type_t>(0xfedcba9876543210),
+};
+
+const thread_state_flavor_t kThreadStateFlavor = MACHINE_THREAD_STATE;
+const mach_msg_type_number_t kThreadStateFlavorCount =
+ MACHINE_THREAD_STATE_COUNT;
+
+void InitializeMachMsgPortDescriptor(mach_msg_port_descriptor_t* descriptor,
+ mach_port_t port) {
+ descriptor->name = port;
+ descriptor->disposition = MACH_MSG_TYPE_MOVE_SEND;
+ descriptor->type = MACH_MSG_PORT_DESCRIPTOR;
+}
+
+// The definitions of the request and reply structures from mach_exc.h aren’t
+// available here. They need custom initialization code, and the reply
+// structures need verification code too, so duplicate the expected definitions
+// of the structures from both exc.h and mach_exc.h here in this file, and
+// provide the initialization and verification code as methods in true
+// object-oriented fashion.
+
+struct __attribute__((packed, aligned(4))) ExceptionRaiseRequest {
+ mach_msg_header_t Head;
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ integer_t code[2];
+
+ void InitializeForTesting() {
+ memset(this, 0xa5, sizeof(*this));
+ Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) |
+ MACH_MSGH_BITS_COMPLEX;
+ Head.msgh_size = sizeof(*this);
+ Head.msgh_remote_port = kClientRemotePort;
+ Head.msgh_local_port = kServerLocalPort;
+ Head.msgh_id = 2401;
+ msgh_body.msgh_descriptor_count = 2;
+ InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort);
+ InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort);
+ NDR = NDR_record;
+ exception = kExceptionType;
+ codeCnt = 2;
+ code[0] = kExceptionCodes[0];
+ code[1] = kExceptionCodes[1];
+ }
+};
+
+struct __attribute__((packed, aligned(4))) ExceptionRaiseReply {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ kern_return_t RetCode;
+
+ void InitializeForTesting() {
+ memset(this, 0x5a, sizeof(*this));
+ RetCode = KERN_FAILURE;
+ }
+
+ // Verify accepts a |behavior| parameter because the same message format and
+ // verification function is used for ExceptionRaiseReply and
+ // MachExceptionRaiseReply. Knowing which behavior is expected allows the
+ // message ID to be checked.
+ void Verify(exception_behavior_t behavior) {
+ EXPECT_EQ(static_cast<mach_msg_bits_t>(
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0)),
+ Head.msgh_bits);
+ EXPECT_EQ(sizeof(*this), Head.msgh_size);
+ EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port);
+ EXPECT_EQ(kMachPortNull, Head.msgh_local_port);
+ switch (behavior) {
+ case EXCEPTION_DEFAULT:
+ EXPECT_EQ(2501, Head.msgh_id);
+ break;
+ case static_cast<exception_behavior_t>(EXCEPTION_DEFAULT |
+ MACH_EXCEPTION_CODES):
+ EXPECT_EQ(2505, Head.msgh_id);
+ break;
+ default:
+ ADD_FAILURE() << "behavior " << behavior << ", Head.msgh_id "
+ << Head.msgh_id;
+ break;
+ }
+ EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR)));
+ EXPECT_EQ(KERN_SUCCESS, RetCode);
+ }
+};
+
+struct __attribute__((packed, aligned(4))) ExceptionRaiseStateRequest {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ integer_t code[2];
+ int flavor;
+ mach_msg_type_number_t old_stateCnt;
+ natural_t old_state[THREAD_STATE_MAX];
+
+ void InitializeForTesting() {
+ memset(this, 0xa5, sizeof(*this));
+ Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ Head.msgh_size = sizeof(*this);
+ Head.msgh_remote_port = kClientRemotePort;
+ Head.msgh_local_port = kServerLocalPort;
+ Head.msgh_id = 2402;
+ NDR = NDR_record;
+ exception = kExceptionType;
+ codeCnt = 2;
+ code[0] = kExceptionCodes[0];
+ code[1] = kExceptionCodes[1];
+ flavor = kThreadStateFlavor;
+ old_stateCnt = kThreadStateFlavorCount;
+
+ // Adjust the message size for the data that it’s actually carrying, which
+ // may be smaller than the maximum that it can carry.
+ Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state);
+ }
+};
+
+struct __attribute__((packed, aligned(4))) ExceptionRaiseStateReply {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ kern_return_t RetCode;
+ int flavor;
+ mach_msg_type_number_t new_stateCnt;
+ natural_t new_state[THREAD_STATE_MAX];
+
+ void InitializeForTesting() {
+ memset(this, 0x5a, sizeof(*this));
+ RetCode = KERN_FAILURE;
+ }
+
+ // Verify accepts a |behavior| parameter because the same message format and
+ // verification function is used for ExceptionRaiseStateReply,
+ // ExceptionRaiseStateIdentityReply, MachExceptionRaiseStateReply, and
+ // MachExceptionRaiseStateIdentityReply. Knowing which behavior is expected
+ // allows the message ID to be checked.
+ void Verify(exception_behavior_t behavior) {
+ EXPECT_EQ(static_cast<mach_msg_bits_t>(
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0)),
+ Head.msgh_bits);
+ EXPECT_EQ(sizeof(*this), Head.msgh_size);
+ EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port);
+ EXPECT_EQ(kMachPortNull, Head.msgh_local_port);
+ switch (behavior) {
+ case EXCEPTION_STATE:
+ EXPECT_EQ(2502, Head.msgh_id);
+ break;
+ case EXCEPTION_STATE_IDENTITY:
+ EXPECT_EQ(2503, Head.msgh_id);
+ break;
+ case static_cast<exception_behavior_t>(EXCEPTION_STATE |
+ MACH_EXCEPTION_CODES):
+ EXPECT_EQ(2506, Head.msgh_id);
+ break;
+ case static_cast<exception_behavior_t>(EXCEPTION_STATE_IDENTITY |
+ MACH_EXCEPTION_CODES):
+ EXPECT_EQ(2507, Head.msgh_id);
+ break;
+ default:
+ ADD_FAILURE() << "behavior " << behavior << ", Head.msgh_id "
+ << Head.msgh_id;
+ break;
+ }
+ EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR)));
+ EXPECT_EQ(KERN_SUCCESS, RetCode);
+ EXPECT_EQ(kThreadStateFlavor, flavor);
+ EXPECT_EQ(arraysize(new_state), new_stateCnt);
+ }
+};
+
+struct __attribute__((packed, aligned(4))) ExceptionRaiseStateIdentityRequest {
+ mach_msg_header_t Head;
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ integer_t code[2];
+ int flavor;
+ mach_msg_type_number_t old_stateCnt;
+ natural_t old_state[THREAD_STATE_MAX];
+
+ void InitializeForTesting() {
+ memset(this, 0xa5, sizeof(*this));
+ Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) |
+ MACH_MSGH_BITS_COMPLEX;
+ Head.msgh_size = sizeof(*this);
+ Head.msgh_remote_port = kClientRemotePort;
+ Head.msgh_local_port = kServerLocalPort;
+ Head.msgh_id = 2403;
+ msgh_body.msgh_descriptor_count = 2;
+ InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort);
+ InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort);
+ NDR = NDR_record;
+ exception = kExceptionType;
+ codeCnt = 2;
+ code[0] = kExceptionCodes[0];
+ code[1] = kExceptionCodes[1];
+ flavor = kThreadStateFlavor;
+ old_stateCnt = kThreadStateFlavorCount;
+
+ // Adjust the message size for the data that it’s actually carrying, which
+ // may be smaller than the maximum that it can carry.
+ Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state);
+ }
+};
+
+// The reply messages for exception_raise_state and
+// exception_raise_state_identity are identical.
+typedef ExceptionRaiseStateReply ExceptionRaiseStateIdentityReply;
+
+struct __attribute__((packed, aligned(4))) MachExceptionRaiseRequest {
+ mach_msg_header_t Head;
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ int64_t code[2];
+
+ void InitializeForTesting() {
+ memset(this, 0xa5, sizeof(*this));
+ Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) |
+ MACH_MSGH_BITS_COMPLEX;
+ Head.msgh_size = sizeof(*this);
+ Head.msgh_remote_port = kClientRemotePort;
+ Head.msgh_local_port = kServerLocalPort;
+ Head.msgh_id = 2405;
+ msgh_body.msgh_descriptor_count = 2;
+ InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort);
+ InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort);
+ NDR = NDR_record;
+ exception = kExceptionType;
+ codeCnt = 2;
+ code[0] = kMachExceptionCodes[0];
+ code[1] = kMachExceptionCodes[1];
+ }
+};
+
+// The reply messages for exception_raise and mach_exception_raise are
+// identical.
+typedef ExceptionRaiseReply MachExceptionRaiseReply;
+
+struct __attribute__((packed, aligned(4))) MachExceptionRaiseStateRequest {
+ mach_msg_header_t Head;
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ int64_t code[2];
+ int flavor;
+ mach_msg_type_number_t old_stateCnt;
+ natural_t old_state[THREAD_STATE_MAX];
+
+ void InitializeForTesting() {
+ memset(this, 0xa5, sizeof(*this));
+ Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ Head.msgh_size = sizeof(*this);
+ Head.msgh_remote_port = kClientRemotePort;
+ Head.msgh_local_port = kServerLocalPort;
+ Head.msgh_id = 2406;
+ NDR = NDR_record;
+ exception = kExceptionType;
+ codeCnt = 2;
+ code[0] = kMachExceptionCodes[0];
+ code[1] = kMachExceptionCodes[1];
+ flavor = kThreadStateFlavor;
+ old_stateCnt = kThreadStateFlavorCount;
+
+ // Adjust the message size for the data that it’s actually carrying, which
+ // may be smaller than the maximum that it can carry.
+ Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state);
+ }
+};
+
+// The reply messages for exception_raise_state and mach_exception_raise_state
+// are identical.
+typedef ExceptionRaiseStateReply MachExceptionRaiseStateReply;
+
+struct __attribute__((packed,
+ aligned(4))) MachExceptionRaiseStateIdentityRequest {
+ mach_msg_header_t Head;
+ mach_msg_body_t msgh_body;
+ mach_msg_port_descriptor_t thread;
+ mach_msg_port_descriptor_t task;
+ NDR_record_t NDR;
+ exception_type_t exception;
+ mach_msg_type_number_t codeCnt;
+ int64_t code[2];
+ int flavor;
+ mach_msg_type_number_t old_stateCnt;
+ natural_t old_state[THREAD_STATE_MAX];
+
+ void InitializeForTesting() {
+ memset(this, 0xa5, sizeof(*this));
+ Head.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE) |
+ MACH_MSGH_BITS_COMPLEX;
+ Head.msgh_size = sizeof(*this);
+ Head.msgh_remote_port = kClientRemotePort;
+ Head.msgh_local_port = kServerLocalPort;
+ Head.msgh_id = 2407;
+ msgh_body.msgh_descriptor_count = 2;
+ InitializeMachMsgPortDescriptor(&thread, kExceptionThreadPort);
+ InitializeMachMsgPortDescriptor(&task, kExceptionTaskPort);
+ NDR = NDR_record;
+ exception = kExceptionType;
+ codeCnt = 2;
+ code[0] = kMachExceptionCodes[0];
+ code[1] = kMachExceptionCodes[1];
+ flavor = kThreadStateFlavor;
+ old_stateCnt = kThreadStateFlavorCount;
+
+ // Adjust the message size for the data that it’s actually carrying, which
+ // may be smaller than the maximum that it can carry.
+ Head.msgh_size += sizeof(old_state[0]) * old_stateCnt - sizeof(old_state);
+ }
+};
+
+// The reply messages for exception_raise_state_identity and
+// mach_exception_raise_state_identity are identical.
+typedef ExceptionRaiseStateIdentityReply MachExceptionRaiseStateIdentityReply;
+
+// InvalidRequest and BadIDErrorReply are used to test that
+// UniversalMachExcServer deals appropriately with messages that it does not
+// understand: messages with an unknown Head.msgh_id.
+
+struct __attribute__((packed, aligned(4))) InvalidRequest
+ : public mach_msg_empty_send_t {
+ void InitializeForTesting(mach_msg_id_t id) {
+ memset(this, 0xa5, sizeof(*this));
+ header.msgh_bits =
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND_ONCE);
+ header.msgh_size = sizeof(*this);
+ header.msgh_remote_port = kClientRemotePort;
+ header.msgh_local_port = kServerLocalPort;
+ header.msgh_id = id;
+ }
+};
+
+struct __attribute__((packed, aligned(4))) BadIDErrorReply
+ : public mig_reply_error_t {
+ void InitializeForTesting() {
+ memset(this, 0x5a, sizeof(*this));
+ RetCode = KERN_FAILURE;
+ }
+
+ void Verify(mach_msg_id_t id) {
+ EXPECT_EQ(static_cast<mach_msg_bits_t>(
+ MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0)),
+ Head.msgh_bits);
+ EXPECT_EQ(sizeof(*this), Head.msgh_size);
+ EXPECT_EQ(kClientRemotePort, Head.msgh_remote_port);
+ EXPECT_EQ(kMachPortNull, Head.msgh_local_port);
+ EXPECT_EQ(id + 100, Head.msgh_id);
+ EXPECT_EQ(0, memcmp(&NDR, &NDR_record, sizeof(NDR)));
+ EXPECT_EQ(MIG_BAD_ID, RetCode);
+ }
+};
+
+class MockUniversalMachExcServer : public UniversalMachExcServer {
+ public:
+ struct ConstExceptionCodes {
+ const mach_exception_data_type_t* code;
+ mach_msg_type_number_t code_count;
+ };
+ struct ThreadState {
+ thread_state_t state;
+ mach_msg_type_number_t* state_count;
+ };
+ struct ConstThreadState {
+ const natural_t* state;
+ mach_msg_type_number_t* state_count;
+ };
+
+ // CatchMachException is the method to mock, but it has 13 parameters, and
+ // gmock can only mock methods with up to 10 parameters. Coalesce some related
+ // parameters together into structs, and call a mocked method.
+ virtual kern_return_t CatchMachException(
+ exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ const natural_t* old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ bool* destroy_complex_request) override {
+ *destroy_complex_request = true;
+ const ConstExceptionCodes exception_codes = {code, code_count};
+ const ConstThreadState old_thread_state = {old_state, &old_state_count};
+ ThreadState new_thread_state = {new_state, new_state_count};
+ return MockCatchMachException(behavior,
+ exception_port,
+ thread,
+ task,
+ exception,
+ &exception_codes,
+ flavor,
+ &old_thread_state,
+ &new_thread_state);
+ }
+
+ MOCK_METHOD9(MockCatchMachException,
+ kern_return_t(exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const ConstExceptionCodes* exception_codes,
+ thread_state_flavor_t* flavor,
+ const ConstThreadState* old_thread_state,
+ ThreadState* new_thread_state));
+};
+
+// Matcher for ConstExceptionCodes, testing that it carries 2 codes matching
+// code_0 and code_1.
+MATCHER_P2(AreExceptionCodes, code_0, code_1, "") {
+ if (!arg) {
+ return false;
+ }
+
+ if (arg->code_count == 2 && arg->code[0] == code_0 &&
+ arg->code[1] == code_1) {
+ return true;
+ }
+
+ *result_listener << "codes (";
+ for (size_t index = 0; index < arg->code_count; ++index) {
+ *result_listener << arg->code[index];
+ if (index < arg->code_count - 1) {
+ *result_listener << ", ";
+ }
+ }
+ *result_listener << ")";
+
+ return false;
+}
+
+// Matcher for ThreadState and ConstThreadState, testing that *state_count is
+// present and matches the specified value. If 0 is specified for the count,
+// state must be NULL (not present), otherwise state must be non-NULL (present).
+MATCHER_P(IsThreadStateCount, state_count, "") {
+ if (!arg) {
+ return false;
+ }
+ if (!arg->state_count) {
+ *result_listener << "state_count NULL";
+ return false;
+ }
+ if (*(arg->state_count) != state_count) {
+ *result_listener << "*state_count " << *(arg->state_count);
+ return false;
+ }
+ if (state_count) {
+ if (!arg->state) {
+ *result_listener << "*state_count " << state_count << ", state NULL";
+ return false;
+ }
+ } else {
+ if (arg->state) {
+ *result_listener << "*state_count 0, state non-NULL (" << arg->state
+ << ")";
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename T>
+class ScopedDefaultValue {
+ public:
+ explicit ScopedDefaultValue(const T& default_value) {
+ DefaultValue<T>::Set(default_value);
+ }
+
+ ~ScopedDefaultValue() { DefaultValue<T>::Clear(); }
+};
+
+TEST(ExcServerVariants, MockExceptionRaise) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+
+ ExceptionRaiseRequest request;
+ EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize());
+ request.InitializeForTesting();
+
+ ExceptionRaiseReply reply;
+ EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize());
+ reply.InitializeForTesting();
+
+ const exception_behavior_t kExceptionBehavior = EXCEPTION_DEFAULT;
+
+ EXPECT_CALL(server,
+ MockCatchMachException(
+ kExceptionBehavior,
+ kServerLocalPort,
+ kExceptionThreadPort,
+ kExceptionTaskPort,
+ kExceptionType,
+ AreExceptionCodes(kExceptionCodes[0], kExceptionCodes[1]),
+ Pointee(Eq(THREAD_STATE_NONE)),
+ IsThreadStateCount(0u),
+ IsThreadStateCount(0u)))
+ .WillOnce(Return(KERN_SUCCESS))
+ .RetiresOnSaturation();
+
+ bool destroy_complex_request = false;
+ EXPECT_TRUE(server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+ EXPECT_TRUE(destroy_complex_request);
+
+ reply.Verify(kExceptionBehavior);
+}
+
+TEST(ExcServerVariants, MockExceptionRaiseState) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+
+ ExceptionRaiseStateRequest request;
+ EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize());
+ request.InitializeForTesting();
+
+ ExceptionRaiseStateReply reply;
+ EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize());
+ reply.InitializeForTesting();
+
+ const exception_behavior_t kExceptionBehavior = EXCEPTION_STATE;
+
+ EXPECT_CALL(server,
+ MockCatchMachException(
+ kExceptionBehavior,
+ kServerLocalPort,
+ MACH_PORT_NULL,
+ MACH_PORT_NULL,
+ kExceptionType,
+ AreExceptionCodes(kExceptionCodes[0], kExceptionCodes[1]),
+ Pointee(Eq(kThreadStateFlavor)),
+ IsThreadStateCount(kThreadStateFlavorCount),
+ IsThreadStateCount(arraysize(reply.new_state))))
+ .WillOnce(Return(KERN_SUCCESS))
+ .RetiresOnSaturation();
+
+ bool destroy_complex_request = false;
+ EXPECT_TRUE(server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+
+ // The request wasn’t complex, so nothing got a chance to change the value of
+ // this variable.
+ EXPECT_FALSE(destroy_complex_request);
+
+ reply.Verify(kExceptionBehavior);
+}
+
+TEST(ExcServerVariants, MockExceptionRaiseStateIdentity) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+
+ ExceptionRaiseStateIdentityRequest request;
+ EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize());
+ request.InitializeForTesting();
+
+ ExceptionRaiseStateIdentityReply reply;
+ EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize());
+ reply.InitializeForTesting();
+
+ const exception_behavior_t kExceptionBehavior = EXCEPTION_STATE_IDENTITY;
+
+ EXPECT_CALL(server,
+ MockCatchMachException(
+ kExceptionBehavior,
+ kServerLocalPort,
+ kExceptionThreadPort,
+ kExceptionTaskPort,
+ kExceptionType,
+ AreExceptionCodes(kExceptionCodes[0], kExceptionCodes[1]),
+ Pointee(Eq(kThreadStateFlavor)),
+ IsThreadStateCount(kThreadStateFlavorCount),
+ IsThreadStateCount(arraysize(reply.new_state))))
+ .WillOnce(Return(KERN_SUCCESS))
+ .RetiresOnSaturation();
+
+ bool destroy_complex_request = false;
+ EXPECT_TRUE(server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+ EXPECT_TRUE(destroy_complex_request);
+
+ reply.Verify(kExceptionBehavior);
+}
+
+TEST(ExcServerVariants, MockMachExceptionRaise) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+
+ MachExceptionRaiseRequest request;
+ EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize());
+ request.InitializeForTesting();
+
+ MachExceptionRaiseReply reply;
+ EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize());
+ reply.InitializeForTesting();
+
+ const exception_behavior_t kExceptionBehavior =
+ EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES;
+
+ EXPECT_CALL(
+ server,
+ MockCatchMachException(
+ kExceptionBehavior,
+ kServerLocalPort,
+ kExceptionThreadPort,
+ kExceptionTaskPort,
+ kExceptionType,
+ AreExceptionCodes(kMachExceptionCodes[0], kMachExceptionCodes[1]),
+ Pointee(Eq(THREAD_STATE_NONE)),
+ IsThreadStateCount(0u),
+ IsThreadStateCount(0u)))
+ .WillOnce(Return(KERN_SUCCESS))
+ .RetiresOnSaturation();
+
+ bool destroy_complex_request = false;
+ EXPECT_TRUE(server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+ EXPECT_TRUE(destroy_complex_request);
+
+ reply.Verify(kExceptionBehavior);
+}
+
+TEST(ExcServerVariants, MockMachExceptionRaiseState) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+
+ MachExceptionRaiseStateRequest request;
+ EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize());
+ request.InitializeForTesting();
+
+ MachExceptionRaiseStateReply reply;
+ EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize());
+ reply.InitializeForTesting();
+
+ const exception_behavior_t kExceptionBehavior =
+ EXCEPTION_STATE | MACH_EXCEPTION_CODES;
+
+ EXPECT_CALL(
+ server,
+ MockCatchMachException(
+ kExceptionBehavior,
+ kServerLocalPort,
+ MACH_PORT_NULL,
+ MACH_PORT_NULL,
+ kExceptionType,
+ AreExceptionCodes(kMachExceptionCodes[0], kMachExceptionCodes[1]),
+ Pointee(Eq(kThreadStateFlavor)),
+ IsThreadStateCount(kThreadStateFlavorCount),
+ IsThreadStateCount(arraysize(reply.new_state))))
+ .WillOnce(Return(KERN_SUCCESS))
+ .RetiresOnSaturation();
+
+ bool destroy_complex_request = false;
+ EXPECT_TRUE(server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+
+ // The request wasn’t complex, so nothing got a chance to change the value of
+ // this variable.
+ EXPECT_FALSE(destroy_complex_request);
+
+ reply.Verify(kExceptionBehavior);
+}
+
+TEST(ExcServerVariants, MockMachExceptionRaiseStateIdentity) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+
+ MachExceptionRaiseStateIdentityRequest request;
+ EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize());
+ request.InitializeForTesting();
+
+ MachExceptionRaiseStateIdentityReply reply;
+ EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize());
+ reply.InitializeForTesting();
+
+ const exception_behavior_t kExceptionBehavior =
+ EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES;
+
+ EXPECT_CALL(
+ server,
+ MockCatchMachException(
+ kExceptionBehavior,
+ kServerLocalPort,
+ kExceptionThreadPort,
+ kExceptionTaskPort,
+ kExceptionType,
+ AreExceptionCodes(kMachExceptionCodes[0], kMachExceptionCodes[1]),
+ Pointee(Eq(kThreadStateFlavor)),
+ IsThreadStateCount(kThreadStateFlavorCount),
+ IsThreadStateCount(arraysize(reply.new_state))))
+ .WillOnce(Return(KERN_SUCCESS))
+ .RetiresOnSaturation();
+
+ bool destroy_complex_request = false;
+ EXPECT_TRUE(server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+ EXPECT_TRUE(destroy_complex_request);
+
+ reply.Verify(kExceptionBehavior);
+}
+
+TEST(ExcServerVariants, MockUnknownID) {
+ ScopedDefaultValue<kern_return_t> default_kern_return_t(KERN_FAILURE);
+
+ MockUniversalMachExcServer server;
+
+ // Make sure that a message with an unknown ID is handled appropriately.
+ // UniversalMachExcServer should not dispatch the message to
+ // MachMessageServerFunction, but should generate a MIG_BAD_ID error reply.
+
+ const mach_msg_id_t unknown_ids[] = {
+ // Reasonable things to check.
+ -101,
+ -100,
+ -99,
+ -1,
+ 0,
+ 1,
+ 99,
+ 100,
+ 101,
+
+ // Invalid IDs right around valid ones.
+ 2400,
+ 2404,
+ 2408,
+
+ // Valid and invalid IDs in the range used for replies, not requests.
+ 2500,
+ 2501,
+ 2502,
+ 2503,
+ 2504,
+ 2505,
+ 2506,
+ 2507,
+ 2508,
+ };
+
+ for (size_t index = 0; index < arraysize(unknown_ids); ++index) {
+ mach_msg_id_t id = unknown_ids[index];
+
+ SCOPED_TRACE(base::StringPrintf("unknown id %d", id));
+
+ InvalidRequest request;
+ EXPECT_LE(sizeof(request), server.MachMessageServerRequestSize());
+ request.InitializeForTesting(id);
+
+ BadIDErrorReply reply;
+ EXPECT_LE(sizeof(reply), server.MachMessageServerReplySize());
+ reply.InitializeForTesting();
+
+ bool destroy_complex_request = false;
+ EXPECT_FALSE(server.MachMessageServerFunction(
+ reinterpret_cast<mach_msg_header_t*>(&request),
+ reinterpret_cast<mach_msg_header_t*>(&reply),
+ &destroy_complex_request));
+
+ // The request wasn’t handled, nothing got a chance to change the value of
+ // this variable. MachMessageServer would destroy the request if it was
+ // complex, regardless of what was done to this variable, because the
+ // return code was not KERN_SUCCESS or MIG_NO_REPLY.
+ EXPECT_FALSE(destroy_complex_request);
+
+ reply.Verify(id);
+ }
+}
+
+class TestExcServerVariants : public UniversalMachExcServer,
+ public MachMultiprocess {
+ public:
+ TestExcServerVariants(exception_behavior_t behavior,
+ thread_state_flavor_t flavor,
+ mach_msg_type_number_t state_count)
+ : UniversalMachExcServer(),
+ MachMultiprocess(),
+ behavior_(behavior),
+ flavor_(flavor),
+ state_count_(state_count),
+ handled_(false) {
+ }
+
+ // UniversalMachExcServer:
+
+ virtual kern_return_t CatchMachException(
+ exception_behavior_t behavior,
+ exception_handler_t exception_port,
+ thread_t thread,
+ task_t task,
+ exception_type_t exception,
+ const mach_exception_data_type_t* code,
+ mach_msg_type_number_t code_count,
+ thread_state_flavor_t* flavor,
+ const natural_t* old_state,
+ mach_msg_type_number_t old_state_count,
+ thread_state_t new_state,
+ mach_msg_type_number_t* new_state_count,
+ bool* destroy_complex_request) override {
+ *destroy_complex_request = true;
+
+ EXPECT_FALSE(handled_);
+ handled_ = true;
+
+ EXPECT_EQ(behavior_, behavior);
+ exception_behavior_t basic_behavior = behavior & ~MACH_EXCEPTION_CODES;
+ const bool has_identity = basic_behavior == EXCEPTION_DEFAULT ||
+ basic_behavior == EXCEPTION_STATE_IDENTITY;
+ const bool has_state = basic_behavior == EXCEPTION_STATE ||
+ basic_behavior == EXCEPTION_STATE_IDENTITY;
+
+ EXPECT_EQ(LocalPort(), exception_port);
+
+ if (has_identity) {
+ EXPECT_NE(kMachPortNull, thread);
+ EXPECT_EQ(ChildTask(), task);
+ } else {
+ EXPECT_EQ(kMachPortNull, thread);
+ EXPECT_EQ(kMachPortNull, task);
+ }
+
+ EXPECT_EQ(EXC_CRASH, exception);
+ EXPECT_EQ(2u, code_count);
+
+ if (code_count > 1) {
+ // The signal that terminated the process is stored in code[0] along with
+ // some other data. See 10.9.4 xnu-2422.110.17/bsd/kern/kern_exit.c
+ // proc_prepareexit().
+ int sig = (code[0] >> 24) & 0xff;
+ SetExpectedChildTermination(kTerminationSignal, sig);
+ }
+
+ if (has_state) {
+ EXPECT_EQ(flavor_, *flavor);
+ EXPECT_EQ(state_count_, old_state_count);
+ EXPECT_NE(static_cast<const natural_t*>(NULL), old_state);
+ EXPECT_EQ(static_cast<mach_msg_type_number_t>(THREAD_STATE_MAX),
+ *new_state_count);
+ EXPECT_NE(static_cast<const natural_t*>(NULL), new_state);
+ } else {
+ EXPECT_EQ(THREAD_STATE_NONE, *flavor);
+ EXPECT_EQ(0u, old_state_count);
+ EXPECT_EQ(NULL, old_state);
+ EXPECT_EQ(0u, *new_state_count);
+ EXPECT_EQ(NULL, new_state);
+ }
+
+ // Even for an EXC_CRASH handler, returning KERN_SUCCESS with a
+ // state-carrying reply will cause the kernel to try to set a new thread
+ // state, leading to a perceptible waste of time. Returning
+ // MACH_RCV_PORT_DIED is the only way to suppress this behavior while also
+ // preventing the kernel from looking for another (host-level) EXC_CRASH
+ // handler. See 10.9.4 xnu-2422.110.17/osfmk/kern/exception.c
+ // exception_triage().
+ return has_state ? MACH_RCV_PORT_DIED : KERN_SUCCESS;
+ }
+
+ private:
+ // MachMultiprocess:
+
+ virtual void MachMultiprocessParent() override {
+ kern_return_t kr = MachMessageServer::Run(this,
+ LocalPort(),
+ MACH_MSG_OPTION_NONE,
+ MachMessageServer::kOneShot,
+ MachMessageServer::kBlocking,
+ 0);
+ EXPECT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "MachMessageServer::Run");
+
+ EXPECT_TRUE(handled_);
+ }
+
+ virtual void MachMultiprocessChild() override {
+ // Set the parent as the exception handler for EXC_CRASH.
+ kern_return_t kr = task_set_exception_ports(
+ mach_task_self(), EXC_MASK_CRASH, RemotePort(), behavior_, flavor_);
+ ASSERT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "task_set_exception_ports");
+
+ // Now crash.
+ __builtin_trap();
+ }
+
+ exception_behavior_t behavior_;
+ thread_state_flavor_t flavor_;
+ mach_msg_type_number_t state_count_;
+ bool handled_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestExcServerVariants);
+};
+
+TEST(ExcServerVariants, ExceptionRaise) {
+ TestExcServerVariants test_exc_server_variants(
+ EXCEPTION_DEFAULT, THREAD_STATE_NONE, 0);
+ test_exc_server_variants.Run();
+}
+
+TEST(ExcServerVariants, ExceptionRaiseState) {
+ TestExcServerVariants test_exc_server_variants(
+ EXCEPTION_STATE, MACHINE_THREAD_STATE, MACHINE_THREAD_STATE_COUNT);
+ test_exc_server_variants.Run();
+}
+
+TEST(ExcServerVariants, ExceptionRaiseStateIdentity) {
+ TestExcServerVariants test_exc_server_variants(EXCEPTION_STATE_IDENTITY,
+ MACHINE_THREAD_STATE,
+ MACHINE_THREAD_STATE_COUNT);
+ test_exc_server_variants.Run();
+}
+
+TEST(ExcServerVariants, MachExceptionRaise) {
+ TestExcServerVariants test_exc_server_variants(
+ MACH_EXCEPTION_CODES | EXCEPTION_DEFAULT, THREAD_STATE_NONE, 0);
+ test_exc_server_variants.Run();
+}
+
+TEST(ExcServerVariants, MachExceptionRaiseState) {
+ TestExcServerVariants test_exc_server_variants(
+ MACH_EXCEPTION_CODES | EXCEPTION_STATE,
+ MACHINE_THREAD_STATE,
+ MACHINE_THREAD_STATE_COUNT);
+ test_exc_server_variants.Run();
+}
+
+TEST(ExcServerVariants, MachExceptionRaiseStateIdentity) {
+ TestExcServerVariants test_exc_server_variants(
+ MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY,
+ MACHINE_THREAD_STATE,
+ MACHINE_THREAD_STATE_COUNT);
+ test_exc_server_variants.Run();
+}
+
+TEST(ExcServerVariants, ThreadStates) {
+ // So far, all of the tests worked with MACHINE_THREAD_STATE. Now try all of
+ // the other thread state flavors that are expected to work.
+
+ struct TestData {
+ thread_state_flavor_t flavor;
+ mach_msg_type_number_t count;
+ };
+ const TestData test_data[] = {
+#if defined(ARCH_CPU_X86_FAMILY)
+#if defined(ARCH_CPU_X86)
+ { x86_THREAD_STATE32, x86_THREAD_STATE32_COUNT },
+ { x86_FLOAT_STATE32, x86_FLOAT_STATE32_COUNT },
+ { x86_EXCEPTION_STATE32, x86_EXCEPTION_STATE32_COUNT },
+ { x86_DEBUG_STATE32, x86_DEBUG_STATE32_COUNT },
+ // Don’t test x86_AVX_STATE32 because it’s not available on all CPUs and
+ // OS versionns.
+#endif
+#if defined(ARCH_CPU_X86_64)
+ { x86_THREAD_STATE64, x86_THREAD_STATE64_COUNT },
+ { x86_FLOAT_STATE64, x86_FLOAT_STATE64_COUNT },
+ { x86_EXCEPTION_STATE64, x86_EXCEPTION_STATE64_COUNT },
+ { x86_DEBUG_STATE64, x86_DEBUG_STATE64_COUNT },
+ // Don’t test x86_AVX_STATE64 because it’s not available on all CPUs and
+ // OS versions.
+#endif
+ { x86_THREAD_STATE, x86_THREAD_STATE_COUNT },
+ { x86_FLOAT_STATE, x86_FLOAT_STATE_COUNT },
+ { x86_EXCEPTION_STATE, x86_EXCEPTION_STATE_COUNT },
+ { x86_DEBUG_STATE, x86_DEBUG_STATE_COUNT },
+ // Don’t test x86_AVX_STATE because it’s not available on all CPUs and OS
+ // versions.
+#else
+#error Port this test to your CPU architecture.
+#endif
+ };
+
+ for (size_t index = 0; index < arraysize(test_data); ++index) {
+ const TestData& test = test_data[index];
+ SCOPED_TRACE(base::StringPrintf(
+ "index %zu, flavor %d", index, test.flavor));
+
+ TestExcServerVariants test_exc_server_variants(
+ MACH_EXCEPTION_CODES | EXCEPTION_STATE_IDENTITY,
+ test.flavor,
+ test.count);
+ test_exc_server_variants.Run();
+ }
+}
+
+} // namespace
« no previous file with comments | « util/mach/exc_server_variants.cc ('k') | util/mach/mach_extensions.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698