| // 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.flags & ZX_INFO_PROCESS_FLAG_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) { |
| if (getenv("NO_CHILD_PROCESS")) { |
| ZXTEST_SKIP( |
| "Test requires creating a child process and this environment does not support it. " |
| "Skipping"); |
| } |
| 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) { |
| if (getenv("NO_CHILD_PROCESS")) { |
| ZXTEST_SKIP( |
| "Test requires creating a child process and this environment does not support it. " |
| "Skipping"); |
| } |
| 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 |