blob: 9ff45b529e917b4e3218fcb0b485b5123b22e667 [file] [log] [blame]
// 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