blob: 6a9ca71e07eac3256523e2ab98a90e3b260855e5 [file] [log] [blame]
// Copyright 2018 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 <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <lib/fzl/time.h>
#include <lib/zx/bti.h>
#include <lib/zx/channel.h>
#include <lib/zx/debuglog.h>
#include <lib/zx/event.h>
#include <lib/zx/eventpair.h>
#include <lib/zx/exception.h>
#include <lib/zx/fifo.h>
#include <lib/zx/guest.h>
#include <lib/zx/handle.h>
#include <lib/zx/interrupt.h>
#include <lib/zx/job.h>
#include <lib/zx/port.h>
#include <lib/zx/process.h>
#include <lib/zx/socket.h>
#include <lib/zx/thread.h>
#include <lib/zx/time.h>
#include <lib/zx/timer.h>
#include <lib/zx/vmar.h>
#include <unittest/unittest.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/exception.h>
#include <zircon/syscalls/object.h>
#include <zircon/syscalls/port.h>
template <typename Handle>
bool duplicating(const Handle& handle) {
BEGIN_TEST;
zx_status_t expected_status = ZX_OK;
if (!zx::object_traits<Handle>::supports_duplication) {
expected_status = ZX_ERR_ACCESS_DENIED;
}
zx_handle_t copy = ZX_HANDLE_INVALID;
zx_status_t status = zx_handle_duplicate(handle.get(), ZX_RIGHT_SAME_RIGHTS, &copy);
if (copy != ZX_HANDLE_INVALID) {
zx_handle_close(copy);
}
ASSERT_EQ(status, expected_status);
END_TEST;
}
template <typename Handle>
bool user_signaling(const Handle& handle) {
BEGIN_TEST;
zx_status_t expected_status = ZX_OK;
if (!zx::object_traits<Handle>::supports_user_signal) {
expected_status = ZX_ERR_ACCESS_DENIED;
}
zx_handle_t copy = ZX_HANDLE_INVALID;
zx_status_t status = zx_object_signal(handle.get(), 0u, ZX_USER_SIGNAL_0);
if (copy != ZX_HANDLE_INVALID) {
zx_handle_close(copy);
}
ASSERT_EQ(status, expected_status);
END_TEST;
}
template <typename Handle>
bool waiting(const Handle& handle) {
BEGIN_TEST;
zx_status_t expected_status = ZX_OK;
if (!zx::object_traits<Handle>::supports_wait) {
expected_status = ZX_ERR_ACCESS_DENIED;
}
zx_handle_t copy = ZX_HANDLE_INVALID;
zx_status_t status = zx_object_wait_one(handle.get(), ZX_USER_SIGNAL_0, 0u, nullptr);
if (copy != ZX_HANDLE_INVALID) {
zx_handle_close(copy);
}
ASSERT_EQ(status, expected_status);
END_TEST;
}
template <typename Handle>
bool peering(const Handle& handle) {
BEGIN_TEST;
zx_status_t expected_status = ZX_OK;
if (!zx::object_traits<Handle>::has_peer_handle) {
expected_status = ZX_ERR_ACCESS_DENIED;
}
zx_status_t status = zx_object_signal_peer(handle.get(), 0u, ZX_USER_SIGNAL_0);
ASSERT_EQ(status, expected_status);
END_TEST;
}
[[noreturn]] void do_segfault(uintptr_t /*arg1*/, uintptr_t /*arg2*/) {
volatile int* p = 0;
*p = 1;
zx_thread_exit();
}
bool traits_test() {
BEGIN_TEST;
{
zx::event event;
ASSERT_EQ(zx::event::create(0u, &event), ZX_OK);
duplicating(event);
user_signaling(event);
waiting(event);
peering(event);
}
{
zx::thread thread;
ASSERT_EQ(zx::thread::create(*zx::process::self(), "", 0u, 0u, &thread), ZX_OK);
duplicating(thread);
user_signaling(thread);
waiting(thread);
peering(thread);
}
{
zx::process process;
zx::vmar vmar;
ASSERT_EQ(zx::process::create(*zx::job::default_job(), "", 0u, 0u, &process, &vmar), ZX_OK);
duplicating(process);
user_signaling(process);
waiting(process);
peering(process);
}
{
zx::job job;
ASSERT_EQ(zx::job::create(*zx::job::default_job(), 0u, &job), ZX_OK);
duplicating(job);
user_signaling(job);
waiting(job);
peering(job);
}
{
zx::vmo vmo;
ASSERT_EQ(zx::vmo::create(4096u, 0u, &vmo), ZX_OK);
duplicating(vmo);
user_signaling(vmo);
waiting(vmo);
peering(vmo);
}
{
// Creating a zx::bti is too hard in a generic testing
// environment. Instead, we just assert it's got the traits we
// want.
ASSERT_EQ(zx::object_traits<zx::bti>::supports_duplication, true);
ASSERT_EQ(zx::object_traits<zx::bti>::supports_user_signal, true);
ASSERT_EQ(zx::object_traits<zx::bti>::supports_wait, true);
ASSERT_EQ(zx::object_traits<zx::bti>::has_peer_handle, false);
}
{
// Creating a zx::resource is too hard in a generic testing
// environment. Instead, we just assert it's got the traits we
// want.
ASSERT_EQ(zx::object_traits<zx::resource>::supports_duplication, true);
ASSERT_EQ(zx::object_traits<zx::resource>::supports_user_signal, true);
ASSERT_EQ(zx::object_traits<zx::resource>::supports_wait, true);
ASSERT_EQ(zx::object_traits<zx::resource>::has_peer_handle, false);
}
{
zx::timer timer;
ASSERT_EQ(zx::timer::create(0u, ZX_CLOCK_MONOTONIC, &timer), ZX_OK);
duplicating(timer);
user_signaling(timer);
waiting(timer);
peering(timer);
}
{
zx::channel channel, channel2;
ASSERT_EQ(zx::channel::create(0u, &channel, &channel2), ZX_OK);
duplicating(channel);
user_signaling(channel);
waiting(channel);
peering(channel);
}
{
zx::eventpair eventpair, eventpair2;
ASSERT_EQ(zx::eventpair::create(0u, &eventpair, &eventpair2), ZX_OK);
duplicating(eventpair);
user_signaling(eventpair);
waiting(eventpair);
peering(eventpair);
}
{
zx::fifo fifo, fifo2;
ASSERT_EQ(zx::fifo::create(16u, 16u, 0u, &fifo, &fifo2), ZX_OK);
duplicating(fifo);
user_signaling(fifo);
waiting(fifo);
peering(fifo);
}
{
zx::debuglog debuglog;
ASSERT_EQ(zx::debuglog::create(zx::resource(), 0u, &debuglog), ZX_OK);
duplicating(debuglog);
user_signaling(debuglog);
waiting(debuglog);
peering(debuglog);
}
{
// Creating a zx::pmt is too hard in a generic testing
// environment. Instead, we just assert it's got the traits we
// want.
ASSERT_EQ(zx::object_traits<zx::pmt>::supports_duplication, false);
ASSERT_EQ(zx::object_traits<zx::pmt>::supports_user_signal, false);
ASSERT_EQ(zx::object_traits<zx::pmt>::supports_wait, false);
ASSERT_EQ(zx::object_traits<zx::pmt>::has_peer_handle, false);
}
{
zx::socket socket, socket2;
ASSERT_EQ(zx::socket::create(0u, &socket, &socket2), ZX_OK);
duplicating(socket);
user_signaling(socket);
waiting(socket);
peering(socket);
}
{
zx::port port;
ASSERT_EQ(zx::port::create(0u, &port), ZX_OK);
duplicating(port);
user_signaling(port);
waiting(port);
peering(port);
}
{
zx::vmar vmar;
uintptr_t addr;
ASSERT_EQ(zx::vmar::root_self()->allocate(0u, 4096u, 0u, &vmar, &addr), ZX_OK);
duplicating(vmar);
user_signaling(vmar);
waiting(vmar);
peering(vmar);
}
{
// Creating a zx::interrupt is too hard in a generic testing
// environment. Instead, we just assert it's got the traits we
// want.
ASSERT_EQ(zx::object_traits<zx::interrupt>::supports_duplication, true);
ASSERT_EQ(zx::object_traits<zx::interrupt>::supports_user_signal, false);
ASSERT_EQ(zx::object_traits<zx::interrupt>::supports_wait, true);
ASSERT_EQ(zx::object_traits<zx::interrupt>::has_peer_handle, false);
}
{
// Creating a zx::guest is too hard in a generic testing
// environment. Instead, we just assert it's got the traits we
// want.
ASSERT_EQ(zx::object_traits<zx::guest>::supports_duplication, true);
ASSERT_EQ(zx::object_traits<zx::guest>::supports_user_signal, false);
ASSERT_EQ(zx::object_traits<zx::guest>::supports_wait, false);
ASSERT_EQ(zx::object_traits<zx::guest>::has_peer_handle, false);
}
{
// Creating a zx::iommu is too hard in a generic testing
// environment. Instead, we just assert it's got the traits we
// want.
ASSERT_EQ(zx::object_traits<zx::resource>::supports_duplication, true);
ASSERT_EQ(zx::object_traits<zx::resource>::supports_user_signal, true);
ASSERT_EQ(zx::object_traits<zx::resource>::supports_wait, true);
ASSERT_EQ(zx::object_traits<zx::resource>::has_peer_handle, false);
}
{
// Create a thread that segfaults so we can catch and analyze the
// resulting exception object.
alignas(16) static uint8_t thread_stack[1024];
zx::thread thread;
zx::channel exception_channel;
ASSERT_EQ(zx::thread::create(*zx::process::self(), "", 0, 0, &thread), ZX_OK);
ASSERT_EQ(thread.create_exception_channel(0, &exception_channel), ZX_OK);
// Stack grows down, make sure to pass a pointer to the end.
ASSERT_EQ(thread.start(&do_segfault, thread_stack + sizeof(thread_stack), 0, 0), ZX_OK);
zx::exception exception;
zx_exception_info_t info;
ASSERT_EQ(exception_channel.wait_one(ZX_CHANNEL_READABLE, zx::time::infinite(), nullptr),
ZX_OK);
ASSERT_EQ(exception_channel.read(0, &info, exception.reset_and_get_address(),
sizeof(info), 1, nullptr, nullptr),
ZX_OK);
duplicating(exception);
user_signaling(exception);
waiting(exception);
peering(exception);
ASSERT_EQ(thread.kill(), ZX_OK);
ASSERT_EQ(thread.wait_one(ZX_THREAD_TERMINATED, zx::time::infinite(), nullptr), ZX_OK);
}
END_TEST;
}
BEGIN_TEST_CASE(libzx_traits_tests)
RUN_TEST(traits_test)
END_TEST_CASE(libzx_traits_tests)