blob: 9f95b937e480f72b28b582394fe4ecc893fee7b2 [file] [log] [blame]
// 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/cpp/wire/internal/transport_channel.h>
#include <lib/fidl/cpp/wire/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(IncomingHeaderAndMessage, ConstructNonOkMessage) {
constexpr auto kError = "test error";
auto message = fidl::IncomingHeaderAndMessage::Create(
fidl::Status::TransportError(ZX_ERR_ACCESS_DENIED, kError));
EXPECT_FALSE(message.ok());
EXPECT_EQ(ZX_ERR_ACCESS_DENIED, message.status());
}
TEST(IncomingHeaderAndMessage, ConstructNonOkMessageRequiresNonOkStatus) {
#if ZX_DEBUG_ASSERT_IMPLEMENTED
ASSERT_DEATH(
{ auto message = fidl::IncomingHeaderAndMessage::Create(fidl::Status::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::InitTxnHeader(reinterpret_cast<fidl_message_header_t*>(bytes_), /* txid */ 1,
/* ordinal */ 1, fidl::MessageDynamicFlags::kStrictMethod);
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] = event1_;
handles_[1] = event2_;
handle_metadata_[0] = fidl_channel_handle_metadata_t{
.obj_type = ZX_OBJ_TYPE_EVENT,
.rights = ZX_RIGHTS_BASIC,
};
handle_metadata_[1] = fidl_channel_handle_metadata_t{
.obj_type = ZX_OBJ_TYPE_EVENT,
.rights = ZX_RIGHTS_BASIC,
};
}
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_t handles_[2];
fidl_channel_handle_metadata_t handle_metadata_[2];
};
TEST_F(IncomingMessageWithHandlesTest, AdoptHandlesWithRegularConstructor) {
auto incoming = fidl::IncomingHeaderAndMessage::Create(
bytes_, static_cast<uint32_t>(std::size(bytes_)), handles_, handle_metadata_,
static_cast<uint32_t>(std::size(handles_)));
EXPECT_EQ(ZX_OK, incoming.status());
}
TEST_F(IncomingMessageWithHandlesTest, NotEnoughBytes) {
uint8_t bytes[sizeof(fidl_message_header_t) - 1] = {};
auto incoming = fidl::IncomingHeaderAndMessage::Create(
bytes, static_cast<uint32_t>(std::size(bytes)), handles_, handle_metadata_,
static_cast<uint32_t>(std::size(handles_)));
EXPECT_EQ(ZX_ERR_INVALID_ARGS, incoming.status());
EXPECT_EQ(ZX_OK, zx_handle_close_many(handles_, std::size(handles_)));
}
TEST_F(IncomingMessageWithHandlesTest, ReleaseHandles) {
fidl_incoming_msg c_msg = {};
{
auto incoming = fidl::IncomingHeaderAndMessage::Create(
bytes_, static_cast<uint32_t>(std::size(bytes_)), handles_, handle_metadata_,
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_t event : handles_) {
zx_info_handle_count_t info = {};
zx_status_t status =
zx_object_get_info(event, 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::IncomingHeaderAndMessage::FromEncodedCMessage(c_msg);
EXPECT_EQ(ZX_OK, incoming.status());
}
TEST_F(IncomingMessageWithHandlesTest, MoveConstructorHandleOwnership) {
auto incoming = fidl::IncomingHeaderAndMessage::Create(
bytes_, static_cast<uint32_t>(std::size(bytes_)), handles_, handle_metadata_,
static_cast<uint32_t>(std::size(handles_)));
fidl::IncomingHeaderAndMessage another{std::move(incoming)};
EXPECT_EQ(incoming.handle_actual(), 0u);
EXPECT_GT(another.handle_actual(), 0u);
EXPECT_EQ(ZX_OK, another.status());
}
TEST(IncomingHeaderAndMessage, ValidateTransactionalMessageHeader) {
uint8_t bytes[sizeof(fidl_message_header_t)] = {};
auto* hdr = reinterpret_cast<fidl_message_header_t*>(bytes);
fidl::InitTxnHeader(hdr, /* txid */ 1, /* ordinal */ 1, fidl::MessageDynamicFlags::kStrictMethod);
// Unsupported wire-format magic number.
hdr->magic_number = 42;
{
auto incoming = fidl::IncomingHeaderAndMessage::Create<fidl::internal::ChannelTransport>(
bytes, static_cast<uint32_t>(std::size(bytes)), nullptr, nullptr, 0);
EXPECT_EQ(ZX_ERR_PROTOCOL_NOT_SUPPORTED, incoming.status());
EXPECT_FALSE(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_t, ZX_CHANNEL_MAX_MSG_HANDLES>>();
handle_metadata_buffer_ =
std::make_unique<std::array<fidl_channel_handle_metadata_t, ZX_CHANNEL_MAX_MSG_HANDLES>>();
}
fidl::BufferSpan byte_buffer_view() {
return fidl::BufferSpan(byte_buffer_->data(), ZX_CHANNEL_MAX_MSG_HANDLES);
}
zx_handle_t* handle_data() { return handle_buffer_->data(); }
fidl_channel_handle_metadata_t* handle_metadata_data() { return handle_metadata_buffer_->data(); }
uint32_t handle_buffer_size() { return static_cast<uint32_t>(handle_buffer_->size()); }
private:
std::unique_ptr<std::array<uint8_t, ZX_CHANNEL_MAX_MSG_BYTES>> byte_buffer_;
std::unique_ptr<std::array<zx_handle_t, ZX_CHANNEL_MAX_MSG_HANDLES>> handle_buffer_;
std::unique_ptr<std::array<fidl_channel_handle_metadata_t, ZX_CHANNEL_MAX_MSG_HANDLES>>
handle_metadata_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::InitTxnHeader(hdr, /* txid */ 1, /* ordinal */ 1, fidl::MessageDynamicFlags::kStrictMethod);
sink.write(0, bytes, std::size(bytes), nullptr, 0);
auto incoming = fidl::MessageRead(source, fidl::ChannelMessageStorageView{
.bytes = byte_buffer_view(),
.handles = handle_data(),
.handle_metadata = handle_metadata_data(),
.handle_capacity = handle_buffer_size(),
});
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::MessageRead(source, fidl::ChannelMessageStorageView{
.bytes = byte_buffer_view(),
.handles = handle_data(),
.handle_metadata = handle_metadata_data(),
.handle_capacity = handle_buffer_size(),
});
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::MessageRead(source, fidl::ChannelMessageStorageView{
.bytes = byte_buffer_view(),
.handles = handle_data(),
.handle_metadata = handle_metadata_data(),
.handle_capacity = handle_buffer_size(),
});
EXPECT_EQ(ZX_ERR_PEER_CLOSED, incoming.status());
EXPECT_EQ(fidl::Reason::kPeerClosedWhileReading, 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::InitTxnHeader(hdr, /* txid */ 42, /* ordinal */ kFidlOrdinalEpitaph,
fidl::MessageDynamicFlags::kStrictMethod);
sink.write(0, bytes, std::size(bytes), nullptr, 0);
auto incoming = fidl::MessageRead(source, fidl::ChannelMessageStorageView{
.bytes = byte_buffer_view(),
.handles = handle_data(),
.handle_metadata = handle_metadata_data(),
.handle_capacity = handle_buffer_size(),
});
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());
}