Add sanity check on return values from Write

io.Writer.Write requires err to be non-nil if n < len(v).

We could allow this but it will be irreversible if users depend on this
behavior.

Ported the test that discovered this.

PiperOrigin-RevId: 352065946
This commit is contained in:
Ting-Yu Wang 2021-01-15 12:48:58 -08:00 committed by gVisor bot
parent f7f66c8c6c
commit f1420cf484
3 changed files with 77 additions and 0 deletions

View File

@ -17,6 +17,7 @@ package buffer
import (
"bytes"
"fmt"
"io"
)
@ -167,6 +168,9 @@ func (vv *VectorisedView) ReadTo(dst io.Writer, count int, peek bool) (int, erro
if err != nil {
break
}
if n != len(v) {
panic(fmt.Sprintf("io.Writer.Write succeeded with incomplete write: %d != %d", n, len(v)))
}
}
if !peek {
vv.TrimFront(done)

View File

@ -2304,9 +2304,11 @@ cc_binary(
deps = [
":ip_socket_test_util",
":socket_test_util",
"@com_google_absl//absl/strings",
gtest,
"//test/util:test_main",
"//test/util:test_util",
"//test/util:thread_util",
],
)

View File

@ -18,10 +18,15 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <array>
#include <string>
#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/test_util.h"
#include "test/util/thread_util.h"
namespace gvisor {
namespace testing {
@ -138,5 +143,71 @@ INSTANTIATE_TEST_SUITE_P(
SetSockOpt(SOL_SOCKET, SO_REUSEADDR, &kSockOptOn)(
DualStackTCPAcceptBindPersistentListenerSocketPair(0))));
using DataTransferStressTest = SocketPairTest;
TEST_P(DataTransferStressTest, BigDataTransfer) {
// TODO(b/165912341): These are too slow on KVM platform with nested virt.
SKIP_IF(GvisorPlatform() == Platform::kKVM);
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int client_fd = sockets->first_fd();
int server_fd = sockets->second_fd();
ScopedThread echo([server_fd]() {
std::array<uint8_t, 1024> buf;
for (;;) {
ssize_t r = read(server_fd, buf.data(), buf.size());
ASSERT_THAT(r, SyscallSucceeds());
if (r == 0) {
break;
}
for (size_t i = 0; i < r;) {
ssize_t w = write(server_fd, buf.data() + i, r - i);
ASSERT_GE(w, 0);
i += w;
}
}
ASSERT_THAT(shutdown(server_fd, SHUT_WR), SyscallSucceeds());
});
const std::string chunk = "Though this upload be but little, it is fierce.";
std::string big_string;
while (big_string.size() < 31 << 20) {
big_string += chunk;
}
absl::string_view data = big_string;
ScopedThread writer([client_fd, data]() {
absl::string_view view = data;
while (!view.empty()) {
ssize_t n = write(client_fd, view.data(), view.size());
ASSERT_GE(n, 0);
view = view.substr(n);
}
ASSERT_THAT(shutdown(client_fd, SHUT_WR), SyscallSucceeds());
});
std::string buf;
buf.resize(1 << 20);
while (!data.empty()) {
ssize_t n = read(client_fd, buf.data(), buf.size());
ASSERT_GE(n, 0);
for (size_t i = 0; i < n; i += chunk.size()) {
size_t c = std::min(chunk.size(), n - i);
ASSERT_EQ(buf.substr(i, c), data.substr(i, c)) << "offset " << i;
}
data = data.substr(n);
}
// Should read EOF now.
ASSERT_THAT(read(client_fd, buf.data(), buf.size()),
SyscallSucceedsWithValue(0));
}
INSTANTIATE_TEST_SUITE_P(
AllConnectedSockets, DataTransferStressTest,
::testing::Values(IPv6TCPAcceptBindPersistentListenerSocketPair(0),
IPv4TCPAcceptBindPersistentListenerSocketPair(0),
DualStackTCPAcceptBindPersistentListenerSocketPair(0)));
} // namespace testing
} // namespace gvisor