splice: cap splice calls to MAX_RW_COUNT

The Linux does the same.

Reported-by: syzbot+e81716e8956e92e9d56b@syzkaller.appspotmail.com
PiperOrigin-RevId: 305625439
This commit is contained in:
Andrei Vagin 2020-04-08 23:02:09 -07:00 committed by gVisor bot
parent 7297fd7238
commit a10389e783
3 changed files with 53 additions and 58 deletions

View File

@ -29,6 +29,10 @@ func doSplice(t *kernel.Task, outFile, inFile *fs.File, opts fs.SpliceOpts, nonB
return 0, syserror.EINVAL
}
if opts.Length > int64(kernel.MAX_RW_COUNT) {
opts.Length = int64(kernel.MAX_RW_COUNT)
}
var (
total int64
n int64

View File

@ -2026,6 +2026,8 @@ cc_binary(
"//test/util:file_descriptor",
"@com_google_absl//absl/strings",
gtest,
":ip_socket_test_util",
":unix_domain_socket_test_util",
"//test/util:temp_path",
"//test/util:test_main",
"//test/util:test_util",

View File

@ -23,6 +23,7 @@
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
#include "test/syscalls/linux/ip_socket_test_util.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/temp_path.h"
@ -35,61 +36,39 @@ namespace {
class SendFileTest : public ::testing::TestWithParam<int> {
protected:
PosixErrorOr<std::tuple<int, int>> Sockets() {
PosixErrorOr<std::unique_ptr<SocketPair>> Sockets(int type) {
// Bind a server socket.
int family = GetParam();
struct sockaddr server_addr = {};
switch (family) {
case AF_INET: {
struct sockaddr_in* server_addr_in =
reinterpret_cast<struct sockaddr_in*>(&server_addr);
server_addr_in->sin_family = family;
server_addr_in->sin_addr.s_addr = INADDR_ANY;
break;
if (type == SOCK_STREAM) {
return SocketPairKind{
"TCP", AF_INET, type, 0,
TCPAcceptBindSocketPairCreator(AF_INET, type, 0, false)}
.Create();
} else {
return SocketPairKind{
"UDP", AF_INET, type, 0,
UDPBidirectionalBindSocketPairCreator(AF_INET, type, 0, false)}
.Create();
}
}
case AF_UNIX: {
struct sockaddr_un* server_addr_un =
reinterpret_cast<struct sockaddr_un*>(&server_addr);
server_addr_un->sun_family = family;
server_addr_un->sun_path[0] = '\0';
break;
if (type == SOCK_STREAM) {
return SocketPairKind{
"UNIX", AF_UNIX, type, 0,
FilesystemAcceptBindSocketPairCreator(AF_UNIX, type, 0)}
.Create();
} else {
return SocketPairKind{
"UNIX", AF_UNIX, type, 0,
FilesystemBidirectionalBindSocketPairCreator(AF_UNIX, type, 0)}
.Create();
}
}
default:
return PosixError(EINVAL);
}
int server = socket(family, SOCK_STREAM, 0);
if (bind(server, &server_addr, sizeof(server_addr)) < 0) {
return PosixError(errno);
}
if (listen(server, 1) < 0) {
close(server);
return PosixError(errno);
}
// Fetch the address; both are anonymous.
socklen_t length = sizeof(server_addr);
if (getsockname(server, &server_addr, &length) < 0) {
close(server);
return PosixError(errno);
}
// Connect the client.
int client = socket(family, SOCK_STREAM, 0);
if (connect(client, &server_addr, length) < 0) {
close(server);
close(client);
return PosixError(errno);
}
// Accept on the server.
int server_client = accept(server, nullptr, 0);
if (server_client < 0) {
close(server);
close(client);
return PosixError(errno);
}
close(server);
return std::make_tuple(client, server_client);
}
};
@ -106,9 +85,7 @@ TEST_P(SendFileTest, SendMultiple) {
const TempPath out_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
// Create sockets.
std::tuple<int, int> fds = ASSERT_NO_ERRNO_AND_VALUE(Sockets());
const FileDescriptor server(std::get<0>(fds));
FileDescriptor client(std::get<1>(fds)); // non-const, reset is used.
auto socks = ASSERT_NO_ERRNO_AND_VALUE(Sockets(SOCK_STREAM));
// Thread that reads data from socket and dumps to a file.
ScopedThread th([&] {
@ -118,7 +95,7 @@ TEST_P(SendFileTest, SendMultiple) {
// Read until socket is closed.
char buf[10240];
for (int cnt = 0;; cnt++) {
int r = RetryEINTR(read)(server.get(), buf, sizeof(buf));
int r = RetryEINTR(read)(socks->first_fd(), buf, sizeof(buf));
// We cannot afford to save on every read() call.
if (cnt % 1000 == 0) {
ASSERT_THAT(r, SyscallSucceeds());
@ -152,7 +129,7 @@ TEST_P(SendFileTest, SendMultiple) {
<< ", remain=" << remain << std::endl;
// Send data and verify that sendfile returns the correct value.
int res = sendfile(client.get(), inf.get(), nullptr, remain);
int res = sendfile(socks->second_fd(), inf.get(), nullptr, remain);
// We cannot afford to save on every sendfile() call.
if (cnt % 120 == 0) {
MaybeSave();
@ -169,7 +146,7 @@ TEST_P(SendFileTest, SendMultiple) {
}
// Close socket to stop thread.
client.reset();
close(socks->release_second_fd());
th.Join();
// Verify that the output file has the correct data.
@ -183,9 +160,7 @@ TEST_P(SendFileTest, SendMultiple) {
TEST_P(SendFileTest, Shutdown) {
// Create a socket.
std::tuple<int, int> fds = ASSERT_NO_ERRNO_AND_VALUE(Sockets());
const FileDescriptor client(std::get<0>(fds));
FileDescriptor server(std::get<1>(fds)); // non-const, reset below.
auto socks = ASSERT_NO_ERRNO_AND_VALUE(Sockets(SOCK_STREAM));
// If this is a TCP socket, then turn off linger.
if (GetParam() == AF_INET) {
@ -193,7 +168,7 @@ TEST_P(SendFileTest, Shutdown) {
sl.l_onoff = 1;
sl.l_linger = 0;
ASSERT_THAT(
setsockopt(server.get(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)),
setsockopt(socks->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)),
SyscallSucceeds());
}
@ -212,12 +187,12 @@ TEST_P(SendFileTest, Shutdown) {
ScopedThread t([&]() {
size_t done = 0;
while (done < data.size()) {
int n = RetryEINTR(read)(server.get(), data.data(), data.size());
int n = RetryEINTR(read)(socks->first_fd(), data.data(), data.size());
ASSERT_THAT(n, SyscallSucceeds());
done += n;
}
// Close the server side socket.
server.reset();
close(socks->release_first_fd());
});
// Continuously stream from the file to the socket. Note we do not assert
@ -225,7 +200,7 @@ TEST_P(SendFileTest, Shutdown) {
// data is written. Eventually, we should get a connection reset error.
while (1) {
off_t offset = 0; // Always read from the start.
int n = sendfile(client.get(), inf.get(), &offset, data.size());
int n = sendfile(socks->second_fd(), inf.get(), &offset, data.size());
EXPECT_THAT(n, AnyOf(SyscallFailsWithErrno(ECONNRESET),
SyscallFailsWithErrno(EPIPE), SyscallSucceeds()));
if (n <= 0) {
@ -234,6 +209,20 @@ TEST_P(SendFileTest, Shutdown) {
}
}
TEST_P(SendFileTest, SendpageFromEmptyFileToUDP) {
auto socks = ASSERT_NO_ERRNO_AND_VALUE(Sockets(SOCK_DGRAM));
TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR));
// The value to the count argument has to be so that it is impossible to
// allocate a buffer of this size. In Linux, sendfile transfer at most
// 0x7ffff000 (MAX_RW_COUNT) bytes.
EXPECT_THAT(sendfile(socks->first_fd(), fd.get(), 0x0, 0x8000000000004),
SyscallSucceedsWithValue(0));
}
INSTANTIATE_TEST_SUITE_P(AddressFamily, SendFileTest,
::testing::Values(AF_UNIX, AF_INET));