sockets: ignore io.EOF from view.ReadAt

Reported-by: syzbot+5466463b7604c2902875@syzkaller.appspotmail.com
PiperOrigin-RevId: 337451896
This commit is contained in:
Andrei Vagin
2020-10-15 23:15:48 -07:00
committed by gVisor bot
parent fc40ead685
commit c002fc36f9
6 changed files with 126 additions and 0 deletions
+7
View File
@@ -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)
+5
View File
@@ -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
@@ -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
@@ -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));
@@ -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.
@@ -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.