|  | // Copyright 2019 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include <assert.h> | 
|  | #include <lib/zx/channel.h> | 
|  | #include <lib/zx/event.h> | 
|  | #include <lib/zx/handle.h> | 
|  | #include <lib/zx/job.h> | 
|  | #include <lib/zx/process.h> | 
|  | #include <lib/zx/thread.h> | 
|  | #include <lib/zx/vmar.h> | 
|  | #include <zircon/syscalls/policy.h> | 
|  |  | 
|  | #include <iterator> | 
|  |  | 
|  | #include <mini-process/mini-process.h> | 
|  | #include <zxtest/zxtest.h> | 
|  |  | 
|  | // This file contains tests that verify the behavior of the default exception handler, that is, the | 
|  | // in-kernel exception handler that's used when no usermode exception handler is installed. | 
|  | // | 
|  | // To ensure these tests verify the unhandle exception path, they should be run on a system that has | 
|  | // no usermode exception handlers.  Standalone core-tests provides such an environment.  See also | 
|  | // |VerifyNoExceptionHandler|. | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Test helper that runs a mini-process in |job| that will execute the command specified by | 
|  | // |minip_cmd|.  Be sure to call with ASSERT_NO_FAILURES. | 
|  | // | 
|  | // See mini-process.h for valid |minip_cmd| values. | 
|  | // | 
|  | // Upon success, sets |exit_code| to the process's exit code.  See the ZX_TASK_RETCODE_* constants | 
|  | // in syscalls/object.h. | 
|  | void RunMiniProcessTestHelper(const zx::job& job, uint32_t minip_cmd, int64_t* exit_code) { | 
|  | zx::vmar vmar; | 
|  | zx::process proc; | 
|  | static constexpr char kName[] = "unhandled-exception-test"; | 
|  | ASSERT_OK(zx::process::create(job, kName, sizeof(kName) - 1, 0u, &proc, &vmar)); | 
|  |  | 
|  | zx::thread thread; | 
|  | ASSERT_OK(zx::thread::create(proc, kName, sizeof(kName) - 1, 0, &thread)); | 
|  |  | 
|  | zx::event event; | 
|  | ASSERT_OK(zx::event::create(0u, &event)); | 
|  |  | 
|  | zx::channel control; | 
|  | ASSERT_OK(start_mini_process_etc(proc.get(), thread.release(), vmar.get(), event.release(), true, | 
|  | control.reset_and_get_address())); | 
|  | zx::handle handle; | 
|  | ASSERT_STATUS(mini_process_cmd(control.get(), minip_cmd, handle.reset_and_get_address()), | 
|  | ZX_ERR_PEER_CLOSED); | 
|  |  | 
|  | ASSERT_OK(proc.wait_one(ZX_TASK_TERMINATED, zx::time::infinite(), nullptr)); | 
|  |  | 
|  | zx_info_process_t proc_info; | 
|  | ASSERT_OK(proc.get_info(ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), nullptr, nullptr)); | 
|  | ASSERT_TRUE(proc_info.exited); | 
|  |  | 
|  | *exit_code = proc_info.return_code; | 
|  | } | 
|  |  | 
|  | // Verfies that the default job has no exception handler. | 
|  | // | 
|  | // TODO(maniscalco): We're only checking the default job.  It's entirely possible one of its | 
|  | // ancestors has an exception channel.  Unfortunately, there is no good way to verify that none in | 
|  | // the tree have an exception handler. | 
|  | void VerifyNoExceptionHandler() { | 
|  | zx::unowned_job job(zx::job::default_job()); | 
|  | ASSERT_TRUE(job->is_valid()); | 
|  |  | 
|  | // If we can't install an exception channel, then there must be one already installed. | 
|  | zx::channel exception_channel; | 
|  | ASSERT_OK(job->create_exception_channel(0, &exception_channel)); | 
|  | } | 
|  |  | 
|  | // Verify that an unhandled exception terminates the process. | 
|  | TEST(DefaultExceptionHandlerTest, UnhandledHardwareException) { | 
|  | ASSERT_NO_FAILURES(VerifyNoExceptionHandler()); | 
|  |  | 
|  | zx::job job; | 
|  | ASSERT_OK(zx::job::create(*zx::job::default_job(), 0u, &job)); | 
|  |  | 
|  | int64_t exit_code = 0; | 
|  | ASSERT_NO_FAILURES(RunMiniProcessTestHelper(job, MINIP_CMD_BUILTIN_TRAP, &exit_code)); | 
|  | ASSERT_EQ(exit_code, ZX_TASK_RETCODE_EXCEPTION_KILL); | 
|  | } | 
|  |  | 
|  | // Verify that an unhandled policy exception terminates the process.  Policy exceptions are special | 
|  | // in that they are generated by the kernel itself rather than the hardware. | 
|  | TEST(DefaultExceptionHandlerTest, UnhandledPolicyException) { | 
|  | ASSERT_NO_FAILURES(VerifyNoExceptionHandler()); | 
|  |  | 
|  | zx::job job; | 
|  | ASSERT_OK(zx::job::create(*zx::job::default_job(), 0u, &job)); | 
|  |  | 
|  | // Create a job with policy that generates an excpetion when an object is created. | 
|  | zx_policy_basic_v2_t policy[] = { | 
|  | {ZX_POL_NEW_ANY, ZX_POL_ACTION_ALLOW_EXCEPTION, ZX_POL_OVERRIDE_DENY}}; | 
|  | ASSERT_OK(job.set_policy(ZX_JOB_POL_ABSOLUTE, ZX_JOB_POL_BASIC_V2, policy, | 
|  | static_cast<uint32_t>(std::size(policy)))); | 
|  |  | 
|  | // Tell the process to create an event.  See that it's killed because the exception is unhandled. | 
|  | int64_t exit_code = 0; | 
|  | ASSERT_NO_FAILURES(RunMiniProcessTestHelper(job, MINIP_CMD_CREATE_EVENT, &exit_code)); | 
|  | ASSERT_EQ(exit_code, ZX_TASK_RETCODE_EXCEPTION_KILL); | 
|  | } | 
|  |  | 
|  | }  // namespace |