| // Copyright 2019 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/zx/event.h> |
| #include <lib/zx/eventpair.h> |
| #include <zircon/fidl.h> |
| |
| #include <iostream> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include <fidl/llcpp/types/test/llcpp/fidl.h> |
| #include <gtest/gtest.h> |
| #include <src/lib/fidl/llcpp/tests/test_utils.h> |
| #include <src/lib/fidl/llcpp/tests/types_test_utils.h> |
| |
| namespace llcpp_test = ::llcpp::fidl::llcpp::types::test; |
| |
| TEST(XUnionPayload, Primitive) { |
| { |
| llcpp_test::TestUnion test_union; |
| EXPECT_TRUE(test_union.has_invalid_tag()); |
| int32_t primitive = 5; |
| test_union.set_primitive(fidl::unowned_ptr(&primitive)); |
| EXPECT_EQ(llcpp_test::TestUnion::Tag::kPrimitive, test_union.which()); |
| EXPECT_EQ(5, test_union.primitive()); |
| } |
| { |
| int32_t primitive = 5; |
| auto test_union = llcpp_test::TestUnion::WithPrimitive(fidl::unowned_ptr(&primitive)); |
| EXPECT_EQ(llcpp_test::TestUnion::Tag::kPrimitive, test_union.which()); |
| EXPECT_EQ(5, test_union.primitive()); |
| } |
| } |
| |
| TEST(XUnionPayload, WhichDisallowedWhenUninitialized) { |
| llcpp_test::TestUnion test_union; |
| ASSERT_DEATH({ test_union.which(); }, "!has_invalid_tag()"); |
| } |
| |
| TEST(XUnionPayload, Struct) { |
| llcpp_test::CopyableStruct copyable{.x = 5}; |
| auto test_xunion = llcpp_test::TestXUnion::WithCopyable(fidl::unowned_ptr(©able)); |
| EXPECT_EQ(llcpp_test::TestXUnion::Tag::kCopyable, test_xunion.which()); |
| } |
| |
| TEST(XUnionPayload, CopyableStruct) { |
| { |
| llcpp_test::TestUnion test_union; |
| EXPECT_TRUE(test_union.has_invalid_tag()); |
| llcpp_test::CopyableStruct copyable_struct{.x = 5}; |
| test_union.set_copyable(fidl::unowned_ptr(©able_struct)); |
| EXPECT_EQ(llcpp_test::TestUnion::Tag::kCopyable, test_union.which()); |
| } |
| { |
| llcpp_test::CopyableStruct copyable_struct{.x = 5}; |
| auto test_union = llcpp_test::TestUnion::WithCopyable(fidl::unowned_ptr(©able_struct)); |
| EXPECT_EQ(llcpp_test::TestUnion::Tag::kCopyable, test_union.which()); |
| } |
| } |
| |
| TEST(XUnionPayload, MoveOnlyStruct) { |
| { |
| llcpp_test::TestUnion test_union; |
| EXPECT_TRUE(test_union.has_invalid_tag()); |
| llcpp_test::MoveOnlyStruct move_only_struct{.h = zx::handle()}; |
| test_union.set_move_only(fidl::unowned_ptr(&move_only_struct)); |
| EXPECT_EQ(llcpp_test::TestUnion::Tag::kMoveOnly, test_union.which()); |
| } |
| { |
| llcpp_test::TestUnion test_union; |
| EXPECT_TRUE(test_union.has_invalid_tag()); |
| zx::event event; |
| ASSERT_EQ(ZX_OK, zx::event::create(0, &event)); |
| llcpp_test::MoveOnlyStruct move_only_struct{.h = std::move(event)}; |
| EXPECT_NE(ZX_HANDLE_INVALID, move_only_struct.h.get()); |
| test_union.set_move_only(fidl::unowned_ptr(&move_only_struct)); |
| EXPECT_EQ(llcpp_test::TestUnion::Tag::kMoveOnly, test_union.which()); |
| EXPECT_NE(ZX_HANDLE_INVALID, move_only_struct.h.get()); |
| } |
| { |
| llcpp_test::MoveOnlyStruct move_only_struct{.h = zx::handle()}; |
| auto test_union = llcpp_test::TestUnion::WithMoveOnly(fidl::unowned_ptr(&move_only_struct)); |
| EXPECT_EQ(llcpp_test::TestUnion::Tag::kMoveOnly, test_union.which()); |
| } |
| } |
| |
| 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(0)), |
| &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; |
| } |
| } |
| |
| TEST(MoveUnion, NoDoubleDestructPayload) { |
| zx::eventpair canary_a, canary_b; |
| ASSERT_EQ(zx::eventpair::create(0, &canary_a, &canary_b), ZX_OK); |
| ASSERT_TRUE(IsPeerValid(zx::unowned_eventpair(canary_a))); |
| // Initialize the union such that the handle |h| within the |MoveOnlyStruct| |
| // payload overlaps with the eventpair. |
| zx_handle_t h = canary_b.release(); |
| static_assert(sizeof(llcpp_test::TestUnion) == 24); |
| uint8_t dangerous_buffer[sizeof(llcpp_test::TestUnion)] = {}; |
| memcpy(&dangerous_buffer[4], &h, sizeof(h)); |
| { |
| llcpp_test::TestUnion* test_union = reinterpret_cast<llcpp_test::TestUnion*>(dangerous_buffer); |
| llcpp_test::TestUnion union_with_absent_handle; |
| llcpp_test::MoveOnlyStruct move_only_struct{.h = zx::handle()}; |
| union_with_absent_handle.set_move_only(fidl::unowned_ptr(&move_only_struct)); |
| // Manually running the move constructor. |
| new (test_union) llcpp_test::TestUnion(std::move(union_with_absent_handle)); |
| } |
| // |canary_b| should not be closed. |
| EXPECT_TRUE(IsPeerValid(zx::unowned_eventpair(canary_a))); |
| zx_handle_close(h); |
| } |
| |
| TEST(XUnion, InitialTag) { |
| llcpp_test::TestXUnion flexible_xunion; |
| EXPECT_TRUE(flexible_xunion.has_invalid_tag()); |
| |
| llcpp_test::TestStrictXUnion strict_xunion; |
| EXPECT_TRUE(strict_xunion.has_invalid_tag()); |
| } |
| |
| TEST(XUnion, UnknownBytes) { |
| auto bytes = std::vector<uint8_t>{ |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // txn header |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, // invalid ordinal |
| 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 bytes, 0 handles |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // present |
| 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, // unknown bytes |
| }; |
| auto check_tag = [](const llcpp_test::TestXUnion& xu) { |
| EXPECT_EQ(xu.which(), llcpp_test::TestXUnion::Tag::kUnknown); |
| }; |
| llcpp_types_test_utils::CannotProxyUnknownEnvelope<llcpp_test::MsgWrapper::TestXUnionResponse>( |
| bytes, {}, std::move(check_tag)); |
| } |
| |
| TEST(XUnion, UnknownHandlesResource) { |
| auto bytes = std::vector<uint8_t>{ |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // txn header |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, // invalid ordinal |
| 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // 8 bytes, 3 handles |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // present |
| 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, // unknown bytes |
| }; |
| |
| zx_handle_t h1, h2, h3; |
| ASSERT_EQ(ZX_OK, zx_event_create(0, &h1)); |
| ASSERT_EQ(ZX_OK, zx_event_create(0, &h2)); |
| ASSERT_EQ(ZX_OK, zx_event_create(0, &h3)); |
| std::vector<zx_handle_t> handles = {h1, h2, h3}; |
| |
| auto check_tag = [](const llcpp_test::TestXUnion& xu) { |
| EXPECT_EQ(xu.which(), llcpp_test::TestXUnion::Tag::kUnknown); |
| }; |
| llcpp_types_test_utils::CannotProxyUnknownEnvelope<llcpp_test::MsgWrapper::TestXUnionResponse>( |
| bytes, handles, std::move(check_tag)); |
| } |
| |
| TEST(XUnion, UnknownHandlesNonResource) { |
| auto bytes = std::vector<uint8_t>{ |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // txn header |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, // invalid ordinal |
| 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, // 8 bytes, 3 handles |
| 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // present |
| 0xde, 0xad, 0xbe, 0xef, 0x00, 0x00, 0x00, 0x00, // unknown bytes |
| }; |
| |
| zx_handle_t h1, h2, h3; |
| ASSERT_EQ(ZX_OK, zx_event_create(0, &h1)); |
| ASSERT_EQ(ZX_OK, zx_event_create(0, &h2)); |
| ASSERT_EQ(ZX_OK, zx_event_create(0, &h3)); |
| std::vector<zx_handle_t> handles = {h1, h2, h3}; |
| |
| auto check_tag = [](const llcpp_test::TestNonResourceXUnion& xu) { |
| EXPECT_EQ(xu.which(), llcpp_test::TestNonResourceXUnion::Tag::kUnknown); |
| }; |
| llcpp_types_test_utils::CannotProxyUnknownEnvelope< |
| llcpp_test::MsgWrapper::TestNonResourceXUnionResponse>(bytes, handles, std::move(check_tag)); |
| } |