blob: a7a7fdc93a48a8b5bbf82caa6a22af581c9528a2 [file] [log] [blame]
// Copyright 2016 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 <lib/fit/function.h>
#include <lib/zx/clock.h>
#include <lib/zx/event.h>
#include <lib/zx/job.h>
#include <lib/zx/msi.h>
#include <lib/zx/process.h>
#include <lib/zx/thread.h>
#include <lib/zx/time.h>
#include <lib/zx/vmar.h>
#include <threads.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/debug.h>
#include <zircon/syscalls/exception.h>
#include <zircon/syscalls/object.h>
#include <zircon/types.h>
#include <algorithm>
#include <atomic>
#include <cassert>
#include <climits>
#include <vector>
#include <mini-process/mini-process.h>
#include <zxtest/zxtest.h>
namespace {
#ifdef __aarch64__
constexpr auto kThreadRegister = &zx_thread_state_general_regs_t::tpidr;
#elif defined(__x86_64__)
constexpr auto kThreadRegister = &zx_thread_state_general_regs_t::fs_base;
#endif
const zx_time_t kTimeoutNs = ZX_MSEC(250);
TEST(ProcessTest, LongNameSucceeds) {
// Creating a process with a super long name should succeed.
static const char long_name[] =
"0123456789012345678901234567890123456789"
"0123456789012345678901234567890123456789";
ASSERT_GT(strlen(long_name), (size_t)ZX_MAX_NAME_LEN - 1, "too short to truncate");
zx_handle_t proc;
zx_handle_t vmar;
ASSERT_OK(zx_process_create(zx_job_default(), long_name, sizeof(long_name), 0, &proc, &vmar));
static char proc_name[ZX_MAX_NAME_LEN];
ASSERT_OK(zx_object_get_property(proc, ZX_PROP_NAME, proc_name, ZX_MAX_NAME_LEN));
ASSERT_EQ(strncmp(proc_name, long_name, ZX_MAX_NAME_LEN - 1), 0);
ASSERT_OK(zx_handle_close(vmar));
ASSERT_OK(zx_handle_close(proc));
}
TEST(ProcessTest, EmptyNameSucceeds) {
// Creating a process with "" name, 0 name_len should succeed.
zx_handle_t proc;
zx_handle_t vmar;
ASSERT_OK(zx_process_create(zx_job_default(), "", 0, 0, &proc, &vmar));
static char proc_name[ZX_MAX_NAME_LEN];
ASSERT_OK(zx_object_get_property(proc, ZX_PROP_NAME, proc_name, ZX_MAX_NAME_LEN));
ASSERT_EQ(strcmp(proc_name, ""), 0);
ASSERT_OK(zx_handle_close(vmar));
ASSERT_OK(zx_handle_close(proc));
}
TEST(ProcessTest, GetRuntimeNoPermission) {
zx::process proc;
zx::vmar vmar;
ASSERT_OK(zx::process::create(*zx::job::default_job(), "", 0, 0, &proc, &vmar));
zx_info_handle_basic basic;
ASSERT_OK(proc.get_info(ZX_INFO_HANDLE_BASIC, &basic, sizeof(basic), nullptr, nullptr));
zx::process proc_dup;
ASSERT_OK(proc.duplicate(basic.rights & ~ZX_RIGHT_INSPECT, &proc_dup));
zx_info_task_runtime_t info;
ASSERT_OK(proc.get_info(ZX_INFO_TASK_RUNTIME, &info, sizeof(info), nullptr, nullptr));
ASSERT_EQ(proc_dup.get_info(ZX_INFO_TASK_RUNTIME, &info, sizeof(info), nullptr, nullptr),
ZX_ERR_ACCESS_DENIED);
}
TEST(ProcessTest, MiniProcessSanity) {
zx_handle_t proc;
zx_handle_t thread;
zx_handle_t vmar;
ASSERT_OK(zx_process_create(zx_job_default(), "mini-p", 3u, 0, &proc, &vmar));
ASSERT_OK(zx_thread_create(proc, "mini-p", 2u, 0u, &thread));
zx_handle_t event;
ASSERT_OK(zx_event_create(0u, &event));
zx_handle_t cmd_channel;
EXPECT_OK(start_mini_process_etc(proc, thread, vmar, event, true, &cmd_channel));
EXPECT_OK(mini_process_cmd(cmd_channel, MINIP_CMD_ECHO_MSG, nullptr));
zx_handle_t oev;
EXPECT_OK(mini_process_cmd(cmd_channel, MINIP_CMD_CREATE_EVENT, &oev));
EXPECT_EQ(mini_process_cmd(cmd_channel, MINIP_CMD_EXIT_NORMAL, nullptr), ZX_ERR_PEER_CLOSED);
zx_handle_close(thread);
zx_handle_close(proc);
zx_handle_close(vmar);
}
TEST(ProcessTest, ProcessStartNoHandle) {
zx_handle_t proc;
zx_handle_t thread;
zx_handle_t vmar;
constexpr const char kTestName[] = "test-no-handles";
ASSERT_OK(zx_process_create(zx_job_default(), kTestName, sizeof(kTestName) - 1, 0, &proc, &vmar));
ASSERT_OK(zx_thread_create(proc, kTestName, sizeof(kTestName) - 1, 0u, &thread));
// The process will get no handles, but it can still make syscalls.
// The vDSO's e_entry points to zx_process_exit. So the process will
// enter at `zx_process_exit(ZX_HANDLE_INVALID);`.
uintptr_t entry;
EXPECT_OK(mini_process_load_vdso(proc, vmar, nullptr, &entry));
// The vDSO ABI needs a stack, though zx_process_exit actually might not.
uintptr_t stack_base, sp;
EXPECT_OK(mini_process_load_stack(vmar, false, &stack_base, &sp));
zx_handle_close(vmar);
EXPECT_OK(zx_process_start(proc, thread, entry, sp, ZX_HANDLE_INVALID, 0));
zx_handle_close(thread);
zx_signals_t signals;
EXPECT_OK(zx_object_wait_one(proc, ZX_TASK_TERMINATED, zx_deadline_after(ZX_SEC(1)), &signals));
EXPECT_EQ(signals, ZX_TASK_TERMINATED);
{
zx_info_process_t info{};
EXPECT_OK(zx_object_get_info(proc, ZX_INFO_PROCESS, &info, sizeof(info), nullptr, nullptr));
EXPECT_EQ(info.return_code, int64_t{ZX_HANDLE_INVALID});
}
zx_handle_close(proc);
}
#if defined(__x86_64__)
#include <cpuid.h>
// This is based on code from kernel/ which isn't usable by code in system/.
enum { X86_CPUID_ADDR_WIDTH = 0x80000008 };
static uint32_t x86_linear_address_width() {
uint32_t eax, ebx, ecx, edx;
__cpuid(X86_CPUID_ADDR_WIDTH, eax, ebx, ecx, edx);
return (eax >> 8) & 0xff;
}
#endif
TEST(ProcessTest, ProcessStartNonUserspaceEntry) {
auto test_process_start = [&](uintptr_t entry, zx_status_t expected) {
zx_handle_t proc;
zx_handle_t thread;
zx_handle_t vmar;
constexpr const char kTestName[] = "test-noncanonical-entry";
ASSERT_OK(
zx_process_create(zx_job_default(), kTestName, sizeof(kTestName) - 1, 0, &proc, &vmar));
zx_handle_close(vmar);
ASSERT_OK(zx_thread_create(proc, kTestName, sizeof(kTestName) - 1, 0u, &thread));
char stack[1024] __ALIGNED(16); // a small stack for the process.
uintptr_t sp = reinterpret_cast<uintptr_t>(&stack[1024]);
EXPECT_EQ(expected, zx_process_start(proc, thread, entry, sp, ZX_HANDLE_INVALID, 0));
zx_handle_close(thread);
zx_handle_close(proc);
};
// This represents an inaccessible address on both aarch64 (because bit 55 == 0
// indicates an accessible user address) and x86_64 (because the upper 16 bits
// are not all zero).
uintptr_t non_user_pc = 0x1UL << 55;
uintptr_t kernel_pc = 0xffffff8000000000UL;
test_process_start(non_user_pc, ZX_ERR_INVALID_ARGS);
test_process_start(kernel_pc, ZX_ERR_INVALID_ARGS);
#if defined(__x86_64__)
uintptr_t non_canonical_pc = ((uintptr_t)1) << (x86_linear_address_width() - 1);
test_process_start(non_canonical_pc, ZX_ERR_INVALID_ARGS);
#endif // defined(__x86_64__)
}
TEST(ProcessTest, ProcessStartFail) {
zx_handle_t event1, event2;
zx_handle_t process;
zx_handle_t thread;
ASSERT_OK(zx_event_create(0u, &event1));
ASSERT_OK(zx_event_create(0u, &event2));
ASSERT_OK(start_mini_process(zx_job_default(), event1, &process, &thread));
zx_handle_t other_thread;
ASSERT_OK(zx_thread_create(process, "test", 4u, 0, &other_thread));
// Test that calling process_start() again for an existing process fails in a
// reasonable way. Also test that the transferred object is closed.
EXPECT_EQ(zx_process_start(process, other_thread, 0, 0, event2, 0), ZX_ERR_BAD_STATE);
zx_handle_close(process);
zx_handle_close(thread);
zx_handle_close(other_thread);
}
TEST(ProcessTest, ProcessNotKilledViaThreadClose) {
zx_handle_t event;
ASSERT_OK(zx_event_create(0u, &event));
zx_handle_t process;
zx_handle_t thread;
ASSERT_OK(start_mini_process(zx_job_default(), event, &process, &thread));
EXPECT_OK(zx_handle_close(thread));
// The timeout below does not have to be large because the processing happens
// synchronously if indeed |thread| is the last handle.
zx_signals_t signals = 0;
EXPECT_EQ(
zx_object_wait_one(process, ZX_TASK_TERMINATED, zx_deadline_after(ZX_MSEC(1)), &signals),
ZX_ERR_TIMED_OUT);
EXPECT_NE(signals, ZX_TASK_TERMINATED);
EXPECT_OK(zx_handle_close(process));
}
TEST(ProcessTest, ProcessNotKilledViaProcessClose) {
zx_handle_t event;
ASSERT_OK(zx_event_create(0u, &event));
zx_handle_t process;
zx_handle_t thread;
ASSERT_OK(start_mini_process(zx_job_default(), event, &process, &thread));
EXPECT_OK(zx_handle_close(process));
// The timeout below does not have to be large because the processing happens
// synchronously if indeed |process| is the last handle.
zx_signals_t signals;
EXPECT_EQ(zx_object_wait_one(thread, ZX_TASK_TERMINATED, zx_deadline_after(ZX_MSEC(1)), &signals),
ZX_ERR_TIMED_OUT);
EXPECT_OK(zx_handle_close(thread));
}
zx_status_t dup_send_handle(zx_handle_t channel, zx_handle_t handle) {
zx_handle_t dup;
zx_status_t st = zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, &dup);
if (st < 0)
return st;
return zx_channel_write(channel, 0u, nullptr, 0u, &dup, 1u);
}
TEST(ProcessTest, KillChannelHandleCycle) {
zx_handle_t chan[2] = {ZX_HANDLE_INVALID, ZX_HANDLE_INVALID};
ASSERT_OK(zx_channel_create(0u, &chan[0], &chan[1]));
zx_handle_t proc1, proc2;
zx_handle_t vmar1, vmar2;
zx_handle_t job_child;
ASSERT_OK(zx_job_create(zx_job_default(), 0u, &job_child));
ASSERT_OK(zx_process_create(job_child, "ttp1", 4u, 0u, &proc1, &vmar1));
ASSERT_OK(zx_process_create(job_child, "ttp2", 4u, 0u, &proc2, &vmar2));
zx_handle_t thread1, thread2;
ASSERT_OK(zx_thread_create(proc1, "th1", 3u, 0u, &thread1));
ASSERT_OK(zx_thread_create(proc2, "th2", 3u, 0u, &thread2));
// Now we stuff duplicated process and thread handles into each side of the channel.
EXPECT_OK(dup_send_handle(chan[0], proc2));
EXPECT_OK(dup_send_handle(chan[0], thread2));
EXPECT_OK(dup_send_handle(chan[1], proc1));
EXPECT_OK(dup_send_handle(chan[1], thread1));
// The process start with each one side of the channel. We don't have access to the
// channel anymore.
zx_handle_t minip_chn[2];
EXPECT_OK(start_mini_process_etc(proc1, thread1, vmar1, chan[0], true, &minip_chn[0]));
EXPECT_OK(start_mini_process_etc(proc2, thread2, vmar2, chan[1], true, &minip_chn[1]));
EXPECT_OK(zx_handle_close(vmar2));
EXPECT_OK(zx_handle_close(vmar1));
EXPECT_OK(zx_handle_close(proc1));
EXPECT_OK(zx_handle_close(proc2));
// Make (relatively) certain the processes are alive.
zx_signals_t signals;
EXPECT_EQ(
zx_object_wait_one(thread1, ZX_TASK_TERMINATED, zx_deadline_after(kTimeoutNs), &signals),
ZX_ERR_TIMED_OUT);
EXPECT_EQ(
zx_object_wait_one(thread2, ZX_TASK_TERMINATED, zx_deadline_after(kTimeoutNs), &signals),
ZX_ERR_TIMED_OUT);
// At this point the two processes have each other thread/process handles.
EXPECT_OK(zx_handle_close(thread1));
EXPECT_EQ(
zx_object_wait_one(thread2, ZX_TASK_TERMINATED, zx_deadline_after(kTimeoutNs), &signals),
ZX_ERR_TIMED_OUT);
// The only way out of this situation is to use the job handle.
EXPECT_OK(zx_task_kill(job_child));
EXPECT_OK(zx_object_wait_one(thread2, ZX_TASK_TERMINATED, ZX_TIME_INFINITE, &signals));
signals &= ZX_TASK_TERMINATED;
EXPECT_EQ(signals, ZX_TASK_TERMINATED);
EXPECT_OK(zx_handle_close(thread2));
EXPECT_OK(zx_handle_close(job_child));
}
// Tests that |zx_info_process_t| fields reflect the current state of a process.
TEST(ProcessTest, InfoReflectsProcessState) {
// Create a process with one thread.
zx_handle_t event;
ASSERT_OK(zx_event_create(0u, &event));
zx_handle_t job_child;
ASSERT_OK(zx_job_create(zx_job_default(), 0u, &job_child));
zx_handle_t proc;
zx_handle_t vmar;
ASSERT_OK(zx_process_create(job_child, "ttp", 4u, 0u, &proc, &vmar));
EXPECT_OK(zx_handle_close(job_child));
zx_handle_t thread;
ASSERT_OK(zx_thread_create(proc, "th", 3u, 0u, &thread));
{
zx_info_process_t info;
ASSERT_OK(zx_object_get_info(proc, ZX_INFO_PROCESS, &info, sizeof(info), NULL, NULL));
EXPECT_FALSE(info.flags & ZX_INFO_PROCESS_FLAG_STARTED, "process should not appear as started");
EXPECT_FALSE(info.flags & ZX_INFO_PROCESS_FLAG_EXITED, "process should not appear as exited");
EXPECT_EQ(info.return_code, 0, "return code is zero");
}
const zx_time_t before_start = zx_clock_get_monotonic();
zx_handle_t minip_chn;
// Start the process and make (relatively) certain it's alive.
ASSERT_OK(start_mini_process_etc(proc, thread, vmar, event, true, &minip_chn));
const zx_time_t after_start = zx_clock_get_monotonic();
zx_signals_t signals;
ASSERT_EQ(zx_object_wait_one(proc, ZX_TASK_TERMINATED, zx_deadline_after(kTimeoutNs), &signals),
ZX_ERR_TIMED_OUT);
{
zx_info_process_t info;
ASSERT_OK(zx_object_get_info(proc, ZX_INFO_PROCESS, &info, sizeof(info), NULL, NULL));
// `info.start_time` is only valid if the STARTED flag is set.
ASSERT_TRUE(info.flags & ZX_INFO_PROCESS_FLAG_STARTED, "process should appear as started");
EXPECT_FALSE(info.flags & ZX_INFO_PROCESS_FLAG_EXITED, "process should not appear as exited");
EXPECT_GE(info.start_time, before_start);
EXPECT_LE(info.start_time, after_start);
}
// Kill the process and wait for it to terminate.
ASSERT_OK(zx_task_kill(proc));
ASSERT_OK(zx_object_wait_one(proc, ZX_TASK_TERMINATED, ZX_TIME_INFINITE, &signals));
ASSERT_EQ(signals, ZX_TASK_TERMINATED);
{
zx_info_process_t info;
ASSERT_OK(zx_object_get_info(proc, ZX_INFO_PROCESS, &info, sizeof(info), NULL, NULL));
// `info.start_time` is only valid if the STARTED flag is set.
ASSERT_TRUE(info.flags & ZX_INFO_PROCESS_FLAG_STARTED, "process should appear as started");
EXPECT_TRUE(info.flags & ZX_INFO_PROCESS_FLAG_EXITED, "process should appear as exited");
EXPECT_EQ(info.return_code, ZX_TASK_RETCODE_SYSCALL_KILL, "process retcode invalid");
EXPECT_GE(info.start_time, before_start);
EXPECT_LE(info.start_time, after_start);
}
}
// Helper class to encapsulate starting a process with up to kNumThreads no-op child threads.
class TestProcess {
public:
// Creates the process handle, must be called first before any other function.
void CreateProcess() {
constexpr const char* kProcessName = "test_process";
EXPECT_OK(zx_process_create(zx_job_default(), kProcessName, strlen(kProcessName), 0, &process_,
&vmar_));
}
// Creates a child thread but does not start it.
void CreateThread() {
char name[32];
size_t name_length = snprintf(name, sizeof(name), "test_thread_%lu", threads_.size());
zx_handle_t thread;
ASSERT_OK(zx_thread_create(process_, name, name_length, 0, &thread));
threads_.push_back(thread);
}
// Starts the process and all child threads.
void StartProcess() { return StartProcessWithControl(nullptr); }
// Starts a process with a control channel that can be used to send commands.
// Also starts all child threads.
void StartProcessWithControl(zx_handle_t* out_control_channel) {
ASSERT_GT(threads_.size(), 0);
// The first thread must start the process.
// We don't use this event but starting a new process requires passing it a handle.
zx_handle_t event = ZX_HANDLE_INVALID;
ASSERT_OK(zx_event_create(0u, &event));
ASSERT_OK(
start_mini_process_etc(process_, threads_[0], vmar_, event, true, out_control_channel));
for (size_t i = 1; i < threads_.size(); ++i) {
ASSERT_OK(start_mini_process_thread(threads_[i], vmar_));
}
}
// Waits for a signal on the requested thread and returns true if the result
// matches |expected|.
//
// If |expected| is ZX_ERR_TIMED_OUT this waits for a finite amount of time,
// otherwise it waits forever.
bool WaitForThreadSignal(int index, zx_signals_t signal, zx_status_t expected) {
zx_time_t timeout = ZX_TIME_INFINITE;
if (expected == ZX_ERR_TIMED_OUT)
timeout = zx_deadline_after(kTimeoutNs);
return zx_object_wait_one(threads_[index], signal, timeout, nullptr) == expected;
}
// Do this explicitly rather than in the destructor to catch any errors.
void StopProcess() {
EXPECT_OK(zx_task_kill(process_));
EXPECT_OK(zx_handle_close(process_));
EXPECT_OK(zx_handle_close(vmar_));
if (!threads_.empty()) {
EXPECT_OK(zx_handle_close_many(threads_.data(), threads_.size()));
}
}
zx_handle_t process() const { return process_; }
zx_handle_t thread(int index) const { return threads_[index]; }
zx::channel& control_channel() { return control_channel_; }
private:
zx_handle_t process_ = ZX_HANDLE_INVALID;
zx_handle_t vmar_ = ZX_HANDLE_INVALID;
zx::channel control_channel_;
std::vector<zx_handle_t> threads_;
};
TEST(ProcessTest, Suspend) {
TestProcess test_process;
ASSERT_NO_FAILURES(test_process.CreateProcess());
ASSERT_NO_FAILURES(test_process.CreateThread());
ASSERT_NO_FAILURES(test_process.StartProcess());
zx_handle_t suspend_token;
ASSERT_OK(zx_task_suspend(test_process.process(), &suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_SUSPENDED, ZX_OK));
ASSERT_OK(zx_handle_close(suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_OK));
ASSERT_NO_FAILURES(test_process.StopProcess());
}
TEST(ProcessTest, SuspendSelf) {
zx_handle_t suspend_token;
EXPECT_EQ(zx_task_suspend(zx_process_self(), &suspend_token), ZX_ERR_NOT_SUPPORTED);
}
TEST(ProcessTest, SuspendMultipleThreads) {
TestProcess test_process;
ASSERT_NO_FAILURES(test_process.CreateProcess());
ASSERT_NO_FAILURES(test_process.CreateThread());
ASSERT_NO_FAILURES(test_process.CreateThread());
ASSERT_NO_FAILURES(test_process.CreateThread());
ASSERT_NO_FAILURES(test_process.StartProcess());
zx_handle_t suspend_token;
ASSERT_OK(zx_task_suspend(test_process.process(), &suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_SUSPENDED, ZX_OK));
ASSERT_TRUE(test_process.WaitForThreadSignal(1, ZX_THREAD_SUSPENDED, ZX_OK));
ASSERT_TRUE(test_process.WaitForThreadSignal(2, ZX_THREAD_SUSPENDED, ZX_OK));
ASSERT_OK(zx_handle_close(suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_OK));
ASSERT_TRUE(test_process.WaitForThreadSignal(1, ZX_THREAD_RUNNING, ZX_OK));
ASSERT_TRUE(test_process.WaitForThreadSignal(2, ZX_THREAD_RUNNING, ZX_OK));
ASSERT_NO_FAILURES(test_process.StopProcess());
}
TEST(ProcessTest, SuspendBeforeCreatingThreads) {
TestProcess test_process;
ASSERT_NO_FAILURES(test_process.CreateProcess());
zx_handle_t suspend_token;
ASSERT_OK(zx_task_suspend(test_process.process(), &suspend_token));
ASSERT_NO_FAILURES(test_process.CreateThread());
ASSERT_NO_FAILURES(test_process.StartProcess());
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_SUSPENDED, ZX_OK));
ASSERT_OK(zx_handle_close(suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_OK));
ASSERT_NO_FAILURES(test_process.StopProcess());
}
TEST(ProcessTest, SuspendBeforeStartingThreads) {
TestProcess test_process;
ASSERT_NO_FAILURES(test_process.CreateProcess());
ASSERT_NO_FAILURES(test_process.CreateThread());
zx_handle_t suspend_token;
ASSERT_OK(zx_task_suspend(test_process.process(), &suspend_token));
ASSERT_NO_FAILURES(test_process.StartProcess());
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_SUSPENDED, ZX_OK));
ASSERT_OK(zx_handle_close(suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_OK));
ASSERT_NO_FAILURES(test_process.StopProcess());
}
TEST(ProcessTest, SuspendProcessThenThread) {
TestProcess test_process;
ASSERT_NO_FAILURES(test_process.CreateProcess());
ASSERT_NO_FAILURES(test_process.CreateThread());
ASSERT_NO_FAILURES(test_process.StartProcess());
zx_handle_t process_suspend_token;
ASSERT_OK(zx_task_suspend(test_process.process(), &process_suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_SUSPENDED, ZX_OK));
zx_handle_t thread_suspend_token;
ASSERT_OK(zx_task_suspend(test_process.thread(0), &thread_suspend_token));
// When we release the process token, the thread should remain suspended.
ASSERT_OK(zx_handle_close(process_suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_ERR_TIMED_OUT));
// Now close the thread token and it should resume.
ASSERT_OK(zx_handle_close(thread_suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_OK));
ASSERT_NO_FAILURES(test_process.StopProcess());
}
TEST(ProcessTest, SuspendThreadThenProcess) {
TestProcess test_process;
ASSERT_NO_FAILURES(test_process.CreateProcess());
ASSERT_NO_FAILURES(test_process.CreateThread());
ASSERT_NO_FAILURES(test_process.StartProcess());
zx_handle_t thread_suspend_token;
ASSERT_OK(zx_task_suspend(test_process.thread(0), &thread_suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_SUSPENDED, ZX_OK));
zx_handle_t process_suspend_token;
ASSERT_OK(zx_task_suspend(test_process.process(), &process_suspend_token));
ASSERT_OK(zx_handle_close(process_suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_ERR_TIMED_OUT));
ASSERT_OK(zx_handle_close(thread_suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_OK));
ASSERT_NO_FAILURES(test_process.StopProcess());
}
TEST(ProcessTest, SuspendThreadAndProcessBeforeStartingProcess) {
TestProcess test_process;
// Create and immediately suspend the process and thread.
ASSERT_NO_FAILURES(test_process.CreateProcess());
zx_handle_t process_suspend_token;
ASSERT_OK(zx_task_suspend(test_process.process(), &process_suspend_token));
ASSERT_NO_FAILURES(test_process.CreateThread());
zx_handle_t thread_suspend_token;
ASSERT_OK(zx_task_suspend(test_process.thread(0), &thread_suspend_token));
ASSERT_NO_FAILURES(test_process.StartProcess());
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_SUSPENDED, ZX_OK));
// Resume the process, thread should stay suspended.
ASSERT_OK(zx_handle_close(process_suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_ERR_TIMED_OUT));
ASSERT_OK(zx_handle_close(thread_suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_OK));
ASSERT_NO_FAILURES(test_process.StopProcess());
}
TEST(ProcessTest, SuspendTwice) {
TestProcess test_process;
ASSERT_NO_FAILURES(test_process.CreateProcess());
ASSERT_NO_FAILURES(test_process.CreateThread());
ASSERT_NO_FAILURES(test_process.StartProcess());
zx_handle_t suspend_tokens[2];
ASSERT_OK(zx_task_suspend(test_process.process(), &suspend_tokens[0]));
ASSERT_OK(zx_task_suspend(test_process.process(), &suspend_tokens[1]));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_SUSPENDED, ZX_OK));
ASSERT_OK(zx_handle_close(suspend_tokens[0]));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_ERR_TIMED_OUT));
ASSERT_OK(zx_handle_close(suspend_tokens[1]));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_OK));
ASSERT_NO_FAILURES(test_process.StopProcess());
}
TEST(ProcessTest, SuspendTwiceBeforeCreatingThreads) {
TestProcess test_process;
ASSERT_NO_FAILURES(test_process.CreateProcess());
zx_handle_t suspend_tokens[2];
ASSERT_OK(zx_task_suspend(test_process.process(), &suspend_tokens[0]));
ASSERT_OK(zx_task_suspend(test_process.process(), &suspend_tokens[1]));
ASSERT_NO_FAILURES(test_process.CreateThread());
ASSERT_NO_FAILURES(test_process.StartProcess());
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_SUSPENDED, ZX_OK));
ASSERT_OK(zx_handle_close(suspend_tokens[0]));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_ERR_TIMED_OUT));
ASSERT_OK(zx_handle_close(suspend_tokens[1]));
ASSERT_TRUE(test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_OK));
ASSERT_NO_FAILURES(test_process.StopProcess());
}
// This test isn't super reliable since it has to try to suspend and resume while a thread is in
// the small window while it's dying but before it's dead, but there doesn't seem to be a way
// to deterministically hit that window so unfortunately this is the best we can do.
//
// In the expected case this test will always succeed, but if there is an underlying bug it
// will occasionally fail, so if this test begins to show flakiness it likely represents a real
// bug.
TEST(ProcessTest, SuspendWithDyingThread) {
TestProcess test_process;
zx::channel control_channel;
ASSERT_NO_FAILURES(test_process.CreateProcess());
ASSERT_NO_FAILURES(test_process.CreateThread());
ASSERT_NO_FAILURES(test_process.CreateThread());
ASSERT_NO_FAILURES(test_process.CreateThread());
ASSERT_NO_FAILURES(test_process.StartProcessWithControl(control_channel.reset_and_get_address()));
// Tell the main process thread to exit. Hopefully we can catch it in the DYING state.
ASSERT_OK(mini_process_cmd_send(control_channel.get(), MINIP_CMD_THREAD_EXIT));
// Now suspend the process and make sure it still works on the live threads.
// Don't check thread 0 because that's the one that's exiting.
zx_handle_t suspend_token;
ASSERT_OK(zx_task_suspend(test_process.process(), &suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(1, ZX_THREAD_SUSPENDED, ZX_OK));
ASSERT_TRUE(test_process.WaitForThreadSignal(2, ZX_THREAD_SUSPENDED, ZX_OK));
ASSERT_OK(zx_handle_close(suspend_token));
ASSERT_TRUE(test_process.WaitForThreadSignal(1, ZX_THREAD_RUNNING, ZX_OK));
ASSERT_TRUE(test_process.WaitForThreadSignal(2, ZX_THREAD_RUNNING, ZX_OK));
ASSERT_NO_FAILURES(test_process.StopProcess());
}
template <typename InfoT>
static void TestProcessGetInfoRuntime(const uint32_t topic) {
TestProcess test_process;
ASSERT_NO_FAILURES(test_process.CreateProcess());
ASSERT_NO_FAILURES(test_process.CreateThread());
// Get info before the threads start running.
InfoT info;
size_t actual = 0;
size_t avail = 0;
ASSERT_OK(
zx_object_get_info(test_process.process(), topic, &info, sizeof(info), &actual, &avail));
ASSERT_EQ(info.cpu_time, 0);
ASSERT_EQ(info.queue_time, 0);
EXPECT_EQ(actual, 1);
EXPECT_EQ(avail, 1);
ASSERT_NO_FAILURES(test_process.StartProcess());
test_process.WaitForThreadSignal(0, ZX_THREAD_RUNNING, ZX_OK);
// We are occasionally fast enough reading the thread info to see it before it gets scheduled.
// Loop until we see the values we are looking for.
while (info.cpu_time == 0 || info.queue_time == 0) {
ASSERT_OK(
zx_object_get_info(test_process.process(), topic, &info, sizeof(info), nullptr, nullptr));
}
EXPECT_GT(info.cpu_time, 0);
EXPECT_GT(info.queue_time, 0);
ASSERT_OK(zx_task_kill(test_process.process()));
ASSERT_OK(
zx_object_wait_one(test_process.process(), ZX_TASK_TERMINATED, ZX_TIME_INFINITE, nullptr));
// Read info after process death, ensure it does not change.
ASSERT_OK(
zx_object_get_info(test_process.process(), topic, &info, sizeof(info), nullptr, nullptr));
EXPECT_GT(info.cpu_time, 0);
EXPECT_GT(info.queue_time, 0);
zx_info_task_runtime_t info2;
ASSERT_OK(
zx_object_get_info(test_process.process(), topic, &info2, sizeof(info2), nullptr, nullptr));
EXPECT_EQ(info.cpu_time, info2.cpu_time);
EXPECT_EQ(info.queue_time, info2.queue_time);
ASSERT_NO_FAILURES(test_process.StopProcess());
}
TEST(ProcessTest, GetInfoRuntime) {
TestProcessGetInfoRuntime<zx_info_task_runtime_t>(ZX_INFO_TASK_RUNTIME);
}
TEST(ProcessTest, GetInfoRuntimeV1) {
TestProcessGetInfoRuntime<zx_info_task_runtime_v1_t>(ZX_INFO_TASK_RUNTIME_V1);
}
// A stress test designed to create a race where one thread is creating a process while another
// thread is killing its parent job.
TEST(ProcessTest, CreateAndKillJobRaceStress) {
constexpr zx_duration_t kTestDuration = ZX_SEC(1);
srand(4);
struct args_t {
std::atomic<bool>* keep_running;
std::atomic<zx_handle_t>* job;
};
// Repeatedly create and kill a job.
auto killer_thread = [](void* arg) -> int {
auto [job, keep_running] = *reinterpret_cast<args_t*>(arg);
while (keep_running->load()) {
zx_handle_t handle = ZX_HANDLE_INVALID;
zx_status_t status = zx_job_create(zx_job_default(), 0, &handle);
if (status != ZX_OK) {
return status;
}
job->store(handle);
// Give the creator threads an opportunity to get the handle before killing the job.
zx_nanosleep(ZX_MSEC(10));
status = zx_task_kill(handle);
zx_handle_close(handle);
if (status != ZX_OK) {
return status;
}
handle = ZX_HANDLE_INVALID;
job->store(handle);
}
return ZX_OK;
};
// Repeatedly create a process.
auto creator_thread = [](void* arg) -> int {
auto [job, keep_running] = *reinterpret_cast<args_t*>(arg);
constexpr const char kName[] = "create-and-kill";
while (keep_running->load()) {
zx_handle_t handle = job->load();
if (handle == ZX_HANDLE_INVALID) {
continue;
}
zx_handle_t proc = ZX_HANDLE_INVALID;
zx_handle_t vmar = ZX_HANDLE_INVALID;
zx_status_t status =
zx_process_create(handle, "create-and-kill", sizeof(kName) - 1, 0, &proc, &vmar);
// We're racing with the killer_thread so it's entirely possible for zx_process_create
// to fail with ZX_ERR_BAD_HANDLE or ZX_ERR_BAD_STATE. Just ignore those.
if (status != ZX_OK && status != ZX_ERR_BAD_HANDLE && status != ZX_ERR_BAD_STATE) {
return status;
}
zx_handle_close(proc);
proc = ZX_HANDLE_INVALID;
zx_handle_close(vmar);
vmar = ZX_HANDLE_INVALID;
}
return ZX_OK;
};
std::atomic<bool> keep_running(true);
std::atomic<zx_handle_t> job(ZX_HANDLE_INVALID);
args_t args{&keep_running, &job};
thrd_t killer;
ASSERT_EQ(thrd_create(&killer, killer_thread, &args), thrd_success);
constexpr unsigned kNumCreators = 4;
thrd_t creators[kNumCreators];
for (auto& t : creators) {
ASSERT_EQ(thrd_create(&t, creator_thread, &args), thrd_success);
}
zx_nanosleep(zx_deadline_after(kTestDuration));
keep_running.store(false);
for (auto& t : creators) {
int res;
ASSERT_EQ(thrd_join(t, &res), thrd_success);
ASSERT_OK(res);
}
int res;
ASSERT_EQ(thrd_join(killer, &res), thrd_success);
ASSERT_OK(res);
zx_handle_close(args.job->load());
}
TEST(ProcessTest, ProcessStartWriteThreadState) {
zx_handle_t proc;
zx_handle_t vmar;
ASSERT_OK(zx_process_create(zx_job_default(), "ttp", 3u, 0, &proc, &vmar));
zx_handle_t thread;
ASSERT_OK(zx_thread_create(proc, "th", 2u, 0u, &thread));
// Suspend the thread before it starts.
zx_handle_t token;
ASSERT_OK(zx_task_suspend(thread, &token));
zx_handle_t event;
ASSERT_OK(zx_event_create(0u, &event));
zx_handle_t minip_chn;
ASSERT_OK(start_mini_process_etc(proc, thread, vmar, event, false, &minip_chn));
// Get a known word into memory to point the thread pointer at. It would
// be simpler and sufficient for the purpose of this test just to check
// the value of the thread register itself for a known bit pattern. But
// on older x86 hardware there is no unprivileged way to read the register
// directly (rdfsbase) and it can only be used in a memory access.
const uintptr_t kCheckValue = MINIP_THREAD_POINTER_CHECK_VALUE;
zx_handle_t vmo;
ASSERT_OK(zx_vmo_create(zx_system_get_page_size(), 0, &vmo));
ASSERT_OK(zx_vmo_write(vmo, &kCheckValue, 0, sizeof(kCheckValue)));
uintptr_t addr;
ASSERT_OK(zx_vmar_map(vmar, ZX_VM_PERM_READ, 0, vmo, 0, zx_system_get_page_size(), &addr));
EXPECT_OK(zx_handle_close(vmo));
// Wait for the new thread to reach quiescent suspended state.
zx_signals_t signals;
EXPECT_OK(zx_object_wait_one(thread, ZX_THREAD_SUSPENDED, ZX_TIME_INFINITE, &signals));
EXPECT_TRUE(signals & ZX_THREAD_SUSPENDED);
// Fetch the initial register state.
zx_thread_state_general_regs_t regs;
ASSERT_OK(zx_thread_read_state(thread, ZX_THREAD_STATE_GENERAL_REGS, &regs, sizeof(regs)));
EXPECT_EQ(regs.*kThreadRegister, 0);
// Write it back with the thread register pointed at our memory.
regs.*kThreadRegister = addr;
ASSERT_OK(zx_thread_write_state(thread, ZX_THREAD_STATE_GENERAL_REGS, &regs, sizeof(regs)));
// Now let the thread run again.
EXPECT_OK(zx_handle_close(token));
// Complete the startup handshake that had to be delayed while the thread
// was suspended.
EXPECT_OK(mini_process_wait_for_ack(minip_chn));
// Now have it read from its thread pointer and check the value.
EXPECT_OK(mini_process_cmd(minip_chn, MINIP_CMD_CHECK_THREAD_POINTER, nullptr));
// All done!
EXPECT_EQ(mini_process_cmd(minip_chn, MINIP_CMD_EXIT_NORMAL, nullptr), ZX_ERR_PEER_CLOSED);
EXPECT_OK(zx_handle_close(proc));
EXPECT_OK(zx_handle_close(vmar));
EXPECT_OK(zx_handle_close(thread));
}
// This checks for lock ordering violations between the acquiring the process dispatcher lock and
// the process handle table lock.
//
// Given that the 'standard' lock ordering is handle table and then dispatcher, this is really
// testing that ZX_INFO_PROCESS_VMOS doesn't acquire in the other order.
//
// object_wait_async and port_cancel are used as syscalls that will allow us to hold the handle
// table lock whilst operating on a process in a way that requires grabbing the dispatcher lock.
// This represents the 'correct' ordering.
TEST(ProcessTest, ProcessWaitAsyncCancelSelf) {
// Start up a thread in a mini-process that is given a copy of the process handle and will
// create a port and infinitely loop doing process.wait_async(port) + port.cancel(process)
zx::process process;
zx::vmar vmar;
constexpr const char kProcessName[] = "test_process";
ASSERT_OK(zx::process::create(*zx::job::default_job(), kProcessName, sizeof(kProcessName), 0,
&process, &vmar));
zx::thread thread;
constexpr const char kThreadName[] = "test_thread";
ASSERT_OK(zx::thread::create(process, kThreadName, sizeof(kThreadName), 0, &thread));
zx::channel cntrl_channel;
zx::process process_dup;
ASSERT_OK(process.duplicate(ZX_RIGHT_SAME_RIGHTS, &process_dup));
ASSERT_OK(start_mini_process_etc(process.get(), thread.get(), vmar.get(), process_dup.release(),
true, cntrl_channel.reset_and_get_address()));
ASSERT_OK(mini_process_cmd_send(cntrl_channel.get(), MINIP_CMD_WAIT_ASYNC_CANCEL));
// Call get_info several times on the process. We're trying to trigger a race that will cause a
// kernel deadlock. In testing with the deadlock present 10000 iterations would reliably trigger
// and does not take very long to run.
zx_info_vmo_t vmo;
size_t actual;
size_t available;
for (int i = 0; i < 10000; i++) {
ASSERT_OK(process.get_info(ZX_INFO_PROCESS_VMOS, &vmo, sizeof(vmo), &actual, &available));
}
// We need to explicitly kill the process tree as we gave the mini-process a handle to itself,
// so it is able to keep itself alive when we close our copies of the handles otherwise.
ASSERT_OK(process.kill());
zx_signals_t pending;
ASSERT_OK(process.wait_one(ZX_TASK_TERMINATED, zx::time::infinite(), &pending));
}
TEST(ProcessTest, ForbidDestroyRootVmar) {
zx::process process;
zx::vmar vmar;
constexpr const char kProcessName[] = "test_process";
ASSERT_OK(zx::process::create(*zx::job::default_job(), kProcessName, sizeof(kProcessName), 0,
&process, &vmar));
// Attempt to destroy the vmar. We accept this call either succeeding or not being supported, as
// long as our future get_info call doesn't cause a kernel panic.
zx_status_t result = vmar.destroy();
ASSERT_TRUE(result == ZX_OK || result == ZX_ERR_NOT_SUPPORTED);
// Query the address space.
zx_info_maps_t map;
size_t actual, avail;
ASSERT_OK(process.get_info(ZX_INFO_PROCESS_MAPS, &map, sizeof(map), &actual, &avail));
}
TEST(ProcessTest, ProcessHwTraceContextIdProperty) {
// Handle whether or not the needed syscall is enabled.
// It is only enabled with the "kernel.enable-debugging-syscalls=true"
// kernel command line argument. Unsupported architectures act as-if the
// syscall is disabled.
bool debugging_syscalls_enabled = false;
#ifdef __x86_64__
{
zx_status_t status;
char too_small;
status = zx_object_get_property(zx_process_self(), ZX_PROP_PROCESS_HW_TRACE_CONTEXT_ID,
&too_small, sizeof(too_small));
if (status != ZX_ERR_NOT_SUPPORTED) {
EXPECT_EQ(status, ZX_ERR_BUFFER_TOO_SMALL, "unexpected status: %d", status);
// If we didn't get ZX_ERR_NOT_SUPPORTED, then the needed support is present and enabled.
debugging_syscalls_enabled = true;
}
}
#endif
auto supported_read_prop_test = [](const char* test_name) {
zx_status_t status;
uintptr_t prop_aspace = 0;
status = zx_object_get_property(zx_process_self(), ZX_PROP_PROCESS_HW_TRACE_CONTEXT_ID,
&prop_aspace, sizeof(prop_aspace));
EXPECT_EQ(status, ZX_OK, "%s: zx_object_get_property failed: %d", test_name, status);
// We can't verify the value, but we can at least check it's reasonable.
EXPECT_NE(prop_aspace, 0, "%s", test_name);
EXPECT_EQ(prop_aspace & (zx_system_get_page_size() - 1), 0, "%s", test_name);
};
auto unsupported_read_prop_test = [](const char* test_name) {
zx_status_t status;
uintptr_t prop_aspace;
status = zx_object_get_property(zx_process_self(), ZX_PROP_PROCESS_HW_TRACE_CONTEXT_ID,
&prop_aspace, sizeof(prop_aspace));
EXPECT_EQ(status, ZX_ERR_NOT_SUPPORTED, "%s: unexpected status: %d", test_name, status);
};
fit::function<void(const char*)> read_prop_test;
printf("Note: debugging syscalls are %s\n", debugging_syscalls_enabled ? "enabled" : "disabled");
if (debugging_syscalls_enabled) {
read_prop_test = std::move(supported_read_prop_test);
} else {
read_prop_test = std::move(unsupported_read_prop_test);
}
// Verify obtaining the context ID works through the stages of process
// creation/death.
static const char name[] = "context-id-test";
{
zx::process proc;
zx::vmar vmar;
ASSERT_OK(
zx::process::create(*zx::job::default_job(), name, sizeof(name) - 1, 0, &proc, &vmar));
read_prop_test("process created");
zx::thread thread;
ASSERT_OK(zx::thread::create(proc, name, sizeof(name) - 1, 0u, &thread));
zx::event event;
ASSERT_OK(zx::event::create(0u, &event));
zx::channel cmd_channel;
ASSERT_OK(start_mini_process_etc(proc.get(), thread.get(), vmar.get(), event.get(), true,
cmd_channel.reset_and_get_address()));
ASSERT_OK(mini_process_cmd(cmd_channel.get(), MINIP_CMD_ECHO_MSG, nullptr));
read_prop_test("process live");
ASSERT_EQ(mini_process_cmd(cmd_channel.get(), MINIP_CMD_EXIT_NORMAL, nullptr),
ZX_ERR_PEER_CLOSED);
zx_signals_t signals;
ASSERT_OK(proc.wait_one(ZX_TASK_TERMINATED, zx::time::infinite(), &signals));
ASSERT_EQ(signals, ZX_TASK_TERMINATED);
read_prop_test("process dead");
}
// The property is read-only.
{
uintptr_t prop_to_set = 0;
zx_status_t status = zx_object_set_property(
zx_process_self(), ZX_PROP_PROCESS_HW_TRACE_CONTEXT_ID, &prop_to_set, sizeof(prop_to_set));
EXPECT_EQ(status, ZX_ERR_INVALID_ARGS, "unexpected status: %d", status);
}
}
TEST(ProcessTest, InfoProcessNoProcessHandles) {
// Create process with a thread
zx::process process;
zx::thread thread;
zx::vmar vmar;
zx::unowned<zx::job> job = zx::job::default_job();
ASSERT_OK(zx::process::create(*job, "mini-p", 3u, 0, &process, &vmar));
ASSERT_OK(zx::thread::create(process, "mini-p", 2u, 0u, &thread));
zx::event event;
ASSERT_OK(zx::event::create(0u, &event));
zx_handle_t cmd_channel;
EXPECT_OK(start_mini_process_etc(process.get(), thread.get(), vmar.get(), event.get(), true,
&cmd_channel));
// Get our KOID.
zx_info_handle_basic_t handle_info = {};
size_t actual = 0;
size_t avail = 0;
ASSERT_OK(
process.get_info(ZX_INFO_HANDLE_BASIC, &handle_info, sizeof(handle_info), &actual, &avail));
EXPECT_EQ(actual, 1);
EXPECT_EQ(avail, 1);
const zx_koid_t process_koid = handle_info.koid;
// Check that our handle count is one.
zx_info_handle_count_t handle_count = {};
ASSERT_OK(
process.get_info(ZX_INFO_HANDLE_COUNT, &handle_count, sizeof(handle_count), &actual, &avail));
EXPECT_EQ(actual, 1);
EXPECT_EQ(avail, 1);
EXPECT_EQ(handle_count.handle_count, 1);
// Close the handle, the runnable thread will keep the process alive.
process.reset();
// Query the job and make sure the process is still listed.
std::vector<zx_koid_t> children;
do {
ASSERT_OK(job->get_info(ZX_INFO_JOB_PROCESSES, children.data(),
sizeof(zx_koid_t) * children.size(), &actual, &avail));
children.resize(avail);
} while (avail > actual);
EXPECT_TRUE(std::any_of(children.begin(), children.end(),
[process_koid](zx_koid_t koid) { return koid == process_koid; }));
// Now retrieve the handle.
zx::process original_process;
EXPECT_OK(job->get_child(process_koid, ZX_RIGHT_SAME_RIGHTS, &original_process));
// Cleanup
EXPECT_EQ(mini_process_cmd(cmd_channel, MINIP_CMD_EXIT_NORMAL, nullptr), ZX_ERR_PEER_CLOSED);
}
} // namespace