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

Unified Diff: util/mach/exception_ports_test.cc

Issue 549023005: Add ExceptionPorts and its test (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Rebase 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/exception_ports.cc ('k') | util/util.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: util/mach/exception_ports_test.cc
diff --git a/util/mach/exception_ports_test.cc b/util/mach/exception_ports_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d549651c53d4474bc40adabb7059c4148a59f5dd
--- /dev/null
+++ b/util/mach/exception_ports_test.cc
@@ -0,0 +1,601 @@
+// 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/exception_ports.h"
+
+#include <dispatch/dispatch.h>
+#include <mach/mach.h>
+#include <pthread.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "base/mac/mach_logging.h"
+#include "base/mac/scoped_mach_port.h"
+#include "base/strings/stringprintf.h"
+#include "gtest/gtest.h"
+#include "util/file/fd_io.h"
+#include "util/mach/exc_server_variants.h"
+#include "util/mach/mach_extensions.h"
+#include "util/misc/scoped_forbid_return.h"
+#include "util/test/errors.h"
+#include "util/test/mac/mach_errors.h"
+#include "util/test/mac/mach_multiprocess.h"
+
+namespace {
+
+using namespace crashpad;
+using namespace crashpad::test;
+
+// Calls GetExceptionPorts() on its |exception_ports| argument to look up the
+// EXC_MASK_CRASH handler. If |expect_port| is not MACH_PORT_NULL, it expects to
+// find a handler for this mask whose port matches |expect_port| and whose
+// behavior matches |expect_behavior| exactly. In this case, if
+// |expect_behavior| is a state-carrying behavior, the looked-up thread state
+// flavor is expected to be MACHINE_THREAD_STATE, otherwise, it is expected to
+// be THREAD_STATE_NONE. If |expect_port| is MACH_PORT_NULL, no handler for
+// EXC_MASK_CRASH is expected to be found.
+//
+// A second GetExceptionPorts() lookup is also performed on a wider exception
+// mask, EXC_MASK_ALL | EXC_MASK_CRASH. The EXC_MASK_CRASH handler’s existence
+// and properties from this second lookup are validated in the same way.
+//
+// This function uses gtest EXPECT_* and ASSERT_* macros to perform its
+// validation.
+void TestGetExceptionPorts(const ExceptionPorts& exception_ports,
+ mach_port_t expect_port,
+ exception_behavior_t expect_behavior) {
+ const exception_mask_t kExceptionMask = EXC_MASK_CRASH;
+
+ thread_state_flavor_t expect_flavor = (expect_behavior == EXCEPTION_DEFAULT)
+ ? THREAD_STATE_NONE
+ : MACHINE_THREAD_STATE;
+
+ std::vector<ExceptionPorts::ExceptionHandler> crash_handler;
+ ASSERT_TRUE(
+ exception_ports.GetExceptionPorts(kExceptionMask, &crash_handler));
+
+ if (expect_port != MACH_PORT_NULL) {
+ ASSERT_EQ(1u, crash_handler.size());
+ base::mac::ScopedMachSendRight port_owner(crash_handler[0].port);
+
+ EXPECT_EQ(kExceptionMask, crash_handler[0].mask);
+ EXPECT_EQ(expect_port, crash_handler[0].port);
+ EXPECT_EQ(expect_behavior, crash_handler[0].behavior);
+ EXPECT_EQ(expect_flavor, crash_handler[0].flavor);
+ } else {
+ EXPECT_TRUE(crash_handler.empty());
+ }
+
+ std::vector<ExceptionPorts::ExceptionHandler> handlers;
+ ASSERT_TRUE(exception_ports.GetExceptionPorts(
+ ExcMaskAll() | EXC_MASK_CRASH, &handlers));
+
+ EXPECT_GE(handlers.size(), crash_handler.size());
+ bool found = false;
+ for (const ExceptionPorts::ExceptionHandler& handler : handlers) {
+ if ((handler.mask & kExceptionMask) != 0) {
+ base::mac::ScopedMachSendRight port_owner(handler.port);
+
+ EXPECT_FALSE(found);
+ found = true;
+ EXPECT_EQ(expect_port, handler.port);
+ EXPECT_EQ(expect_behavior, handler.behavior);
+ EXPECT_EQ(expect_flavor, handler.flavor);
+ }
+ }
+
+ if (expect_port != MACH_PORT_NULL) {
+ EXPECT_TRUE(found);
+ } else {
+ EXPECT_FALSE(found);
+ }
+}
+
+class TestExceptionPorts : public UniversalMachExcServer,
+ public MachMultiprocess {
+ public:
+ // Where to call ExceptionPorts::SetExceptionPort() from.
+ enum SetType {
+ // Call it from the child process on itself.
+ kSetInProcess = 0,
+
+ // Call it from the parent process on the child.
+ kSetOutOfProcess,
+ };
+
+ // Which entities to set exception ports for.
+ enum SetOn {
+ kSetOnTaskOnly = 0,
+ kSetOnTaskAndThreads,
+ };
+
+ // Which thread in the child process is expected to crash.
+ enum WhoCrashes {
+ kNobodyCrashes = 0,
+ kMainThreadCrashes,
+ kOtherThreadCrashes,
+ };
+
+ TestExceptionPorts(SetType set_type, SetOn set_on, WhoCrashes who_crashes)
+ : UniversalMachExcServer(),
+ MachMultiprocess(),
+ set_type_(set_type),
+ set_on_(set_on),
+ who_crashes_(who_crashes),
+ handled_(false) {}
+
+ SetType set_type() const { return set_type_; }
+ SetOn set_on() const { return set_on_; }
+ WhoCrashes who_crashes() const { return who_crashes_; }
+
+ // 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;
+
+ // To be able to distinguish between which handler was actually triggered,
+ // the different handlers are registered with different behavior values.
+ exception_behavior_t expect_behavior;
+ if (set_on_ == kSetOnTaskOnly) {
+ expect_behavior = EXCEPTION_DEFAULT;
+ } else if (who_crashes_ == kMainThreadCrashes) {
+ expect_behavior = EXCEPTION_STATE;
+ } else if (who_crashes_ == kOtherThreadCrashes) {
+ expect_behavior = EXCEPTION_STATE_IDENTITY;
+ } else {
+ NOTREACHED();
+ expect_behavior = 0;
+ }
+
+ EXPECT_EQ(expect_behavior, behavior);
+
+ EXPECT_EQ(LocalPort(), exception_port);
+
+ EXPECT_EQ(EXC_CRASH, exception);
+ EXPECT_EQ(2u, code_count);
+
+ // The code_count check above would ideally use ASSERT_EQ so that the next
+ // conditional would not be necessary, but ASSERT_* requires a function
+ // returning type void, and the interface dictates otherwise here.
+ 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;
+
+ // The child crashed with a division by zero, which shows up as SIGFPE.
+ // This was chosen because it’s unlikely to be generated by testing or
+ // assertion failures.
+ EXPECT_EQ(SIGFPE, sig);
+
+ SetExpectedChildTermination(kTerminationSignal, sig);
+ }
+
+ // 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().
+ exception_behavior_t basic_behavior = behavior & ~MACH_EXCEPTION_CODES;
+ bool has_state = basic_behavior == EXCEPTION_STATE ||
+ basic_behavior == EXCEPTION_STATE_IDENTITY;
+ return has_state ? MACH_RCV_PORT_DIED : KERN_SUCCESS;
+ }
+
+ private:
+ class Child {
+ public:
+ explicit Child(TestExceptionPorts* test_exception_ports)
+ : test_exception_ports_(test_exception_ports),
+ thread_(),
+ init_semaphore_(dispatch_semaphore_create(0)),
+ crash_semaphore_(dispatch_semaphore_create(0)) {}
+
+ ~Child() {
+ dispatch_release(crash_semaphore_);
+ dispatch_release(init_semaphore_);
+ }
+
+ void Run() {
+ ExceptionPorts self_task_ports(ExceptionPorts::kTargetTypeTask,
+ MACH_PORT_NULL);
+ ExceptionPorts self_thread_ports(ExceptionPorts::kTargetTypeThread,
+ MACH_PORT_NULL);
+
+ mach_port_t remote_port = test_exception_ports_->RemotePort();
+
+ // Set the task’s and this thread’s exception ports, if appropriate.
+ if (test_exception_ports_->set_type() == kSetInProcess) {
+ ASSERT_TRUE(self_task_ports.SetExceptionPort(
+ EXC_MASK_CRASH, remote_port, EXCEPTION_DEFAULT, THREAD_STATE_NONE));
+
+ if (test_exception_ports_->set_on() == kSetOnTaskAndThreads) {
+ ASSERT_TRUE(self_thread_ports.SetExceptionPort(EXC_MASK_CRASH,
+ remote_port,
+ EXCEPTION_STATE,
+ MACHINE_THREAD_STATE));
+ }
+ }
+
+ int rv_int = pthread_create(&thread_, NULL, ThreadMainThunk, this);
+ ASSERT_EQ(0, rv_int);
+
+ // Wait for the new thread to be ready.
+ long rv_long =
+ dispatch_semaphore_wait(init_semaphore_, DISPATCH_TIME_FOREVER);
+ ASSERT_EQ(0, rv_long);
+
+ // Tell the parent process that everything is set up.
+ char c = '\0';
+ ssize_t rv_ssize = WriteFD(test_exception_ports_->WritePipeFD(), &c, 1);
+ ASSERT_EQ(1, rv_ssize) << ErrnoMessage("write");
+
+ // Wait for the parent process to say that its end is set up.
+ rv_ssize = ReadFD(test_exception_ports_->ReadPipeFD(), &c, 1);
+ ASSERT_EQ(1, rv_ssize) << ErrnoMessage("read");
+ EXPECT_EQ('\0', c);
+
+ // Regardless of where ExceptionPorts::SetExceptionPort() ran,
+ // ExceptionPorts::GetExceptionPorts() can always be tested in-process.
+ {
+ SCOPED_TRACE("task");
+ TestGetExceptionPorts(self_task_ports, remote_port, EXCEPTION_DEFAULT);
+ }
+
+ {
+ SCOPED_TRACE("main_thread");
+ mach_port_t thread_handler =
+ (test_exception_ports_->set_on() == kSetOnTaskAndThreads)
+ ? remote_port
+ : MACH_PORT_NULL;
+ TestGetExceptionPorts(
+ self_thread_ports, thread_handler, EXCEPTION_STATE);
+ }
+
+ // Let the other thread know it’s safe to proceed.
+ dispatch_semaphore_signal(crash_semaphore_);
+
+ // If this thread is the one that crashes, do it.
+ if (test_exception_ports_->who_crashes() == kMainThreadCrashes) {
+ Crash();
+ }
+
+ // Reap the other thread.
+ rv_int = pthread_join(thread_, NULL);
+ ASSERT_EQ(0, rv_int);
+ }
+
+ private:
+ // Calls ThreadMain().
+ static void* ThreadMainThunk(void* argument) {
+ Child* self = reinterpret_cast<Child*>(argument);
+ return self->ThreadMain();
+ }
+
+ // Runs the “other” thread.
+ void* ThreadMain() {
+ ExceptionPorts self_thread_ports(ExceptionPorts::kTargetTypeThread,
+ MACH_PORT_NULL);
+ mach_port_t remote_port = test_exception_ports_->RemotePort();
+
+ // Set this thread’s exception handler, if appropriate.
+ if (test_exception_ports_->set_type() == kSetInProcess &&
+ test_exception_ports_->set_on() == kSetOnTaskAndThreads) {
+ CHECK(self_thread_ports.SetExceptionPort(EXC_MASK_CRASH,
+ remote_port,
+ EXCEPTION_STATE_IDENTITY,
+ MACHINE_THREAD_STATE));
+ }
+
+ // Let the main thread know that this thread is ready.
+ dispatch_semaphore_signal(init_semaphore_);
+
+ // Wait for the main thread to signal that it’s safe to proceed.
+ long rv =
+ dispatch_semaphore_wait(crash_semaphore_, DISPATCH_TIME_FOREVER);
+ CHECK_EQ(0, rv) << "dispatch_semaphore_wait";
+
+ // Regardless of where ExceptionPorts::SetExceptionPort() ran,
+ // ExceptionPorts::GetExceptionPorts() can always be tested in-process.
+ {
+ SCOPED_TRACE("other_thread");
+ mach_port_t thread_handler =
+ (test_exception_ports_->set_on() == kSetOnTaskAndThreads)
+ ? remote_port
+ : MACH_PORT_NULL;
+ TestGetExceptionPorts(
+ self_thread_ports, thread_handler, EXCEPTION_STATE_IDENTITY);
+ }
+
+ // If this thread is the one that crashes, do it.
+ if (test_exception_ports_->who_crashes() == kOtherThreadCrashes) {
+ Crash();
+ }
+
+ return NULL;
+ }
+
+ // Crashes by performing a division by zero. The assignment is present to
+ // avoid optimizing zero_ out entirely by making it appear that its value
+ // might change.
+ static void Crash() { zero_ = 1 / zero_; }
+
+ // The parent object.
+ TestExceptionPorts* test_exception_ports_; // weak
+
+ // The “other” thread.
+ pthread_t thread_;
+
+ // The main thread waits on this for the other thread to start up and
+ // perform its own initialization.
+ dispatch_semaphore_t init_semaphore_;
+
+ // The child thread waits on this for the parent thread to indicate that the
+ // child can test its exception ports and possibly crash, as appropriate.
+ dispatch_semaphore_t crash_semaphore_;
+
+ // Always zero. Crash() divides by this in order to trigger a crash. This is
+ // structured as a static volatile int to ward off aggressive compiler
+ // optimizations.
+ static volatile int zero_;
+
+ DISALLOW_COPY_AND_ASSIGN(Child);
+ };
+
+ // MachMultiprocess:
+
+ virtual void MachMultiprocessParent() override {
+ // Wait for the child process to be ready. It needs to have all of its
+ // threads set up before proceeding if in kSetOutOfProcess mode.
+ char c;
+ ssize_t rv = ReadFD(ReadPipeFD(), &c, 1);
+ ASSERT_EQ(1, rv) << ErrnoMessage("read");
+ EXPECT_EQ('\0', c);
+
+ mach_port_t local_port = LocalPort();
+
+ // Get an ExceptionPorts object for the task and each of its threads.
+ ExceptionPorts task_ports(ExceptionPorts::kTargetTypeTask, ChildTask());
+ EXPECT_EQ("task", task_ports.TargetTypeName());
+
+ // Hopefully the threads returned by task_threads() are in order, with the
+ // main thread first and the other thread second. This is currently always
+ // the case, although nothing guarantees that it will remain so.
+ thread_act_array_t threads;
+ mach_msg_type_number_t thread_count = 0;
+ kern_return_t kr = task_threads(ChildTask(), &threads, &thread_count);
+ ASSERT_EQ(KERN_SUCCESS, kr) << MachErrorMessage(kr, "task_threads");
+
+ ScopedForbidReturn threads_need_owners;
+ ASSERT_EQ(2u, thread_count);
+ base::mac::ScopedMachSendRight main_thread(threads[0]);
+ base::mac::ScopedMachSendRight other_thread(threads[1]);
+ threads_need_owners.Disarm();
+
+ ExceptionPorts main_thread_ports(ExceptionPorts::kTargetTypeThread,
+ main_thread);
+ ExceptionPorts other_thread_ports(ExceptionPorts::kTargetTypeThread,
+ other_thread);
+ EXPECT_EQ("thread", main_thread_ports.TargetTypeName());
+ EXPECT_EQ("thread", other_thread_ports.TargetTypeName());
+
+ if (set_type_ == kSetOutOfProcess) {
+ // Test ExceptionPorts::SetExceptionPorts() being called from
+ // out-of-process.
+ //
+ // local_port is only a receive right, but a send right is needed for
+ // ExceptionPorts::SetExceptionPort(). Make a send right, which can be
+ // deallocated once the calls to ExceptionPorts::SetExceptionPort() are
+ // done.
+ kr = mach_port_insert_right(
+ mach_task_self(), local_port, local_port, MACH_MSG_TYPE_MAKE_SEND);
+ ASSERT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "mach_port_insert_right");
+ base::mac::ScopedMachSendRight send_owner(local_port);
+
+ ASSERT_TRUE(task_ports.SetExceptionPort(
+ EXC_MASK_CRASH, local_port, EXCEPTION_DEFAULT, THREAD_STATE_NONE));
+
+ if (set_on_ == kSetOnTaskAndThreads) {
+ ASSERT_TRUE(main_thread_ports.SetExceptionPort(
+ EXC_MASK_CRASH, local_port, EXCEPTION_STATE, MACHINE_THREAD_STATE));
+
+ ASSERT_TRUE(
+ other_thread_ports.SetExceptionPort(EXC_MASK_CRASH,
+ local_port,
+ EXCEPTION_STATE_IDENTITY,
+ MACHINE_THREAD_STATE));
+ }
+ }
+
+ // Regardless of where ExceptionPorts::SetExceptionPort() ran,
+ // ExceptionPorts::GetExceptionPorts() can always be tested out-of-process.
+ {
+ SCOPED_TRACE("task");
+ TestGetExceptionPorts(task_ports, local_port, EXCEPTION_DEFAULT);
+ }
+
+ mach_port_t thread_handler =
+ (set_on_ == kSetOnTaskAndThreads) ? local_port : MACH_PORT_NULL;
+
+ {
+ SCOPED_TRACE("main_thread");
+ TestGetExceptionPorts(main_thread_ports, thread_handler, EXCEPTION_STATE);
+ }
+
+ {
+ SCOPED_TRACE("other_thread");
+ TestGetExceptionPorts(
+ other_thread_ports, thread_handler, EXCEPTION_STATE_IDENTITY);
+ }
+
+ // Let the child process know that everything in the parent process is set
+ // up.
+ c = '\0';
+ rv = WriteFD(WritePipeFD(), &c, 1);
+ ASSERT_EQ(1, rv) << ErrnoMessage("write");
+
+ if (who_crashes_ != kNobodyCrashes) {
+ kern_return_t kr = MachMessageServer::Run(this,
+ local_port,
+ MACH_MSG_OPTION_NONE,
+ MachMessageServer::kOneShot,
+ MachMessageServer::kBlocking,
+ 0);
+ EXPECT_EQ(KERN_SUCCESS, kr)
+ << MachErrorMessage(kr, "MachMessageServer::Run");
+
+ EXPECT_TRUE(handled_);
+ }
+
+ // Wait for the child process to exit or terminate, as indicated by it
+ // closing its pipe. This keeps LocalPort() alive in the child as
+ // RemotePort(), for the child’s use in its TestGetExceptionPorts().
+ rv = ReadFD(ReadPipeFD(), &c, 1);
+ ASSERT_EQ(0, rv);
+ }
+
+ virtual void MachMultiprocessChild() override {
+ Child child(this);
+ child.Run();
+ }
+
+ SetType set_type_;
+ SetOn set_on_;
+ WhoCrashes who_crashes_;
+
+ // true if an exception message was handled.
+ bool handled_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestExceptionPorts);
+};
+
+volatile int TestExceptionPorts::Child::zero_ = 0;
+
+TEST(ExceptionPorts, TaskAndThreadExceptionPorts) {
+ struct Testcase {
+ TestExceptionPorts::SetType set_type;
+ TestExceptionPorts::SetOn set_on;
+ TestExceptionPorts::WhoCrashes who_crashes;
+ };
+ const Testcase kTestcases[] = {
+ {TestExceptionPorts::kSetInProcess,
+ TestExceptionPorts::kSetOnTaskOnly,
+ TestExceptionPorts::kNobodyCrashes},
+ {TestExceptionPorts::kSetInProcess,
+ TestExceptionPorts::kSetOnTaskOnly,
+ TestExceptionPorts::kMainThreadCrashes},
+ {TestExceptionPorts::kSetInProcess,
+ TestExceptionPorts::kSetOnTaskOnly,
+ TestExceptionPorts::kOtherThreadCrashes},
+ {TestExceptionPorts::kSetInProcess,
+ TestExceptionPorts::kSetOnTaskAndThreads,
+ TestExceptionPorts::kNobodyCrashes},
+ {TestExceptionPorts::kSetInProcess,
+ TestExceptionPorts::kSetOnTaskAndThreads,
+ TestExceptionPorts::kMainThreadCrashes},
+ {TestExceptionPorts::kSetInProcess,
+ TestExceptionPorts::kSetOnTaskAndThreads,
+ TestExceptionPorts::kOtherThreadCrashes},
+ {TestExceptionPorts::kSetOutOfProcess,
+ TestExceptionPorts::kSetOnTaskOnly,
+ TestExceptionPorts::kNobodyCrashes},
+ {TestExceptionPorts::kSetOutOfProcess,
+ TestExceptionPorts::kSetOnTaskOnly,
+ TestExceptionPorts::kMainThreadCrashes},
+ {TestExceptionPorts::kSetOutOfProcess,
+ TestExceptionPorts::kSetOnTaskOnly,
+ TestExceptionPorts::kOtherThreadCrashes},
+ {TestExceptionPorts::kSetOutOfProcess,
+ TestExceptionPorts::kSetOnTaskAndThreads,
+ TestExceptionPorts::kNobodyCrashes},
+ {TestExceptionPorts::kSetOutOfProcess,
+ TestExceptionPorts::kSetOnTaskAndThreads,
+ TestExceptionPorts::kMainThreadCrashes},
+ {TestExceptionPorts::kSetOutOfProcess,
+ TestExceptionPorts::kSetOnTaskAndThreads,
+ TestExceptionPorts::kOtherThreadCrashes},
+ };
+
+ for (size_t index = 0; index < arraysize(kTestcases); ++index) {
+ const Testcase& testcase = kTestcases[index];
+ SCOPED_TRACE(
+ base::StringPrintf("index %zu, set_type %d, set_on %d, who_crashes %d",
+ index,
+ testcase.set_type,
+ testcase.set_on,
+ testcase.who_crashes));
+
+ TestExceptionPorts test_exception_ports(
+ testcase.set_type, testcase.set_on, testcase.who_crashes);
+ test_exception_ports.Run();
+ }
+}
+
+TEST(ExceptionPorts, HostExceptionPorts) {
+ // ExceptionPorts isn’t expected to work as non-root. Just do a quick test to
+ // make sure that TargetTypeName() returns the right string, and that the
+ // underlying host_get_exception_ports() function appears to be called by
+ // looking for a KERN_INVALID_ARGUMENT return value. Or, on the off chance
+ // that the test is being run as root, just look for KERN_SUCCESS.
+ // host_set_exception_ports() is not tested, because if the test were running
+ // as root and the call succeeded, it would have global effects.
+
+ base::mac::ScopedMachSendRight host(mach_host_self());
+ ExceptionPorts explicit_host_ports(ExceptionPorts::kTargetTypeHost, host);
+ EXPECT_EQ("host", explicit_host_ports.TargetTypeName());
+
+ std::vector<ExceptionPorts::ExceptionHandler> handlers;
+ bool rv = explicit_host_ports.GetExceptionPorts(
+ ExcMaskAll() | EXC_MASK_CRASH, &handlers);
+ if (geteuid() == 0) {
+ EXPECT_TRUE(rv);
+ } else {
+ EXPECT_FALSE(rv);
+ }
+
+ ExceptionPorts implicit_host_ports(ExceptionPorts::kTargetTypeHost,
+ MACH_PORT_NULL);
+ EXPECT_EQ("host", implicit_host_ports.TargetTypeName());
+
+ rv = implicit_host_ports.GetExceptionPorts(
+ ExcMaskAll() | EXC_MASK_CRASH, &handlers);
+ if (geteuid() == 0) {
+ EXPECT_TRUE(rv);
+ } else {
+ EXPECT_FALSE(rv);
+ }
+}
+
+} // namespace
« no previous file with comments | « util/mach/exception_ports.cc ('k') | util/util.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698