| // Copyright 2016 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/fit/defer.h> |
| #include <lib/zx/socket.h> |
| #include <lib/zx/vmar.h> |
| #include <lib/zx/vmo.h> |
| |
| #include <string_view> |
| #include <utility> |
| |
| #include <fbl/array.h> |
| #include <zxtest/zxtest.h> |
| |
| namespace { |
| |
| constexpr std::string_view kMsg1 = "12345"; |
| constexpr std::string_view kMsg2 = "abcdef"; |
| constexpr std::string_view kMsg3 = "ghijklm"; |
| constexpr uint32_t kReadBufSize = std::max({kMsg1.size(), kMsg2.size(), kMsg3.size()}) + 1; |
| |
| zx_signals_t GetSignals(const zx::socket& socket) { |
| zx_signals_t pending = 0; |
| socket.wait_one(0u, zx::time(), &pending); |
| return pending; |
| } |
| |
| TEST(SocketTest, EndpointsAreRelated) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| // Check that koids line up. |
| zx_info_handle_basic_t info_local = {}, info_remote = {}; |
| ASSERT_OK( |
| local.get_info(ZX_INFO_HANDLE_BASIC, &info_local, sizeof(info_local), nullptr, nullptr)); |
| ASSERT_OK( |
| remote.get_info(ZX_INFO_HANDLE_BASIC, &info_remote, sizeof(info_remote), nullptr, nullptr)); |
| EXPECT_NE(info_local.koid, 0u, "zero koid!"); |
| EXPECT_NE(info_local.related_koid, 0u, "zero peer koid!"); |
| EXPECT_NE(info_remote.koid, 0u, "zero koid!"); |
| EXPECT_NE(info_remote.related_koid, 0u, "zero peer koid!"); |
| EXPECT_EQ(info_local.koid, info_remote.related_koid, "mismatched koids!"); |
| EXPECT_EQ(info_remote.koid, info_local.related_koid, "mismatched koids!"); |
| } |
| |
| TEST(SocketTest, EmptySocketShouldWait) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| uint32_t data; |
| ASSERT_STATUS(local.read(0u, &data, sizeof(data), nullptr), ZX_ERR_SHOULD_WAIT); |
| } |
| |
| TEST(SocketTest, WriteReadDataVerify) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| constexpr uint32_t write_data[] = {0xdeadbeef, 0xc0ffee}; |
| |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, &write_data[0], sizeof(write_data[0]), &count)); |
| ASSERT_EQ(count, sizeof(write_data[0])); |
| } |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, &write_data[1], sizeof(write_data[1]), &count)); |
| ASSERT_EQ(count, sizeof(write_data[1])); |
| } |
| { |
| size_t count; |
| uint32_t read_data[std::size(write_data)]; |
| ASSERT_OK(remote.read(0u, read_data, sizeof(read_data), &count)); |
| ASSERT_EQ(count, sizeof(read_data)); |
| EXPECT_BYTES_EQ(read_data, write_data, sizeof(write_data)); |
| } |
| |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, write_data, sizeof(write_data), &count)); |
| ASSERT_EQ(count, sizeof(write_data)); |
| } |
| { |
| size_t count; |
| uint32_t read_data[std::size(write_data)]; |
| ASSERT_OK(remote.read(0u, read_data, sizeof(read_data), &count)); |
| ASSERT_EQ(count, sizeof(read_data)); |
| EXPECT_BYTES_EQ(read_data, write_data, sizeof(write_data)); |
| } |
| } |
| |
| TEST(SocketTest, PeerClosedError) { |
| zx::socket local; |
| { |
| zx::socket remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| // remote gets closed here. |
| } |
| |
| uint32_t data; |
| ASSERT_STATUS(local.write(0u, &data, sizeof(data), nullptr), ZX_ERR_PEER_CLOSED); |
| } |
| |
| TEST(SocketTest, PeekingLeavesData) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| constexpr uint32_t write_data[] = {0xdeadbeef, 0xc0ffee}; |
| |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, &write_data[0], sizeof(write_data[0]), &count)); |
| ASSERT_EQ(count, sizeof(write_data[0])); |
| } |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, &write_data[1], sizeof(write_data[1]), &count)); |
| ASSERT_EQ(count, sizeof(write_data[1])); |
| } |
| |
| { |
| size_t count; |
| uint32_t read_data[std::size(write_data)]; |
| ASSERT_OK(remote.read(ZX_SOCKET_PEEK, read_data, sizeof(read_data), &count)); |
| ASSERT_EQ(count, sizeof(read_data)); |
| EXPECT_BYTES_EQ(read_data, write_data, sizeof(write_data)); |
| } |
| |
| // The message should still be pending for h1 to read. |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE); |
| |
| { |
| size_t count; |
| uint32_t read_data[std::size(write_data)]; |
| ASSERT_OK(remote.read(0u, read_data, sizeof(read_data), &count)); |
| ASSERT_EQ(count, sizeof(read_data)); |
| EXPECT_BYTES_EQ(read_data, write_data, sizeof(write_data)); |
| } |
| |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE); |
| } |
| |
| TEST(SocketTest, PeekingIntoEmpty) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| uint32_t data; |
| ASSERT_STATUS(local.read(ZX_SOCKET_PEEK, &data, sizeof(data), nullptr), ZX_ERR_SHOULD_WAIT); |
| } |
| |
| TEST(SocketTest, Signals) { |
| zx::socket local; |
| |
| { |
| zx::socket remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE); |
| |
| const size_t kAllSize = 128 * 1024; |
| const size_t kChunk = kAllSize / 16; |
| fbl::Array<char> big_buf(new char[kAllSize], kAllSize); |
| ASSERT_NOT_NULL(big_buf.data()); |
| memset(big_buf.data(), 0x66, big_buf.size()); |
| |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, big_buf.data(), kChunk, &count)); |
| ASSERT_EQ(count, kChunk); |
| } |
| |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_READABLE | ZX_SOCKET_WRITABLE); |
| |
| { |
| size_t count; |
| ASSERT_OK(remote.read(0u, big_buf.data(), big_buf.size(), &count)); |
| ASSERT_EQ(count, kChunk); |
| } |
| |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE); |
| |
| ASSERT_STATUS(local.signal_peer(ZX_SOCKET_WRITABLE, 0u), ZX_ERR_INVALID_ARGS); |
| |
| ASSERT_OK(local.signal_peer(0u, ZX_USER_SIGNAL_1)); |
| |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE | ZX_USER_SIGNAL_1); |
| // remote closed |
| } |
| |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_PEER_CLOSED); |
| } |
| |
| TEST(SocketTest, SetThreshholdsProp) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| size_t count; |
| |
| /* Set some valid and invalid threshold values and verify */ |
| count = 0; |
| ASSERT_OK(local.set_property(ZX_PROP_SOCKET_RX_THRESHOLD, &count, sizeof(count))); |
| count = 0xefffffff; |
| ASSERT_STATUS(local.set_property(ZX_PROP_SOCKET_RX_THRESHOLD, &count, sizeof(count)), |
| ZX_ERR_INVALID_ARGS); |
| count = 0; |
| ASSERT_OK(local.set_property(ZX_PROP_SOCKET_TX_THRESHOLD, &count, sizeof(count))); |
| count = 0xefffffff; |
| EXPECT_STATUS(local.set_property(ZX_PROP_SOCKET_TX_THRESHOLD, &count, sizeof(count)), |
| ZX_ERR_INVALID_ARGS); |
| } |
| |
| TEST(SocketTest, SetThreshholdsAndCheckSignals) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| /* |
| * In the code below, we are going to trigger the READ threshold |
| * signal as soon as 101 bytes are available to read, and trigger |
| * the WRITE threshold as long as we have 103 bytes we can write/ |
| */ |
| |
| /* Set valid Read/Write thresholds and verify */ |
| constexpr size_t SOCKET2_SIGNALTEST_RX_THRESHOLD = 101; |
| { |
| size_t count = SOCKET2_SIGNALTEST_RX_THRESHOLD; |
| ASSERT_OK(local.set_property(ZX_PROP_SOCKET_RX_THRESHOLD, &count, sizeof(count))); |
| } |
| |
| { |
| size_t count; |
| ASSERT_OK(local.get_property(ZX_PROP_SOCKET_RX_THRESHOLD, &count, sizeof(count))); |
| ASSERT_EQ(count, (size_t)SOCKET2_SIGNALTEST_RX_THRESHOLD); |
| } |
| |
| zx_info_socket_t info{}; |
| ASSERT_OK(remote.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr)); |
| size_t write_threshold = info.tx_buf_max - (SOCKET2_SIGNALTEST_RX_THRESHOLD + 2); |
| ASSERT_OK( |
| remote.set_property(ZX_PROP_SOCKET_TX_THRESHOLD, &write_threshold, sizeof(write_threshold))); |
| { |
| size_t count; |
| ASSERT_OK(remote.get_property(ZX_PROP_SOCKET_TX_THRESHOLD, &count, sizeof(count))); |
| ASSERT_EQ(count, write_threshold); |
| } |
| |
| /* Make sure duplicates get the same thresholds ! */ |
| zx::socket local_clone, remote_clone; |
| ASSERT_OK(local.duplicate(ZX_RIGHT_SAME_RIGHTS, &local_clone)); |
| ASSERT_OK(remote.duplicate(ZX_RIGHT_SAME_RIGHTS, &remote_clone)); |
| |
| { |
| size_t count; |
| ASSERT_OK(local_clone.get_property(ZX_PROP_SOCKET_RX_THRESHOLD, &count, sizeof(count))); |
| ASSERT_EQ(count, (size_t)SOCKET2_SIGNALTEST_RX_THRESHOLD); |
| } |
| { |
| size_t count; |
| ASSERT_OK(remote_clone.get_property(ZX_PROP_SOCKET_TX_THRESHOLD, &count, sizeof(count))); |
| ASSERT_EQ(count, write_threshold); |
| } |
| |
| /* Test starting signal state after setting thresholds */ |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE); |
| EXPECT_EQ(GetSignals(local_clone), ZX_SOCKET_WRITABLE); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_THRESHOLD); |
| EXPECT_EQ(GetSignals(remote_clone), ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_THRESHOLD); |
| |
| /* Write data and test signals */ |
| size_t bufsize = SOCKET2_SIGNALTEST_RX_THRESHOLD - 1; |
| char buf[bufsize]; |
| { |
| size_t count; |
| ASSERT_OK(remote.write(0u, buf, bufsize, &count)); |
| ASSERT_EQ(count, bufsize); |
| } |
| |
| /* |
| * We wrote less than the read and write thresholds. So we expect |
| * the READ_THRESHOLD signal to be de-asserted and the WRITE_THRESHOLD |
| * signal to be asserted. |
| */ |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE); |
| EXPECT_EQ(GetSignals(local_clone), ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE); |
| EXPECT_EQ(GetSignals(remote_clone), ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_THRESHOLD); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_THRESHOLD); |
| |
| /* |
| * Now write exactly enough data to hit the read threshold |
| */ |
| bufsize = 1; |
| { |
| size_t count; |
| ASSERT_OK(remote.write(0u, buf, bufsize, &count)); |
| ASSERT_EQ(count, bufsize); |
| } |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE | ZX_SOCKET_READ_THRESHOLD); |
| EXPECT_EQ(GetSignals(local_clone), |
| ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE | ZX_SOCKET_READ_THRESHOLD); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_THRESHOLD); |
| EXPECT_EQ(GetSignals(remote_clone), ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_THRESHOLD); |
| |
| /* |
| * Bump up the read threshold and make sure the READ THRESHOLD signal gets |
| * deasserted (and then restore the read threshold back). |
| */ |
| { |
| size_t count = SOCKET2_SIGNALTEST_RX_THRESHOLD + 50; |
| ASSERT_OK(local.set_property(ZX_PROP_SOCKET_RX_THRESHOLD, &count, sizeof(count))); |
| } |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE); |
| EXPECT_EQ(GetSignals(local_clone), ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE); |
| { |
| size_t count = SOCKET2_SIGNALTEST_RX_THRESHOLD; |
| ASSERT_OK(local.set_property(ZX_PROP_SOCKET_RX_THRESHOLD, &count, sizeof(count))); |
| } |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE | ZX_SOCKET_READ_THRESHOLD); |
| EXPECT_EQ(GetSignals(local_clone), |
| ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE | ZX_SOCKET_READ_THRESHOLD); |
| |
| /* |
| * Bump the write threshold way up and make sure the WRITE THRESHOLD signal gets |
| * deasserted (and then restore the write threshold back). |
| */ |
| { |
| size_t count = info.tx_buf_max - 10; |
| ASSERT_OK(remote.set_property(ZX_PROP_SOCKET_TX_THRESHOLD, &count, sizeof(count))); |
| } |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE); |
| EXPECT_EQ(GetSignals(remote_clone), ZX_SOCKET_WRITABLE); |
| { |
| size_t count = write_threshold; |
| ASSERT_OK(remote.set_property(ZX_PROP_SOCKET_TX_THRESHOLD, &count, sizeof(count))); |
| } |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_THRESHOLD); |
| EXPECT_EQ(GetSignals(remote_clone), ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_THRESHOLD); |
| /* |
| * Next write enough data to de-assert WRITE Threshold |
| */ |
| bufsize = write_threshold - (SOCKET2_SIGNALTEST_RX_THRESHOLD + 1); |
| fbl::Array<char> buf2(new char[bufsize], bufsize); |
| { |
| size_t count; |
| ASSERT_OK(remote.write(0u, buf2.data(), bufsize, &count)); |
| ASSERT_EQ(count, bufsize); |
| } |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE | ZX_SOCKET_READ_THRESHOLD); |
| EXPECT_EQ(GetSignals(local_clone), |
| ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE | ZX_SOCKET_READ_THRESHOLD); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE); |
| EXPECT_EQ(GetSignals(remote_clone), ZX_SOCKET_WRITABLE); |
| |
| /* |
| * Finally read enough data to de-assert the read threshold and |
| * re-assert the write threshold signals. |
| */ |
| bufsize += 10; |
| buf2.reset(new char[bufsize], bufsize); |
| { |
| size_t count; |
| ASSERT_OK(local.read(0u, buf2.data(), bufsize, &count)); |
| ASSERT_EQ(count, bufsize); |
| } |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE); |
| EXPECT_EQ(GetSignals(local_clone), ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_THRESHOLD); |
| EXPECT_EQ(GetSignals(remote_clone), ZX_SOCKET_WRITABLE | ZX_SOCKET_WRITE_THRESHOLD); |
| } |
| |
| TEST(SocketTest, SignalClosedPeer) { |
| zx::socket local; |
| { |
| zx::socket remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| // remote closed |
| } |
| ASSERT_STATUS(local.signal_peer(0u, ZX_USER_SIGNAL_0), ZX_ERR_PEER_CLOSED); |
| } |
| |
| TEST(SocketTest, PeerClosedSetProperty) { |
| zx::socket local; |
| size_t t = 1; |
| { |
| zx::socket remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| ASSERT_OK(local.set_property(ZX_PROP_SOCKET_TX_THRESHOLD, &t, sizeof(t))); |
| // remote closed |
| } |
| ASSERT_STATUS(local.set_property(ZX_PROP_SOCKET_TX_THRESHOLD, &t, sizeof(t)), ZX_ERR_PEER_CLOSED); |
| } |
| |
| TEST(SocketTest, BytesOutstanding) { |
| zx::socket local; |
| constexpr uint32_t write_data[] = {0xdeadbeef, 0xc0ffee}; |
| |
| { |
| zx::socket remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| { |
| uint32_t read_data[std::size(write_data)]; |
| ASSERT_STATUS(local.read(0u, read_data, sizeof(read_data), nullptr), ZX_ERR_SHOULD_WAIT); |
| } |
| |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, &write_data[0], sizeof(write_data[0]), &count)); |
| ASSERT_EQ(count, sizeof(write_data[0])); |
| } |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, &write_data[1], sizeof(write_data[1]), &count)); |
| ASSERT_EQ(count, sizeof(write_data[1])); |
| } |
| |
| // Check the number of bytes outstanding. |
| zx_info_socket_t info{}; |
| ASSERT_OK(remote.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr)); |
| EXPECT_EQ(info.rx_buf_available, sizeof(write_data)); |
| |
| // Check that the prior zx_socket_read call didn't disturb the pending data. |
| { |
| size_t count; |
| uint32_t read_data[std::size(write_data)]; |
| ASSERT_OK(remote.read(0u, read_data, sizeof(read_data), &count)); |
| ASSERT_EQ(count, sizeof(read_data)); |
| EXPECT_BYTES_EQ(read_data, write_data, sizeof(write_data)); |
| } |
| |
| // remote is closed |
| } |
| |
| ASSERT_STATUS(local.write(0u, &write_data[1], sizeof(write_data[1]), nullptr), |
| ZX_ERR_PEER_CLOSED); |
| } |
| |
| TEST(SocketTest, SetDispositionHandleWithoutRight) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE); |
| |
| zx_info_handle_basic_t info{}; |
| EXPECT_OK(local.get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr)); |
| EXPECT_TRUE((info.rights & ZX_RIGHT_MANAGE_SOCKET) != 0); |
| |
| zx::socket local_clone; |
| ASSERT_OK(local.duplicate(info.rights ^ ZX_RIGHT_MANAGE_SOCKET, &local_clone)); |
| |
| EXPECT_STATUS(local_clone.set_disposition(ZX_SOCKET_DISPOSITION_WRITE_DISABLED, 0), |
| ZX_ERR_ACCESS_DENIED); |
| EXPECT_STATUS(local_clone.set_disposition(0, ZX_SOCKET_DISPOSITION_WRITE_DISABLED), |
| ZX_ERR_ACCESS_DENIED); |
| } |
| |
| TEST(SocketTest, SetDispositionInvalidArgs) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| EXPECT_STATUS(local.set_disposition( |
| ZX_SOCKET_DISPOSITION_WRITE_DISABLED | ZX_SOCKET_DISPOSITION_WRITE_ENABLED, 0), |
| ZX_ERR_INVALID_ARGS); |
| EXPECT_STATUS(local.set_disposition( |
| 0, ZX_SOCKET_DISPOSITION_WRITE_DISABLED | ZX_SOCKET_DISPOSITION_WRITE_ENABLED), |
| ZX_ERR_INVALID_ARGS); |
| constexpr uint32_t invalid_disposition = |
| 1337 & ~(ZX_SOCKET_DISPOSITION_WRITE_DISABLED | ZX_SOCKET_DISPOSITION_WRITE_ENABLED); |
| EXPECT_STATUS(local.set_disposition(invalid_disposition, 0), ZX_ERR_INVALID_ARGS); |
| EXPECT_STATUS(local.set_disposition(0, invalid_disposition), ZX_ERR_INVALID_ARGS); |
| } |
| |
| void disable_write_helper(bool disable_local_write, bool disable_remote_write) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| zx_signals_t local_state = ZX_SOCKET_WRITABLE; |
| zx_signals_t remote_state = ZX_SOCKET_WRITABLE; |
| EXPECT_EQ(GetSignals(local), local_state); |
| EXPECT_EQ(GetSignals(remote), remote_state); |
| |
| auto write_data = [](zx::socket& endpoint, std::string_view msg) { |
| size_t count; |
| ASSERT_OK(endpoint.write(0u, msg.data(), msg.size(), &count)); |
| ASSERT_EQ(count, msg.size()); |
| }; |
| |
| // Write some data on endpoints that are about to get their writes disabled. Endpoints that keep |
| // their write privilege will be written on later: it confirms that disabling writes on a peer |
| // does not prevent the other end from writing data. |
| if (disable_local_write) { |
| ASSERT_NO_FAILURES(write_data(local, kMsg1)); |
| remote_state |= ZX_SOCKET_READABLE; |
| EXPECT_EQ(GetSignals(remote), remote_state); |
| } |
| if (disable_remote_write) { |
| ASSERT_NO_FAILURES(write_data(remote, kMsg2)); |
| local_state |= ZX_SOCKET_READABLE; |
| EXPECT_EQ(GetSignals(local), local_state); |
| } |
| |
| // Set the dispositions. |
| { |
| uint32_t local_disposition = 0; |
| uint32_t remote_disposition = 0; |
| if (disable_local_write) { |
| local_disposition = ZX_SOCKET_DISPOSITION_WRITE_DISABLED; |
| local_state |= ZX_SOCKET_WRITE_DISABLED; |
| local_state ^= ZX_SOCKET_WRITABLE; |
| remote_state |= ZX_SOCKET_PEER_WRITE_DISABLED; |
| } |
| if (disable_remote_write) { |
| remote_disposition = ZX_SOCKET_DISPOSITION_WRITE_DISABLED; |
| remote_state ^= ZX_SOCKET_WRITABLE; |
| remote_state |= ZX_SOCKET_WRITE_DISABLED; |
| local_state |= ZX_SOCKET_PEER_WRITE_DISABLED; |
| } |
| |
| ASSERT_OK(local.set_disposition(local_disposition, remote_disposition)); |
| EXPECT_EQ(GetSignals(local), local_state); |
| EXPECT_EQ(GetSignals(remote), remote_state); |
| } |
| |
| // Attempt to write data on both endpoints. It should fail if their writes were disabled. |
| { |
| auto try_write_data = [write_data](zx::socket& endpoint, zx_signals_t& peer_state, |
| bool write_is_disabled, std::string_view msg) { |
| if (write_is_disabled) { |
| ASSERT_STATUS(endpoint.write(0u, msg.data(), msg.size(), nullptr), ZX_ERR_BAD_STATE); |
| // Furthermore, writes can't be re-enabled when there is buffered data. |
| ASSERT_STATUS(endpoint.set_disposition(ZX_SOCKET_DISPOSITION_WRITE_ENABLED, 0), |
| ZX_ERR_BAD_STATE); |
| } else { |
| write_data(endpoint, msg); |
| peer_state |= ZX_SOCKET_READABLE; |
| } |
| }; |
| ASSERT_NO_FAILURES(try_write_data(local, remote_state, disable_local_write, kMsg1)); |
| EXPECT_EQ(GetSignals(remote), remote_state); |
| ASSERT_NO_FAILURES(try_write_data(remote, local_state, disable_remote_write, kMsg2)); |
| EXPECT_EQ(GetSignals(local), local_state); |
| } |
| |
| auto read_and_verify_data = [](zx::socket& endpoint, std::string_view msg) { |
| char rbuf[kReadBufSize]; |
| size_t count; |
| ASSERT_OK(endpoint.read(0u, rbuf, sizeof(rbuf), &count)); |
| ASSERT_EQ(count, msg.size()); |
| ASSERT_BYTES_EQ(rbuf, msg.data(), msg.size()); |
| }; |
| |
| // Consume the data of both endpoints. Then try to read more: depending on the disposition of the |
| // peer, it should fail one way or another. |
| { |
| auto consume_data = [read_and_verify_data](zx::socket& endpoint, zx_signals_t& state, |
| bool peer_write_disabled, std::string_view msg) { |
| read_and_verify_data(endpoint, msg); |
| state ^= ZX_SOCKET_READABLE; |
| zx_status_t expected = peer_write_disabled ? ZX_ERR_BAD_STATE : ZX_ERR_SHOULD_WAIT; |
| char rbuf[kReadBufSize]; |
| ASSERT_STATUS(endpoint.read(0u, rbuf, 1u, nullptr), expected); |
| }; |
| ASSERT_NO_FAILURES(consume_data(local, local_state, disable_remote_write, kMsg2)); |
| EXPECT_EQ(GetSignals(local), local_state); |
| ASSERT_NO_FAILURES(consume_data(remote, remote_state, disable_local_write, kMsg1)); |
| EXPECT_EQ(GetSignals(remote), remote_state); |
| } |
| |
| // Re-enable writes on both endpoints, and confirm that reading/writing works from both ends. |
| EXPECT_OK(local.set_disposition(ZX_SOCKET_DISPOSITION_WRITE_ENABLED, |
| ZX_SOCKET_DISPOSITION_WRITE_ENABLED)); |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE); |
| |
| ASSERT_NO_FAILURES(write_data(local, kMsg2)); |
| ASSERT_NO_FAILURES(write_data(remote, kMsg3)); |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE | ZX_SOCKET_READABLE); |
| |
| ASSERT_NO_FAILURES(read_and_verify_data(local, kMsg3)); |
| ASSERT_NO_FAILURES(read_and_verify_data(remote, kMsg2)); |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE); |
| } |
| |
| TEST(SocketTest, DisableWriteLocal) { disable_write_helper(true, false); } |
| |
| TEST(SocketTest, DisableWritePeer) { disable_write_helper(false, true); } |
| |
| TEST(SocketTest, DisableWriteBoth) { disable_write_helper(true, true); } |
| |
| TEST(SocketTest, SetDispositionOfClosedPeerWithBufferedData) { |
| zx::socket local; |
| { |
| zx::socket remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| EXPECT_EQ(GetSignals(local), ZX_SOCKET_WRITABLE); |
| EXPECT_EQ(GetSignals(remote), ZX_SOCKET_WRITABLE); |
| |
| { |
| size_t count; |
| EXPECT_OK(remote.write(0u, kMsg1.data(), kMsg1.size(), &count)); |
| EXPECT_EQ(count, kMsg1.size()); |
| } |
| |
| EXPECT_OK(local.set_disposition(0, ZX_SOCKET_DISPOSITION_WRITE_DISABLED)); |
| // There is buffered data, so we can't re-enable writes. |
| EXPECT_STATUS(local.set_disposition(0, ZX_SOCKET_DISPOSITION_WRITE_ENABLED), ZX_ERR_BAD_STATE); |
| } |
| |
| // Even though the peer is now closed, there is still buffered data so the writes can't be |
| // re-enabled. |
| EXPECT_STATUS(local.set_disposition(0, ZX_SOCKET_DISPOSITION_WRITE_ENABLED), ZX_ERR_BAD_STATE); |
| } |
| |
| TEST(SocketTest, ShortWrite) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| |
| zx_info_socket_t info{}; |
| ASSERT_OK(local.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr)); |
| const size_t buffer_size = info.rx_buf_max + 1; |
| fbl::Array<char> buffer(new char[buffer_size], buffer_size); |
| |
| size_t written = ~(size_t)0; // This should get overwritten by the syscall. |
| ASSERT_OK(local.write(0u, buffer.data(), buffer_size, &written)); |
| ASSERT_LT(written, buffer_size); |
| } |
| |
| TEST(SocketTest, Datagram) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, kMsg1.data(), kMsg1.size(), &count)); |
| ASSERT_EQ(count, kMsg1.size()); |
| } |
| |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, kMsg2.data(), kMsg2.size(), &count)); |
| ASSERT_EQ(count, kMsg2.size()); |
| } |
| |
| // zircon/kernel/object/include/object/mbuf.h: kPayloadSize ~ 2kb |
| static constexpr size_t kLargerThanMBufPayloadSize = 4096; |
| uint8_t write_data[kLargerThanMBufPayloadSize]; |
| for (uint32_t i = 0; i < std::size(write_data); i++) { |
| write_data[i] = static_cast<uint8_t>(i); |
| } |
| |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, write_data, sizeof(write_data), &count)); |
| ASSERT_EQ(count, sizeof(write_data)); |
| } |
| |
| { |
| zx_info_socket_t info{}; |
| ASSERT_OK(remote.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr)); |
| EXPECT_EQ(info.rx_buf_available, kMsg1.size()); |
| } |
| // Read less bytes than in the first datagram, the remaining bytes of the first datagram should |
| // be truncated. |
| { |
| size_t count; |
| uint8_t read_data[kMsg1.size()]; |
| ASSERT_OK(remote.read(0u, read_data, kMsg1.size() - 1, &count)); |
| ASSERT_EQ(count, kMsg1.size() - 1); |
| EXPECT_BYTES_EQ(read_data, kMsg1.substr(0, kMsg1.size() - 1).data(), kMsg1.size() - 1); |
| } |
| |
| { |
| zx_info_socket_t info{}; |
| ASSERT_OK(remote.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr)); |
| EXPECT_EQ(info.rx_buf_available, kMsg2.size()); |
| } |
| { |
| size_t count; |
| uint8_t read_data[kMsg2.size()]; |
| ASSERT_OK(remote.read(0u, read_data, sizeof(read_data), &count)); |
| ASSERT_EQ(count, kMsg2.size()); |
| EXPECT_BYTES_EQ(read_data, kMsg2.data(), kMsg2.size()); |
| } |
| |
| { |
| zx_info_socket_t info{}; |
| ASSERT_OK(remote.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr)); |
| EXPECT_EQ(info.rx_buf_available, sizeof(write_data)); |
| } |
| { |
| size_t count; |
| uint8_t read_data[kLargerThanMBufPayloadSize]; |
| ASSERT_OK(remote.read(0u, read_data, sizeof(read_data), &count)); |
| ASSERT_EQ(count, sizeof(write_data)); |
| EXPECT_BYTES_EQ(read_data, write_data, sizeof(write_data)); |
| } |
| |
| { |
| zx_info_socket_t info{}; |
| ASSERT_OK(remote.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr)); |
| EXPECT_EQ(info.rx_buf_available, 0); |
| } |
| } |
| |
| TEST(SocketTest, DatagramPeek) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, kMsg1.data(), kMsg1.size(), &count)); |
| ASSERT_EQ(count, kMsg1.size()); |
| } |
| |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, kMsg2.data(), kMsg2.size(), &count)); |
| ASSERT_EQ(count, kMsg2.size()); |
| } |
| |
| // Short peek. |
| { |
| size_t count; |
| uint8_t read_data[kMsg1.size()]; |
| ASSERT_OK(remote.read(ZX_SOCKET_PEEK, read_data, kMsg1.size() - 1, &count)); |
| ASSERT_EQ(count, kMsg1.size() - 1); |
| EXPECT_BYTES_EQ(read_data, kMsg1.data(), kMsg1.size() - 1); |
| } |
| |
| // Full peek should still see the 1st packet. |
| { |
| size_t count; |
| uint8_t read_data[kMsg1.size()]; |
| ASSERT_OK(remote.read(ZX_SOCKET_PEEK, read_data, kMsg1.size(), &count)); |
| ASSERT_EQ(count, kMsg1.size()); |
| EXPECT_BYTES_EQ(read_data, kMsg1.data(), kMsg1.size()); |
| } |
| |
| // Read and consume the 1st packet. |
| { |
| size_t count; |
| uint8_t read_data[kMsg1.size()]; |
| ASSERT_OK(remote.read(0u, read_data, kMsg1.size(), &count)); |
| ASSERT_EQ(count, kMsg1.size()); |
| EXPECT_BYTES_EQ(read_data, kMsg1.data(), kMsg1.size()); |
| } |
| |
| // Now peek should see the 2nd packet. |
| { |
| size_t count; |
| uint8_t read_data[kMsg2.size()]; |
| ASSERT_OK(remote.read(ZX_SOCKET_PEEK, read_data, kMsg2.size(), &count)); |
| ASSERT_EQ(count, kMsg2.size()); |
| EXPECT_BYTES_EQ(read_data, kMsg2.data(), kMsg2.size()); |
| } |
| } |
| |
| TEST(SocketTest, DatagramPeekEmpty) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| char data; |
| ASSERT_STATUS(local.read(ZX_SOCKET_PEEK, &data, sizeof(data), nullptr), ZX_ERR_SHOULD_WAIT); |
| } |
| |
| TEST(SocketTest, DatagramNoShortWrite) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| |
| zx_info_socket_t info{}; |
| ASSERT_OK(remote.get_info(ZX_INFO_SOCKET, &info, sizeof(info), nullptr, nullptr)); |
| EXPECT_GT(info.tx_buf_max, 0); |
| |
| // Pick a size for a huge datagram, and make sure not to overflow. |
| size_t buffer_size = info.tx_buf_max * 2; |
| ASSERT_GT(buffer_size, 0); |
| |
| fbl::Array<char> buffer(new char[buffer_size]{}, buffer_size); |
| ASSERT_NOT_NULL(buffer.data()); |
| |
| size_t written = ~0u; |
| ASSERT_STATUS(local.write(0u, buffer.data(), buffer_size, &written), ZX_ERR_OUT_OF_RANGE); |
| // Since the syscall failed, it should not have overwritten this output |
| // parameter. |
| ASSERT_EQ(written, ~0u); |
| } |
| |
| TEST(SocketTest, ZeroSize) { |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(0, &local, &remote)); |
| char buffer; |
| |
| EXPECT_STATUS(local.read(0, &buffer, 0, nullptr), ZX_ERR_SHOULD_WAIT); |
| EXPECT_OK(local.write(0, "a", 1, nullptr)); |
| EXPECT_OK(remote.read(0, &buffer, 0, nullptr)); |
| EXPECT_OK(remote.read(0, &buffer, 0, nullptr)); |
| |
| local.reset(); |
| remote.reset(); |
| ASSERT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| |
| EXPECT_STATUS(local.read(0, &buffer, 0, nullptr), ZX_ERR_SHOULD_WAIT); |
| EXPECT_OK(remote.write(0, "a", 1, nullptr)); |
| EXPECT_OK(local.read(0, &buffer, 0, nullptr)); |
| EXPECT_OK(local.read(0, &buffer, 0, nullptr)); |
| } |
| |
| TEST(SocketTest, ReadIntoNullBuffer) { |
| zx::socket a, b; |
| ASSERT_OK(zx::socket::create(0, &a, &b)); |
| |
| ASSERT_OK(a.write(0, "A", 1, nullptr)); |
| |
| size_t actual; |
| ASSERT_STATUS(b.read(0, nullptr, 1, &actual), ZX_ERR_INVALID_ARGS); |
| } |
| |
| TEST(SocketTest, StreamReadIntoBadBuffer) { |
| zx::socket a, b; |
| ASSERT_OK(zx::socket::create(0, &a, &b)); |
| |
| ASSERT_OK(a.write(0, "A", 1, nullptr)); |
| constexpr size_t kSize = 4096; |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(kSize, 0, &vmo)); |
| |
| zx_vaddr_t addr; |
| |
| // Note, no options means the buffer is not writable. |
| ASSERT_OK(zx::vmar::root_self()->map(0, 0, vmo, 0, kSize, &addr)); |
| |
| auto unmap = fit::defer([&]() { |
| // Cleanup the mapping we created. |
| zx::vmar::root_self()->unmap(addr, kSize); |
| }); |
| |
| size_t actual = 99; |
| void* buffer = reinterpret_cast<void*>(addr); |
| ASSERT_NE(nullptr, buffer); |
| |
| // Will fail because buffer points at memory that isn't writable. |
| ASSERT_STATUS(b.read(0, buffer, 1, &actual), ZX_ERR_INVALID_ARGS); |
| |
| // See that it's unmodified. |
| // |
| // N.B. this test is actually stricter than what is promised by the interface. The contract |
| // does not explicitly promise that |actual| is unmodified on error. If you find that this test |
| // has failed, it does not necessarily indicate a bug. |
| ASSERT_EQ(99, actual); |
| } |
| |
| TEST(SocketTest, WriteFromNullBuffer) { |
| zx::socket a, b; |
| ASSERT_OK(zx::socket::create(0, &a, &b)); |
| |
| ASSERT_STATUS(a.write(0, nullptr, 1, nullptr), ZX_ERR_INVALID_ARGS); |
| } |
| |
| TEST(SocketTest, WriteFromBadBuffer) { |
| zx::socket a, b; |
| ASSERT_OK(zx::socket::create(0, &a, &b)); |
| |
| constexpr size_t kSize = 4096; |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(kSize, 0, &vmo)); |
| |
| zx_vaddr_t addr; |
| |
| // Note, no options means the buffer is not readable. |
| ASSERT_OK(zx::vmar::root_self()->map(0, 0, vmo, 0, kSize, &addr)); |
| |
| auto unmap = fit::defer([&]() { |
| // Cleanup the mapping we created. |
| zx::vmar::root_self()->unmap(addr, kSize); |
| }); |
| |
| void* buffer = reinterpret_cast<void*>(addr); |
| ASSERT_NE(nullptr, buffer); |
| |
| // Will fail because buffer points at memory that isn't readable. |
| size_t actual; |
| ASSERT_STATUS(b.write(0, buffer, 1, &actual), ZX_ERR_INVALID_ARGS); |
| } |
| |
| TEST(SocketTest, WriteFromPartialBadBuffer) { |
| zx::socket a, b; |
| ASSERT_OK(zx::socket::create(0, &a, &b)); |
| |
| constexpr size_t kVmoSize = 16 * 1024; |
| constexpr size_t kOpSize = kVmoSize * 2; |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(kVmoSize, 0, &vmo)); |
| |
| zx_vaddr_t addr; |
| |
| ASSERT_OK( |
| zx::vmar::root_self()->map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, vmo, 0, kVmoSize, &addr)); |
| |
| auto unmap = fit::defer([&]() { |
| // Cleanup the mapping we created. |
| zx::vmar::root_self()->unmap(addr, kVmoSize); |
| }); |
| |
| void* buffer = reinterpret_cast<void*>(addr); |
| ASSERT_NE(nullptr, buffer); |
| |
| // There's no data yet in the socket, so it should be writable, but not readable. |
| EXPECT_TRUE(!(GetSignals(a) & ZX_SOCKET_READABLE)); |
| EXPECT_TRUE(GetSignals(b) & ZX_SOCKET_WRITABLE); |
| |
| // Perform a large write that goes out of bounds of our memory mapping. |
| size_t actual; |
| ASSERT_STATUS(b.write(0, buffer, kOpSize, &actual), ZX_ERR_INVALID_ARGS); |
| |
| // Check that the readable signal matches up with our ability to actually read. |
| bool has_read_signal = !!(GetSignals(a) & ZX_SOCKET_READABLE); |
| bool could_read = a.read(0, buffer, 1, &actual) == ZX_OK; |
| EXPECT_EQ(has_read_signal, could_read); |
| } |
| |
| TEST(SocketTest, StreamReadToPartialBadBuffer) { |
| zx::socket a, b; |
| ASSERT_OK(zx::socket::create(0, &a, &b)); |
| |
| constexpr size_t kVmoSize = 16 * 1024; |
| constexpr size_t kOpSize = kVmoSize * 2; |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(kVmoSize, 0, &vmo)); |
| |
| zx_vaddr_t addr; |
| |
| ASSERT_OK( |
| zx::vmar::root_self()->map(ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, vmo, 0, kVmoSize, &addr)); |
| |
| auto unmap = fit::defer([&]() { |
| // Cleanup the mapping we created. |
| zx::vmar::root_self()->unmap(addr, kVmoSize); |
| }); |
| |
| void* buffer = reinterpret_cast<void*>(addr); |
| ASSERT_NE(nullptr, buffer); |
| |
| // First fill the socket with data. |
| size_t actual; |
| while (b.write(0, buffer, kVmoSize, &actual) == ZX_OK) |
| ; |
| |
| // Validate that the socket is readable, but not writable. |
| EXPECT_TRUE(GetSignals(a) & ZX_SOCKET_READABLE); |
| EXPECT_TRUE(!(GetSignals(b) & ZX_SOCKET_WRITABLE)); |
| |
| // Perform a large read that goes out of bounds of our memory mapping. |
| ASSERT_STATUS(a.read(0, buffer, kOpSize, &actual), ZX_ERR_INVALID_ARGS); |
| |
| // Check that the signal and our ability to write match up. |
| bool has_write_signal = !!(GetSignals(b) & ZX_SOCKET_WRITABLE); |
| bool could_write = b.write(0, buffer, 1, &actual) == ZX_OK; |
| EXPECT_EQ(has_write_signal, could_write); |
| |
| // Read everything out of the socket. |
| while (a.read(0, buffer, kVmoSize, &actual) == ZX_OK) |
| ; |
| |
| // The socket should now be writable. |
| EXPECT_TRUE(GetSignals(b) & ZX_SOCKET_WRITABLE); |
| } |
| |
| TEST(SocketTest, ReuseDatagramBuffer) { |
| zx::socket a, b; |
| ASSERT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &a, &b)); |
| |
| char buffer[3000]; |
| memset(buffer, 0xAA, sizeof(buffer)); |
| // Write two datagrams. |
| buffer[0] = 'a'; |
| size_t actual; |
| EXPECT_OK(a.write(0, buffer, 1, &actual)); |
| buffer[0] = 'b'; |
| EXPECT_OK(a.write(0, buffer, 1, &actual)); |
| |
| // Now read back both datagrams. |
| EXPECT_OK(b.read(0, buffer, 1, &actual)); |
| EXPECT_EQ(actual, 1); |
| EXPECT_EQ(buffer[0], 'a'); |
| EXPECT_OK(b.read(0, buffer, 1, &actual)); |
| EXPECT_EQ(actual, 1); |
| EXPECT_EQ(buffer[0], 'b'); |
| |
| // Write a large datagram that might span two buffers internally. |
| buffer[0] = 'c'; |
| EXPECT_OK(a.write(0, buffer, sizeof(buffer), &actual)); |
| EXPECT_EQ(actual, sizeof(buffer)); |
| // Write a second short datagram |
| buffer[0] = 'd'; |
| EXPECT_OK(a.write(0, buffer, 1, &actual)); |
| |
| // Now do a short read to consume the first datagram. |
| EXPECT_OK(b.read(0, buffer, 1, &actual)); |
| EXPECT_EQ(actual, 1); |
| EXPECT_EQ(buffer[0], 'c'); |
| |
| // Reading again should give us the second datagram we wrote, as the remaining of the first should |
| // have been discarded. |
| EXPECT_OK(b.read(0, buffer, 1, &actual)); |
| EXPECT_EQ(actual, 1); |
| EXPECT_EQ(buffer[0], 'd'); |
| |
| // At this point the socket should be empty. |
| EXPECT_FALSE(GetSignals(b) & ZX_SOCKET_READABLE); |
| } |
| |
| enum class ReadBufferFaultsAt { |
| kBeginning, |
| kMiddle, |
| }; |
| |
| enum class ReadBehaviour { |
| kConsume, |
| kPeek, |
| }; |
| |
| class DatagramSocketBadBufferTest |
| : public zxtest::TestWithParam<std::tuple<ReadBufferFaultsAt, ReadBehaviour>> {}; |
| |
| TEST_P(DatagramSocketBadBufferTest, Read) { |
| const auto [read_buffer_faults_at, read_behaviour] = GetParam(); |
| |
| zx::socket local, remote; |
| ASSERT_OK(zx::socket::create(ZX_SOCKET_DATAGRAM, &local, &remote)); |
| |
| // A packet size that is large enough to span multiple |Mbuf|s. |
| constexpr size_t kNumPages = 2; |
| const size_t page_size = zx_system_get_page_size(); |
| const size_t packet_size = page_size * kNumPages; |
| |
| // Enqueue two packets to the socket so that we can see what packet(s) |
| // remain when reading into a bad buffer. |
| constexpr char kFirstPacketByte = 'A'; |
| constexpr char kSecondPacketByte = 'B'; |
| std::vector<char> buf(packet_size, kFirstPacketByte); |
| { |
| size_t count; |
| ASSERT_OK(local.write(0u, buf.data(), buf.size(), &count)); |
| ASSERT_EQ(count, buf.size()); |
| std::fill(buf.begin(), buf.end(), kSecondPacketByte); |
| ASSERT_OK(local.write(0u, buf.data(), buf.size(), &count)); |
| ASSERT_EQ(count, buf.size()); |
| } |
| |
| constexpr int kValidPermFlags = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE; |
| zx::vmo vmo; |
| ASSERT_OK(zx::vmo::create(packet_size, 0, &vmo)); |
| zx_vaddr_t addr; |
| ASSERT_OK(zx::vmar::root_self()->map(kValidPermFlags, 0, vmo, 0, packet_size, &addr)); |
| auto unmap = fit::defer([&]() { |
| // Cleanup the mapping we created. |
| zx::vmar::root_self()->unmap(addr, packet_size); |
| addr = 0; |
| }); |
| ASSERT_NE(0, addr); |
| |
| // Performing a read where the buffer triggers a fault. Where the fault occurs |
| // and whether the datagram is dropped depends on this test's parameter. |
| { |
| zx_vaddr_t protect_addr; |
| switch (read_buffer_faults_at) { |
| case ReadBufferFaultsAt::kBeginning: |
| protect_addr = addr; |
| break; |
| case ReadBufferFaultsAt::kMiddle: |
| protect_addr = addr + page_size; |
| break; |
| } |
| ASSERT_OK(zx::vmar::root_self()->protect(0, protect_addr, page_size)); |
| |
| size_t count; |
| uint32_t read_flags = 0; |
| switch (read_behaviour) { |
| case ReadBehaviour::kConsume: |
| break; |
| case ReadBehaviour::kPeek: |
| read_flags |= ZX_SOCKET_PEEK; |
| break; |
| } |
| ASSERT_STATUS(remote.read(read_flags, reinterpret_cast<char*>(addr), packet_size, &count), |
| ZX_ERR_INVALID_ARGS); |
| } |
| |
| // Perform a read where the whole buffer is valid; the buffer does not fault |
| // anywhere. |
| { |
| ASSERT_OK(zx::vmar::root_self()->protect(kValidPermFlags, addr, packet_size)); |
| |
| std::vector<char> packets_remaining; |
| switch (read_behaviour) { |
| case ReadBehaviour::kConsume: |
| // The first packet should have been dropped by the first (faulting) |
| // consuming read above. |
| break; |
| case ReadBehaviour::kPeek: |
| // Expect to read the first packet; the first packet shouldn't have been |
| // dropped by the first (faulting) peek. |
| packets_remaining.push_back(kFirstPacketByte); |
| break; |
| } |
| // The second packet should always be available. |
| packets_remaining.push_back(kSecondPacketByte); |
| |
| for (auto it = packets_remaining.begin(); it != packets_remaining.end(); ++it) { |
| size_t count; |
| ASSERT_OK(remote.read(0u, reinterpret_cast<char*>(addr), packet_size, &count)); |
| ASSERT_EQ(count, packet_size); |
| std::fill(buf.begin(), buf.end(), *it); |
| EXPECT_BYTES_EQ(reinterpret_cast<char*>(addr), buf.data(), packet_size); |
| } |
| } |
| |
| // The socket(s) should now be empty. |
| { |
| uint32_t data; |
| ASSERT_STATUS(local.read(0u, &data, sizeof(data), nullptr), ZX_ERR_SHOULD_WAIT); |
| ASSERT_STATUS(remote.read(0u, &data, sizeof(data), nullptr), ZX_ERR_SHOULD_WAIT); |
| } |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| SocketTest, DatagramSocketBadBufferTest, |
| zxtest::Combine(zxtest::Values(ReadBufferFaultsAt::kBeginning, ReadBufferFaultsAt::kMiddle), |
| zxtest::Values(ReadBehaviour::kConsume, ReadBehaviour::kPeek))); |
| |
| } // namespace |