summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAndrei Vagin <avagin@google.com>2020-10-15 23:13:40 -0700
committergVisor bot <gvisor-bot@google.com>2020-10-15 23:15:48 -0700
commitc002fc36f9bbf0fe3ed8b7712c72376f8f8190c1 (patch)
tree3bc4869c076c0e9e12e7e6b2c6b078db77a6c4e7
parentfc40ead6856603b85c978e9120c66721cdbfc87b (diff)
sockets: ignore io.EOF from view.ReadAt
Reported-by: syzbot+5466463b7604c2902875@syzkaller.appspotmail.com PiperOrigin-RevId: 337451896
-rw-r--r--pkg/sentry/socket/netlink/socket.go7
-rw-r--r--pkg/sentry/socket/netstack/netstack.go5
-rw-r--r--test/syscalls/linux/socket_ip_tcp_generic.cc51
-rw-r--r--test/syscalls/linux/socket_netlink_route.cc44
-rw-r--r--test/syscalls/linux/socket_netlink_util.cc11
-rw-r--r--test/syscalls/linux/socket_netlink_util.h8
6 files changed, 126 insertions, 0 deletions
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 <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <poll.h>
@@ -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<char> 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<char> 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<char> 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<char> 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 <arpa/inet.h>
+#include <fcntl.h>
#include <ifaddrs.h>
#include <linux/if.h>
#include <linux/netlink.h>
@@ -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<void(const struct nlmsghdr* hdr)>& fn,
+ bool expect_nlmsgerr) {
constexpr size_t kBufferSize = 4096;
std::vector<char> 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<void(const struct nlmsghdr* hdr)>& 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<void(const struct nlmsghdr* hdr)>& 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.