From c002fc36f9bbf0fe3ed8b7712c72376f8f8190c1 Mon Sep 17 00:00:00 2001 From: Andrei Vagin Date: Thu, 15 Oct 2020 23:13:40 -0700 Subject: sockets: ignore io.EOF from view.ReadAt Reported-by: syzbot+5466463b7604c2902875@syzkaller.appspotmail.com PiperOrigin-RevId: 337451896 --- pkg/sentry/socket/netlink/socket.go | 7 ++++ pkg/sentry/socket/netstack/netstack.go | 5 +++ test/syscalls/linux/socket_ip_tcp_generic.cc | 51 ++++++++++++++++++++++++++++ test/syscalls/linux/socket_netlink_route.cc | 44 ++++++++++++++++++++++++ test/syscalls/linux/socket_netlink_util.cc | 11 ++++++ test/syscalls/linux/socket_netlink_util.h | 8 +++++ 6 files changed, 126 insertions(+) diff --git a/pkg/sentry/socket/netlink/socket.go b/pkg/sentry/socket/netlink/socket.go index 5ddcd4be5..3baad098b 100644 --- a/pkg/sentry/socket/netlink/socket.go +++ b/pkg/sentry/socket/netlink/socket.go @@ -16,6 +16,7 @@ package netlink import ( + "io" "math" "gvisor.dev/gvisor/pkg/abi/linux" @@ -748,6 +749,12 @@ func (s *socketOpsCommon) sendMsg(ctx context.Context, src usermem.IOSequence, t buf := make([]byte, src.NumBytes()) n, err := src.CopyIn(ctx, buf) + // io.EOF can be only returned if src is a file, this means that + // sendMsg is called from splice and the error has to be ignored in + // this case. + if err == io.EOF { + err = nil + } if err != nil { // Don't partially consume messages. return 0, syserr.FromError(err) diff --git a/pkg/sentry/socket/netstack/netstack.go b/pkg/sentry/socket/netstack/netstack.go index 87e30d742..211f07947 100644 --- a/pkg/sentry/socket/netstack/netstack.go +++ b/pkg/sentry/socket/netstack/netstack.go @@ -587,6 +587,11 @@ func (i *ioSequencePayload) Payload(size int) ([]byte, *tcpip.Error) { } v := buffer.NewView(size) if _, err := i.src.CopyIn(i.ctx, v); err != nil { + // EOF can be returned only if src is a file and this means it + // is in a splice syscall and the error has to be ignored. + if err == io.EOF { + return v, nil + } return nil, tcpip.ErrBadAddress } return v, nil diff --git a/test/syscalls/linux/socket_ip_tcp_generic.cc b/test/syscalls/linux/socket_ip_tcp_generic.cc index f4b69c46c..831d96262 100644 --- a/test/syscalls/linux/socket_ip_tcp_generic.cc +++ b/test/syscalls/linux/socket_ip_tcp_generic.cc @@ -14,6 +14,7 @@ #include "test/syscalls/linux/socket_ip_tcp_generic.h" +#include #include #include #include @@ -979,6 +980,56 @@ TEST_P(TCPSocketPairTest, SetTCPUserTimeoutAboveZero) { EXPECT_EQ(get, kAbove); } +#ifdef __linux__ +TEST_P(TCPSocketPairTest, SpliceFromPipe) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + FileDescriptor rfd(fds[0]); + FileDescriptor wfd(fds[1]); + + // Fill with some random data. + std::vector buf(kPageSize / 2); + RandomizeBuffer(buf.data(), buf.size()); + ASSERT_THAT(write(wfd.get(), buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + + EXPECT_THAT( + splice(rfd.get(), nullptr, sockets->first_fd(), nullptr, kPageSize, 0), + SyscallSucceedsWithValue(buf.size())); + + std::vector rbuf(buf.size()); + ASSERT_THAT(read(sockets->second_fd(), rbuf.data(), rbuf.size()), + SyscallSucceedsWithValue(buf.size())); + EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0); +} + +TEST_P(TCPSocketPairTest, SpliceToPipe) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + FileDescriptor rfd(fds[0]); + FileDescriptor wfd(fds[1]); + + // Fill with some random data. + std::vector buf(kPageSize / 2); + RandomizeBuffer(buf.data(), buf.size()); + ASSERT_THAT(write(sockets->first_fd(), buf.data(), buf.size()), + SyscallSucceedsWithValue(buf.size())); + shutdown(sockets->first_fd(), SHUT_WR); + EXPECT_THAT( + splice(sockets->second_fd(), nullptr, wfd.get(), nullptr, kPageSize, 0), + SyscallSucceedsWithValue(buf.size())); + + std::vector rbuf(buf.size()); + ASSERT_THAT(read(rfd.get(), rbuf.data(), rbuf.size()), + SyscallSucceedsWithValue(buf.size())); + EXPECT_EQ(memcmp(rbuf.data(), buf.data(), buf.size()), 0); +} +#endif // __linux__ + TEST_P(TCPSocketPairTest, SetTCPWindowClampBelowMinRcvBufConnectedSocket) { auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); // Discover minimum receive buf by setting a really low value diff --git a/test/syscalls/linux/socket_netlink_route.cc b/test/syscalls/linux/socket_netlink_route.cc index b3fcf8e7c..241ddad74 100644 --- a/test/syscalls/linux/socket_netlink_route.cc +++ b/test/syscalls/linux/socket_netlink_route.cc @@ -13,6 +13,7 @@ // limitations under the License. #include +#include #include #include #include @@ -335,6 +336,49 @@ TEST(NetlinkRouteTest, MsgHdrMsgTrunc) { EXPECT_EQ((msg.msg_flags & MSG_TRUNC), MSG_TRUNC); } +TEST(NetlinkRouteTest, SpliceFromPipe) { + Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); + + int fds[2]; + ASSERT_THAT(pipe(fds), SyscallSucceeds()); + FileDescriptor rfd(fds[0]); + FileDescriptor wfd(fds[1]); + + struct request { + struct nlmsghdr hdr; + struct ifinfomsg ifm; + }; + + struct request req = {}; + req.hdr.nlmsg_len = sizeof(req); + req.hdr.nlmsg_type = RTM_GETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST; + req.hdr.nlmsg_seq = kSeq; + req.ifm.ifi_family = AF_UNSPEC; + req.ifm.ifi_index = loopback_link.index; + + ASSERT_THAT(write(wfd.get(), &req, sizeof(req)), + SyscallSucceedsWithValue(sizeof(req))); + + EXPECT_THAT(splice(rfd.get(), nullptr, fd.get(), nullptr, sizeof(req) + 1, 0), + SyscallSucceedsWithValue(sizeof(req))); + close(wfd.release()); + EXPECT_THAT(splice(rfd.get(), nullptr, fd.get(), nullptr, sizeof(req) + 1, 0), + SyscallSucceedsWithValue(0)); + + bool found = false; + ASSERT_NO_ERRNO(NetlinkResponse( + fd, + [&](const struct nlmsghdr* hdr) { + CheckLinkMsg(hdr, loopback_link); + found = true; + }, + false)); + EXPECT_TRUE(found) << "Netlink response does not contain any links."; +} + TEST(NetlinkRouteTest, MsgTruncMsgHdrMsgTrunc) { FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); diff --git a/test/syscalls/linux/socket_netlink_util.cc b/test/syscalls/linux/socket_netlink_util.cc index 952eecfe8..bdebea321 100644 --- a/test/syscalls/linux/socket_netlink_util.cc +++ b/test/syscalls/linux/socket_netlink_util.cc @@ -67,10 +67,21 @@ PosixError NetlinkRequestResponse( RETURN_ERROR_IF_SYSCALL_FAIL(RetryEINTR(sendmsg)(fd.get(), &msg, 0)); + return NetlinkResponse(fd, fn, expect_nlmsgerr); +} + +PosixError NetlinkResponse( + const FileDescriptor& fd, + const std::function& fn, + bool expect_nlmsgerr) { constexpr size_t kBufferSize = 4096; std::vector buf(kBufferSize); + struct iovec iov = {}; iov.iov_base = buf.data(); iov.iov_len = buf.size(); + struct msghdr msg = {}; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; // If NLM_F_MULTI is set, response is a series of messages that ends with a // NLMSG_DONE message. diff --git a/test/syscalls/linux/socket_netlink_util.h b/test/syscalls/linux/socket_netlink_util.h index e13ead406..f97276d44 100644 --- a/test/syscalls/linux/socket_netlink_util.h +++ b/test/syscalls/linux/socket_netlink_util.h @@ -41,6 +41,14 @@ PosixError NetlinkRequestResponse( const std::function& fn, bool expect_nlmsgerr); +// Call fn on all response netlink messages. +// +// To be used on requests with NLM_F_MULTI reponses. +PosixError NetlinkResponse( + const FileDescriptor& fd, + const std::function& fn, + bool expect_nlmsgerr); + // Send the passed request and call fn on all response netlink messages. // // To be used on requests without NLM_F_MULTI reponses. -- cgit v1.2.3