diff options
author | Ting-Yu Wang <anivia@google.com> | 2021-01-15 15:03:30 -0800 |
---|---|---|
committer | gVisor bot <gvisor-bot@google.com> | 2021-01-15 15:10:27 -0800 |
commit | ec9e263f213c59e93f9c8b8123012b3db2dddc9a (patch) | |
tree | 87f0fca5791ece2a138fe822b4067569425f6bd2 /test | |
parent | 55c7fe48d223ee5678dff7f5bf9a9e5f0482ab37 (diff) |
Correctly return EMSGSIZE when packet is too big in raw socket.
IPv4 previously accepts the packet, while IPv6 panics. Neither is the behavior
in Linux.
splice() in Linux has different behavior than in gVisor. This change documents
it in the SpliceTooLong test.
Reported-by: syzbot+b550e78e5c24d1d521f2@syzkaller.appspotmail.com
PiperOrigin-RevId: 352091286
Diffstat (limited to 'test')
-rw-r--r-- | test/syscalls/linux/packet_socket_raw.cc | 52 | ||||
-rw-r--r-- | test/syscalls/linux/socket_test_util.cc | 13 | ||||
-rw-r--r-- | test/syscalls/linux/socket_test_util.h | 7 |
3 files changed, 71 insertions, 1 deletions
diff --git a/test/syscalls/linux/packet_socket_raw.cc b/test/syscalls/linux/packet_socket_raw.cc index a7c46adbf..2ed4f6f9c 100644 --- a/test/syscalls/linux/packet_socket_raw.cc +++ b/test/syscalls/linux/packet_socket_raw.cc @@ -678,6 +678,58 @@ TEST_P(RawPacketTest, GetSocketAcceptConn) { INSTANTIATE_TEST_SUITE_P(AllInetTests, RawPacketTest, ::testing::Values(ETH_P_IP, ETH_P_ALL)); +class RawPacketMsgSizeTest : public ::testing::TestWithParam<TestAddress> {}; + +TEST_P(RawPacketMsgSizeTest, SendTooLong) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + + TestAddress addr = GetParam().WithPort(kPort); + + FileDescriptor udp_sock = + ASSERT_NO_ERRNO_AND_VALUE(Socket(addr.family(), SOCK_RAW, IPPROTO_UDP)); + + ASSERT_THAT( + connect(udp_sock.get(), reinterpret_cast<struct sockaddr*>(&addr.addr), + addr.addr_len), + SyscallSucceeds()); + + const char buf[65536] = {}; + ASSERT_THAT(send(udp_sock.get(), buf, sizeof(buf), 0), + SyscallFailsWithErrno(EMSGSIZE)); +} + +TEST_P(RawPacketMsgSizeTest, SpliceTooLong) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + + const char buf[65536] = {}; + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + ASSERT_THAT(write(fds[1], buf, sizeof(buf)), + SyscallSucceedsWithValue(sizeof(buf))); + + TestAddress addr = GetParam().WithPort(kPort); + + FileDescriptor udp_sock = + ASSERT_NO_ERRNO_AND_VALUE(Socket(addr.family(), SOCK_RAW, IPPROTO_UDP)); + + ASSERT_THAT( + connect(udp_sock.get(), reinterpret_cast<struct sockaddr*>(&addr.addr), + addr.addr_len), + SyscallSucceeds()); + + ssize_t n = splice(fds[0], nullptr, udp_sock.get(), nullptr, sizeof(buf), 0); + if (IsRunningOnGvisor()) { + EXPECT_THAT(n, SyscallFailsWithErrno(EMSGSIZE)); + } else { + // TODO(gvisor.dev/issue/138): Linux sends out multiple UDP datagrams, each + // of the size of a page. + EXPECT_THAT(n, SyscallSucceedsWithValue(sizeof(buf))); + } +} + +INSTANTIATE_TEST_SUITE_P(AllRawPacketMsgSizeTest, RawPacketMsgSizeTest, + ::testing::Values(V4Loopback(), V6Loopback())); + } // namespace } // namespace testing diff --git a/test/syscalls/linux/socket_test_util.cc b/test/syscalls/linux/socket_test_util.cc index 26dacc95e..b2a96086c 100644 --- a/test/syscalls/linux/socket_test_util.cc +++ b/test/syscalls/linux/socket_test_util.cc @@ -791,6 +791,19 @@ void RecvNoData(int sock) { SyscallFailsWithErrno(EAGAIN)); } +TestAddress TestAddress::WithPort(uint16_t port) const { + TestAddress addr = *this; + switch (addr.family()) { + case AF_INET: + reinterpret_cast<sockaddr_in*>(&addr.addr)->sin_port = htons(port); + break; + case AF_INET6: + reinterpret_cast<sockaddr_in6*>(&addr.addr)->sin6_port = htons(port); + break; + } + return addr; +} + TestAddress V4Any() { TestAddress t("V4Any"); t.addr.ss_family = AF_INET; diff --git a/test/syscalls/linux/socket_test_util.h b/test/syscalls/linux/socket_test_util.h index 75c0d4735..b3ab286b8 100644 --- a/test/syscalls/linux/socket_test_util.h +++ b/test/syscalls/linux/socket_test_util.h @@ -486,9 +486,14 @@ struct TestAddress { sockaddr_storage addr; socklen_t addr_len; - int family() const { return addr.ss_family; } explicit TestAddress(std::string description = "") : description(std::move(description)), addr(), addr_len() {} + + int family() const { return addr.ss_family; } + + // Returns a new TestAddress with specified port. If port is not supported, + // the same TestAddress is returned. + TestAddress WithPort(uint16_t port) const; }; constexpr char kMulticastAddress[] = "224.0.2.1"; |