mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
sockets: ignore io.EOF from view.ReadAt
Reported-by: syzbot+5466463b7604c2902875@syzkaller.appspotmail.com PiperOrigin-RevId: 337451896
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user