blob: 43e3b86c41379233b3ce444172680c832fde9d88 [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 <limits.h>
#include <new>
#include <cstddef>
#include <memory>
#include <fbl/algorithm.h>
#include <lib/fidl/coding.h>
#include <lib/fidl/internal.h>
#include <lib/zx/event.h>
#include <lib/zx/eventpair.h>
#include <unittest/unittest.h>
#include <zircon/syscalls.h>
#include "fidl/extra_messages.h"
#include "fidl_coded_types.h"
#include "fidl_structs.h"
namespace fidl {
namespace {
// test utility functions
bool IsPeerValid(const zx::unowned_eventpair& handle) {
zx_signals_t observed_signals = {};
switch (handle->wait_one(ZX_EVENTPAIR_PEER_CLOSED,
zx::deadline_after(zx::msec(1)),
&observed_signals)) {
case ZX_ERR_TIMED_OUT:
// timeout implies peer-closed was not observed
return true;
case ZX_OK:
return (observed_signals & ZX_EVENTPAIR_PEER_CLOSED) == 0;
default:
return false;
}
}
bool IsPeerValid(zx_handle_t handle) {
return IsPeerValid(zx::unowned_eventpair(handle));
}
bool EncodeErrorTest() {
BEGIN_TEST;
// If there is only one handle in the message, fidl_encode should not close beyond one handles.
// Specifically, |event_handle| should remain intact.
zx_handle_t event_handle;
ASSERT_EQ(zx_event_create(0, &event_handle), ZX_OK);
zx_handle_t handles[2] = {
ZX_HANDLE_INVALID,
event_handle,
};
constexpr uint32_t kMessageSize = sizeof(nonnullable_handle_message_layout);
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(kMessageSize);
nonnullable_handle_message_layout& message =
*reinterpret_cast<nonnullable_handle_message_layout*>(buffer.get());
message.inline_struct.handle = ZX_HANDLE_INVALID;
const char* error = nullptr;
uint32_t actual_handles;
auto status = fidl_encode(&nonnullable_handle_message_type, &message, kMessageSize, handles,
fbl::count_of(handles), &actual_handles, &error);
ASSERT_EQ(status, ZX_ERR_INVALID_ARGS);
ASSERT_NONNULL(error);
ASSERT_EQ(handles[0], ZX_HANDLE_INVALID);
ASSERT_EQ(handles[1], event_handle);
ASSERT_EQ(zx_handle_close(event_handle), ZX_OK);
END_TEST;
}
bool EncodeWithNullHandlesTest() {
BEGIN_TEST;
// When the |handles| parameter to fidl_encode is nullptr, it should still close all handles
// inside the message.
for (uint32_t num_handles : {0u, 1u}) {
zx::eventpair eventpair_a;
zx::eventpair eventpair_b;
ASSERT_EQ(zx::eventpair::create(0, &eventpair_a, &eventpair_b), ZX_OK);
constexpr uint32_t kMessageSize = sizeof(nonnullable_handle_message_layout);
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(kMessageSize);
nonnullable_handle_message_layout& message =
*reinterpret_cast<nonnullable_handle_message_layout*>(buffer.get());
message.inline_struct.handle = eventpair_a.release();
const char* error = nullptr;
uint32_t actual_handles;
auto status = fidl_encode(&nonnullable_handle_message_type, &message, kMessageSize, nullptr,
num_handles, &actual_handles, &error);
ASSERT_EQ(status, ZX_ERR_INVALID_ARGS);
ASSERT_NONNULL(error);
ASSERT_FALSE(IsPeerValid(zx::unowned_eventpair(eventpair_b)));
}
END_TEST;
}
bool EncodeWithNullOutActualHandlesTest() {
BEGIN_TEST;
// When the |out_actual_handles| parameter to fidl_encode is nullptr, it should still close
// all handles inside the message.
zx::eventpair eventpair_a;
zx::eventpair eventpair_b;
ASSERT_EQ(zx::eventpair::create(0, &eventpair_a, &eventpair_b), ZX_OK);
zx_handle_t handles[1] = {};
constexpr uint32_t kMessageSize = sizeof(nonnullable_handle_message_layout);
std::unique_ptr<uint8_t[]> buffer = std::make_unique<uint8_t[]>(kMessageSize);
nonnullable_handle_message_layout& message =
*reinterpret_cast<nonnullable_handle_message_layout*>(buffer.get());
message.inline_struct.handle = eventpair_a.release();
const char* error = nullptr;
auto status = fidl_encode(&nonnullable_handle_message_type, &message, kMessageSize, handles,
fbl::count_of(handles), nullptr, &error);
ASSERT_EQ(status, ZX_ERR_INVALID_ARGS);
ASSERT_NONNULL(error);
ASSERT_FALSE(IsPeerValid(zx::unowned_eventpair(eventpair_b)));
END_TEST;
}
bool DecodeErrorTest() {
BEGIN_TEST;
// If an unknown envelope causes the handles contained within to be closed, and later on
// an error was encountered, the handles in the unknown envelope should not be closed again.
zx::eventpair eventpair_a;
zx::eventpair eventpair_b;
ASSERT_EQ(zx::eventpair::create(0, &eventpair_a, &eventpair_b), ZX_OK);
// It should close all handles in case of failure. Add an extra handle at the end of the
// handle array to detect this.
zx::eventpair eventpair_x;
zx::eventpair eventpair_y;
ASSERT_EQ(zx::eventpair::create(0, &eventpair_x, &eventpair_y), ZX_OK);
// Assemble an encoded TableOfStructWithHandle, with first field correctly populated,
// but second field missing non-nullable handles.
constexpr uint32_t buf_size = 512;
uint8_t buffer[buf_size] = {};
TableOfStructLayout* msg = reinterpret_cast<TableOfStructLayout*>(&buffer[0]);
msg->envelope_vector.set_data(reinterpret_cast<fidl_envelope_t*>(FIDL_ALLOC_PRESENT));
msg->envelope_vector.set_count(2);
msg->envelopes.a = fidl_envelope_t {
.num_bytes = sizeof(OrdinalOneStructWithHandle),
.num_handles = 1,
.presence = FIDL_ALLOC_PRESENT
};
msg->envelopes.b = fidl_envelope_t {
.num_bytes = sizeof(OrdinalTwoStructWithManyHandles),
.num_handles = 0,
.presence = FIDL_ALLOC_PRESENT
};
msg->a = OrdinalOneStructWithHandle {
.h = FIDL_HANDLE_PRESENT,
.foo = 42
};
msg->b = OrdinalTwoStructWithManyHandles {
.h1 = ZX_HANDLE_INVALID,
.h2 = ZX_HANDLE_INVALID,
.hs = {}
};
ASSERT_TRUE(IsPeerValid(zx::unowned_eventpair(eventpair_a)));
const char* out_error = nullptr;
zx_handle_t handles[] = { eventpair_b.release(), eventpair_y.release() };
auto status = fidl_decode(&fidl_test_coding_SmallerTableOfStructWithHandleTable,
buffer, buf_size,
handles, fbl::count_of(handles), &out_error);
ASSERT_EQ(status, ZX_ERR_INVALID_ARGS);
ASSERT_NONNULL(out_error);
// The peer was closed by the decoder
ASSERT_FALSE(IsPeerValid(zx::unowned_eventpair(eventpair_a)));
ASSERT_FALSE(IsPeerValid(zx::unowned_eventpair(eventpair_x)));
END_TEST;
}
BEGIN_TEST_CASE(on_error_close_handle)
RUN_TEST(EncodeErrorTest)
RUN_TEST(EncodeWithNullHandlesTest)
RUN_TEST(EncodeWithNullOutActualHandlesTest)
RUN_TEST(DecodeErrorTest)
END_TEST_CASE(on_error_close_handle)
} // namespace
} // namespace fidl