From ec9e263f213c59e93f9c8b8123012b3db2dddc9a Mon Sep 17 00:00:00 2001 From: Ting-Yu Wang Date: Fri, 15 Jan 2021 15:03:30 -0800 Subject: 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 --- test/syscalls/linux/packet_socket_raw.cc | 52 ++++++++++++++++++++++++++++++++ test/syscalls/linux/socket_test_util.cc | 13 ++++++++ test/syscalls/linux/socket_test_util.h | 7 ++++- 3 files changed, 71 insertions(+), 1 deletion(-) (limited to 'test/syscalls/linux') 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 {}; + +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(&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(&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(&addr.addr)->sin_port = htons(port); + break; + case AF_INET6: + reinterpret_cast(&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"; -- cgit v1.2.3