blob: d1d1a170c98d1f83abe90baacf5882a76ea63765 [file]
// Copyright 2021 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/fidl/llcpp/message.h>
#include <zircon/assert.h>
#include <zircon/syscalls.h>
#include <array>
#include <memory>
#include <string>
#include <gtest/gtest.h>
#include "src/lib/fidl/llcpp/tests/types_test_utils.h"
TEST(IncomingMessage, ConstructNonOkMessage) {
constexpr auto kError = "test error";
fidl::IncomingMessage message(fidl::Result::TransportError(ZX_ERR_ACCESS_DENIED, kError));
EXPECT_FALSE(message.ok());
EXPECT_EQ(ZX_ERR_ACCESS_DENIED, message.status());
}
TEST(IncomingMessage, ConstructNonOkMessageRequiresNonOkStatus) {
#if ZX_DEBUG_ASSERT_IMPLEMENTED
ASSERT_DEATH({ fidl::IncomingMessage message(fidl::Result::DecodeError(ZX_OK)); }, "!= ZX_OK");
#else
GTEST_SKIP() << "Debug assertions are disabled";
#endif
}
// This fixture checks that handles have been closed at the end of the test case.
class IncomingMessageWithHandlesTest : public ::testing::Test {
protected:
void SetUp() override {
fidl_init_txn_header(reinterpret_cast<fidl_message_header_t*>(bytes_), /* txid */ 1,
/* ordinal */ 1);
ASSERT_EQ(ZX_OK, zx_event_create(0, &event1_));
checker_.AddEvent(event1_);
ASSERT_EQ(ZX_OK, zx_event_create(0, &event2_));
checker_.AddEvent(event2_);
handles_[0] = zx_handle_info_t{
.handle = event1_,
.type = ZX_OBJ_TYPE_EVENT,
.rights = ZX_RIGHTS_BASIC,
.unused = 0,
};
handles_[1] = zx_handle_info_t{
.handle = event2_,
.type = ZX_OBJ_TYPE_EVENT,
.rights = ZX_RIGHTS_BASIC,
.unused = 0,
};
}
void TearDown() override { checker_.CheckEvents(); }
llcpp_types_test_utils::HandleChecker checker_;
uint8_t bytes_[sizeof(fidl_message_header_t)] = {};
zx_handle_t event1_;
zx_handle_t event2_;
zx_handle_info_t handles_[2];
};
TEST_F(IncomingMessageWithHandlesTest, AdoptHandlesFromC) {
fidl_incoming_msg_t c_msg = {
.bytes = bytes_,
.handles = handles_,
.num_bytes = static_cast<uint32_t>(std::size(bytes_)),
.num_handles = static_cast<uint32_t>(std::size(handles_)),
};
auto incoming = fidl::IncomingMessage::FromEncodedCMessage(&c_msg);
EXPECT_EQ(ZX_OK, incoming.status());
}
TEST_F(IncomingMessageWithHandlesTest, AdoptHandlesWithRegularConstructor) {
auto incoming = fidl::IncomingMessage(bytes_, static_cast<uint32_t>(std::size(bytes_)), handles_,
static_cast<uint32_t>(std::size(handles_)));
EXPECT_EQ(ZX_OK, incoming.status());
}
TEST_F(IncomingMessageWithHandlesTest, ReleaseHandles) {
fidl_incoming_msg c_msg = {};
{
auto incoming = fidl::IncomingMessage(bytes_, static_cast<uint32_t>(std::size(bytes_)),
handles_, static_cast<uint32_t>(std::size(handles_)));
ASSERT_EQ(ZX_OK, incoming.status());
c_msg = std::move(incoming).ReleaseToEncodedCMessage();
// At this point, |incoming| will not close the handles.
}
for (zx_handle_info_t event : handles_) {
zx_info_handle_count_t info = {};
zx_status_t status = zx_object_get_info(event.handle, ZX_INFO_HANDLE_COUNT, &info, sizeof(info),
nullptr, nullptr);
ASSERT_EQ(ZX_OK, status);
EXPECT_GT(info.handle_count, 1U);
}
// Adopt the handles again to close them.
auto incoming = fidl::IncomingMessage::FromEncodedCMessage(&c_msg);
}
TEST_F(IncomingMessageWithHandlesTest, MoveConstructorHandleOwnership) {
auto incoming = fidl::IncomingMessage(bytes_, static_cast<uint32_t>(std::size(bytes_)), handles_,
static_cast<uint32_t>(std::size(handles_)));
fidl::IncomingMessage another{std::move(incoming)};
EXPECT_EQ(incoming.handle_actual(), 0u);
EXPECT_GT(another.handle_actual(), 0u);
EXPECT_EQ(ZX_OK, another.status());
}
TEST(IncomingMessage, ValidateTransactionalMessageHeader) {
uint8_t bytes[sizeof(fidl_message_header_t)] = {};
auto* hdr = reinterpret_cast<fidl_message_header_t*>(bytes);
fidl_init_txn_header(hdr, /* txid */ 1, /* ordinal */ 1);
// Unsupported wire-format magic number.
hdr->magic_number = 42;
{
auto incoming =
fidl::IncomingMessage(bytes, static_cast<uint32_t>(std::size(bytes)), nullptr, 0);
EXPECT_EQ(ZX_ERR_PROTOCOL_NOT_SUPPORTED, incoming.status());
EXPECT_FALSE(incoming.ok());
}
{
auto incoming = fidl::IncomingMessage(bytes, static_cast<uint32_t>(std::size(bytes)), nullptr,
0, fidl::IncomingMessage::kSkipMessageHeaderValidation);
EXPECT_EQ(ZX_OK, incoming.status());
EXPECT_TRUE(incoming.ok());
}
}
class IncomingMessageChannelReadEtcTest : public ::testing::Test {
protected:
void SetUp() override {
byte_buffer_ = std::make_unique<std::array<uint8_t, ZX_CHANNEL_MAX_MSG_BYTES>>();
handle_buffer_ = std::make_unique<std::array<zx_handle_info_t, ZX_CHANNEL_MAX_MSG_HANDLES>>();
}
fidl::BufferSpan byte_buffer_view() {
return fidl::BufferSpan(byte_buffer_->data(), ZX_CHANNEL_MAX_MSG_HANDLES);
}
cpp20::span<zx_handle_info_t> handle_buffer_view() { return cpp20::span(*handle_buffer_); }
private:
std::unique_ptr<std::array<uint8_t, ZX_CHANNEL_MAX_MSG_BYTES>> byte_buffer_;
std::unique_ptr<std::array<zx_handle_info_t, ZX_CHANNEL_MAX_MSG_HANDLES>> handle_buffer_;
};
TEST_F(IncomingMessageChannelReadEtcTest, ReadFromChannel) {
zx::channel source, sink;
ASSERT_EQ(ZX_OK, zx::channel::create(0, &source, &sink));
uint8_t bytes[sizeof(fidl_message_header_t)] = {};
auto* hdr = reinterpret_cast<fidl_message_header_t*>(bytes);
fidl_init_txn_header(hdr, /* txid */ 1, /* ordinal */ 1);
sink.write(0, bytes, std::size(bytes), nullptr, 0);
auto incoming = fidl::ChannelReadEtc(source.get(), 0, byte_buffer_view(), handle_buffer_view());
EXPECT_EQ(ZX_OK, incoming.status());
EXPECT_EQ(incoming.byte_actual(), sizeof(fidl_message_header_t));
EXPECT_EQ(0, memcmp(incoming.bytes(), bytes, incoming.byte_actual()));
EXPECT_EQ(0u, incoming.handle_actual());
auto incoming2 = fidl::ChannelReadEtc(source.get(), 0, byte_buffer_view(), handle_buffer_view());
EXPECT_EQ(ZX_ERR_SHOULD_WAIT, incoming2.status());
EXPECT_EQ(fidl::Reason::kTransportError, incoming2.reason());
EXPECT_EQ(
"FIDL operation failed due to underlying transport I/O error, "
"status: ZX_ERR_SHOULD_WAIT (-22)",
incoming2.FormatDescription());
}
TEST_F(IncomingMessageChannelReadEtcTest, ReadFromClosedChannel) {
zx::channel source, sink;
ASSERT_EQ(ZX_OK, zx::channel::create(0, &source, &sink));
sink.reset();
auto incoming = fidl::ChannelReadEtc(source.get(), 0, byte_buffer_view(), handle_buffer_view());
EXPECT_EQ(ZX_ERR_PEER_CLOSED, incoming.status());
EXPECT_EQ(fidl::Reason::kPeerClosed, incoming.reason());
}
TEST_F(IncomingMessageChannelReadEtcTest, ReadFromChannelInvalidMessage) {
zx::channel source, sink;
ASSERT_EQ(ZX_OK, zx::channel::create(0, &source, &sink));
uint8_t bytes[sizeof(fidl_message_header_t)] = {};
auto* hdr = reinterpret_cast<fidl_message_header_t*>(bytes);
// An epitaph must have zero txid, so the following is invalid.
fidl_init_txn_header(hdr, /* txid */ 42, /* ordinal */ kFidlOrdinalEpitaph);
sink.write(0, bytes, std::size(bytes), nullptr, 0);
auto incoming = fidl::ChannelReadEtc(source.get(), 0, byte_buffer_view(), handle_buffer_view());
EXPECT_EQ(ZX_ERR_INVALID_ARGS, incoming.status());
EXPECT_EQ(fidl::Reason::kUnexpectedMessage, incoming.reason());
EXPECT_EQ(
"FIDL operation failed due to unexpected message, "
"status: ZX_ERR_INVALID_ARGS (-10), detail: invalid header",
incoming.FormatDescription());
}