summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls')
-rw-r--r--test/syscalls/BUILD4
-rw-r--r--test/syscalls/linux/BUILD15
-rw-r--r--test/syscalls/linux/exec_binary.cc28
-rw-r--r--test/syscalls/linux/open_create.cc4
-rw-r--r--test/syscalls/linux/processes.cc90
-rw-r--r--test/syscalls/linux/rename.cc33
-rw-r--r--test/syscalls/linux/sendfile.cc23
-rw-r--r--test/syscalls/linux/socket_generic.cc2
-rw-r--r--test/syscalls/linux/socket_inet_loopback.cc6
-rw-r--r--test/syscalls/linux/socket_unix_dgram.cc35
-rw-r--r--test/syscalls/linux/socket_unix_seqpacket.cc35
-rw-r--r--test/syscalls/linux/socket_unix_stream.cc80
-rw-r--r--test/syscalls/linux/udp_socket.cc81
13 files changed, 431 insertions, 5 deletions
diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD
index e43f30ba3..d6658898d 100644
--- a/test/syscalls/BUILD
+++ b/test/syscalls/BUILD
@@ -993,3 +993,7 @@ syscall_test(
syscall_test(
test = "//test/syscalls/linux:proc_net_udp_test",
)
+
+syscall_test(
+ test = "//test/syscalls/linux:processes_test",
+)
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD
index 80e2837f8..42fc363a2 100644
--- a/test/syscalls/linux/BUILD
+++ b/test/syscalls/linux/BUILD
@@ -2346,6 +2346,7 @@ cc_library(
deps = [
":socket_test_util",
":unix_domain_socket_test_util",
+ "@com_google_absl//absl/time",
gtest,
"//test/util:test_util",
],
@@ -2360,6 +2361,7 @@ cc_library(
deps = [
":socket_test_util",
":unix_domain_socket_test_util",
+ "@com_google_absl//absl/time",
gtest,
"//test/util:test_util",
],
@@ -2678,6 +2680,7 @@ cc_binary(
deps = [
":socket_test_util",
":unix_domain_socket_test_util",
+ "@com_google_absl//absl/time",
gtest,
"//test/util:test_main",
"//test/util:test_util",
@@ -4160,6 +4163,18 @@ cc_binary(
)
cc_binary(
+ name = "processes_test",
+ testonly = 1,
+ srcs = ["processes.cc"],
+ linkstatic = 1,
+ deps = [
+ "//test/util:capability_util",
+ "//test/util:test_main",
+ "//test/util:test_util",
+ ],
+)
+
+cc_binary(
name = "xattr_test",
testonly = 1,
srcs = [
diff --git a/test/syscalls/linux/exec_binary.cc b/test/syscalls/linux/exec_binary.cc
index 3797fd4c8..b0fb120c6 100644
--- a/test/syscalls/linux/exec_binary.cc
+++ b/test/syscalls/linux/exec_binary.cc
@@ -951,6 +951,34 @@ TEST(ElfTest, PIEOutOfOrderSegments) {
EXPECT_EQ(execve_errno, ENOEXEC);
}
+TEST(ElfTest, PIEOverflow) {
+ ElfBinary<64> elf = StandardElf();
+
+ elf.header.e_type = ET_DYN;
+
+ // Choose vaddr of the first segment so that the end address overflows if the
+ // segment is mapped with a non-zero offset.
+ elf.phdrs[1].p_vaddr = 0xfffffffffffff000UL - elf.phdrs[1].p_memsz;
+
+ elf.UpdateOffsets();
+
+ TempPath file = ASSERT_NO_ERRNO_AND_VALUE(CreateElfWith(elf));
+
+ pid_t child;
+ int execve_errno;
+ auto cleanup = ASSERT_NO_ERRNO_AND_VALUE(
+ ForkAndExec(file.path(), {file.path()}, {}, &child, &execve_errno));
+ if (IsRunningOnGvisor()) {
+ ASSERT_EQ(execve_errno, EINVAL);
+ } else {
+ ASSERT_EQ(execve_errno, 0);
+ int status;
+ ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0),
+ SyscallSucceedsWithValue(child));
+ EXPECT_TRUE(WIFSIGNALED(status) && WTERMSIG(status) == SIGSEGV) << status;
+ }
+}
+
// Standard dynamically linked binary with an ELF interpreter.
TEST(ElfTest, ELFInterpreter) {
ElfBinary<64> interpreter = StandardElf();
diff --git a/test/syscalls/linux/open_create.cc b/test/syscalls/linux/open_create.cc
index f8fbea79e..46f41de50 100644
--- a/test/syscalls/linux/open_create.cc
+++ b/test/syscalls/linux/open_create.cc
@@ -46,8 +46,10 @@ TEST(CreateTest, ExistingFile) {
TEST(CreateTest, CreateAtFile) {
auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
auto dirfd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_DIRECTORY, 0666));
- EXPECT_THAT(openat(dirfd.get(), "CreateAtFile", O_RDWR | O_CREAT, 0666),
+ int fd;
+ EXPECT_THAT(fd = openat(dirfd.get(), "CreateAtFile", O_RDWR | O_CREAT, 0666),
SyscallSucceeds());
+ EXPECT_THAT(close(fd), SyscallSucceeds());
}
TEST(CreateTest, HonorsUmask_NoRandomSave) {
diff --git a/test/syscalls/linux/processes.cc b/test/syscalls/linux/processes.cc
new file mode 100644
index 000000000..412582515
--- /dev/null
+++ b/test/syscalls/linux/processes.cc
@@ -0,0 +1,90 @@
+// Copyright 2021 The gVisor Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <stdint.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#include "test/util/capability_util.h"
+#include "test/util/test_util.h"
+
+namespace gvisor {
+namespace testing {
+
+int testSetPGIDOfZombie(void* arg) {
+ int p[2];
+
+ TEST_PCHECK(pipe(p) == 0);
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ pid = fork();
+ // Create a second child to repeat one of syzkaller reproducers.
+ if (pid == 0) {
+ pid = getpid();
+ TEST_PCHECK(setpgid(pid, 0) == 0);
+ TEST_PCHECK(write(p[1], &pid, sizeof(pid)) == sizeof(pid));
+ _exit(0);
+ }
+ TEST_PCHECK(pid > 0);
+ _exit(0);
+ }
+ close(p[1]);
+ TEST_PCHECK(pid > 0);
+
+ // Get PID of the second child.
+ pid_t cpid;
+ TEST_PCHECK(read(p[0], &cpid, sizeof(cpid)) == sizeof(cpid));
+
+ // Wait when both child processes will die.
+ int c;
+ TEST_PCHECK(read(p[0], &c, sizeof(c)) == 0);
+
+ // Wait the second child process to collect its zombie.
+ int status;
+ TEST_PCHECK(RetryEINTR(waitpid)(cpid, &status, 0) == cpid);
+
+ // Set the child's group.
+ TEST_PCHECK(setpgid(pid, pid) == 0);
+
+ TEST_PCHECK(RetryEINTR(waitpid)(-pid, &status, 0) == pid);
+
+ TEST_PCHECK(status == 0);
+ _exit(0);
+}
+
+TEST(Processes, SetPGIDOfZombie) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
+
+ // Fork a test process in a new PID namespace, because it needs to manipulate
+ // with reparanted processes.
+ struct clone_arg {
+ // Reserve some space for clone() to locate arguments and retcode in this
+ // place.
+ char stack[128] __attribute__((aligned(16)));
+ char stack_ptr[0];
+ } ca;
+ pid_t pid;
+ ASSERT_THAT(pid = clone(testSetPGIDOfZombie, ca.stack_ptr,
+ CLONE_NEWPID | SIGCHLD, &ca),
+ SyscallSucceeds());
+
+ int status;
+ ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0),
+ SyscallSucceedsWithValue(pid));
+ EXPECT_EQ(status, 0);
+}
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/rename.cc b/test/syscalls/linux/rename.cc
index 5458f54ad..22c8c19cf 100644
--- a/test/syscalls/linux/rename.cc
+++ b/test/syscalls/linux/rename.cc
@@ -391,6 +391,39 @@ TEST(RenameTest, FileWithOpenFd) {
EXPECT_EQ(absl::string_view(buf, sizeof(buf) - 1), kContents);
}
+// Tests that calling rename with file path ending with . or .. causes EBUSY.
+TEST(RenameTest, PathEndingWithDots) {
+ TempPath root_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ TempPath dir1 =
+ ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root_dir.path()));
+ TempPath dir2 =
+ ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDirIn(root_dir.path()));
+
+ // Try to move dir1 into dir2 but mess up the paths.
+ auto dir1Dot = JoinPath(dir1.path(), ".");
+ auto dir2Dot = JoinPath(dir2.path(), ".");
+ auto dir1DotDot = JoinPath(dir1.path(), "..");
+ auto dir2DotDot = JoinPath(dir2.path(), "..");
+ ASSERT_THAT(rename(dir1.path().c_str(), dir2Dot.c_str()),
+ SyscallFailsWithErrno(EBUSY));
+ ASSERT_THAT(rename(dir1.path().c_str(), dir2DotDot.c_str()),
+ SyscallFailsWithErrno(EBUSY));
+ ASSERT_THAT(rename(dir1Dot.c_str(), dir2.path().c_str()),
+ SyscallFailsWithErrno(EBUSY));
+ ASSERT_THAT(rename(dir1DotDot.c_str(), dir2.path().c_str()),
+ SyscallFailsWithErrno(EBUSY));
+}
+
+// Calling rename with file path ending with . or .. causes EBUSY in sysfs.
+TEST(RenameTest, SysfsPathEndingWithDots) {
+ // If a non-root user tries to rename inside /sys then we get EPERM.
+ SKIP_IF(geteuid() != 0);
+ ASSERT_THAT(rename("/sys/devices/system/cpu/online", "/sys/."),
+ SyscallFailsWithErrno(EBUSY));
+ ASSERT_THAT(rename("/sys/devices/system/cpu/online", "/sys/.."),
+ SyscallFailsWithErrno(EBUSY));
+}
+
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/sendfile.cc b/test/syscalls/linux/sendfile.cc
index 3924e0001..93b3a94f1 100644
--- a/test/syscalls/linux/sendfile.cc
+++ b/test/syscalls/linux/sendfile.cc
@@ -551,6 +551,29 @@ TEST(SendFileTest, SendPipeEOF) {
SyscallSucceedsWithValue(0));
}
+TEST(SendFileTest, SendToFullPipeReturnsEAGAIN) {
+ // Create and open an empty input file.
+ const TempPath in_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ const FileDescriptor in_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(in_file.path(), O_RDWR));
+
+ // Set up the output pipe.
+ int fds[2];
+ ASSERT_THAT(pipe2(fds, O_NONBLOCK), SyscallSucceeds());
+ const FileDescriptor rfd(fds[0]);
+ const FileDescriptor wfd(fds[1]);
+
+ int pipe_size = -1;
+ ASSERT_THAT(pipe_size = fcntl(wfd.get(), F_GETPIPE_SZ), SyscallSucceeds());
+ int data_size = pipe_size * 8;
+ ASSERT_THAT(ftruncate(in_fd.get(), data_size), SyscallSucceeds());
+
+ ASSERT_THAT(sendfile(wfd.get(), in_fd.get(), 0, data_size),
+ SyscallSucceeds());
+ EXPECT_THAT(sendfile(wfd.get(), in_fd.get(), 0, data_size),
+ SyscallFailsWithErrno(EAGAIN));
+}
+
TEST(SendFileTest, SendPipeBlocks) {
// Create temp file.
constexpr char kData[] =
diff --git a/test/syscalls/linux/socket_generic.cc b/test/syscalls/linux/socket_generic.cc
index de0b8bb11..f70047a09 100644
--- a/test/syscalls/linux/socket_generic.cc
+++ b/test/syscalls/linux/socket_generic.cc
@@ -379,7 +379,7 @@ TEST_P(AllSocketPairTest, RcvBufSucceeds) {
EXPECT_GT(size, 0);
}
-TEST_P(AllSocketPairTest, SndBufSucceeds) {
+TEST_P(AllSocketPairTest, GetSndBufSucceeds) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int size = 0;
socklen_t size_size = sizeof(size);
diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc
index a11147085..344a5a22c 100644
--- a/test/syscalls/linux/socket_inet_loopback.cc
+++ b/test/syscalls/linux/socket_inet_loopback.cc
@@ -526,7 +526,7 @@ void TestListenWhileConnect(const TestParam& param,
stopListen(listen_fd);
for (auto& client : clients) {
- const int kTimeout = 10000;
+ constexpr int kTimeout = 10000;
struct pollfd pfd = {
.fd = client.get(),
.events = POLLIN,
@@ -942,7 +942,7 @@ void setupTimeWaitClose(const TestAddress* listener,
// shutdown to trigger TIME_WAIT.
ASSERT_THAT(shutdown(active_closefd.get(), SHUT_WR), SyscallSucceeds());
{
- const int kTimeout = 10000;
+ constexpr int kTimeout = 10000;
struct pollfd pfd = {
.fd = passive_closefd.get(),
.events = POLLIN,
@@ -1186,7 +1186,7 @@ TEST_P(SocketInetLoopbackTest, TCPAcceptAfterReset) {
ASSERT_EQ(addrlen, listener.addr_len);
// Wait for accept_fd to process the RST.
- const int kTimeout = 10000;
+ constexpr int kTimeout = 10000;
struct pollfd pfd = {
.fd = accept_fd.get(),
.events = POLLIN,
diff --git a/test/syscalls/linux/socket_unix_dgram.cc b/test/syscalls/linux/socket_unix_dgram.cc
index af0df4fb4..5b0844493 100644
--- a/test/syscalls/linux/socket_unix_dgram.cc
+++ b/test/syscalls/linux/socket_unix_dgram.cc
@@ -18,6 +18,8 @@
#include <sys/un.h>
#include "gtest/gtest.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/test_util.h"
@@ -39,6 +41,39 @@ TEST_P(DgramUnixSocketPairTest, WriteOneSideClosed) {
SyscallFailsWithErrno(ECONNREFUSED));
}
+TEST_P(DgramUnixSocketPairTest, IncreasedSocketSendBufUnblocksWrites) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ int sock = sockets->first_fd();
+ int buf_size = 0;
+ socklen_t buf_size_len = sizeof(buf_size);
+ ASSERT_THAT(getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buf_size, &buf_size_len),
+ SyscallSucceeds());
+ int opts;
+ ASSERT_THAT(opts = fcntl(sock, F_GETFL), SyscallSucceeds());
+ opts |= O_NONBLOCK;
+ ASSERT_THAT(fcntl(sock, F_SETFL, opts), SyscallSucceeds());
+
+ std::vector<char> buf(buf_size / 4);
+ // Write till the socket buffer is full.
+ while (RetryEINTR(send)(sock, buf.data(), buf.size(), 0) != -1) {
+ // Sleep to give linux a chance to move data from the send buffer to the
+ // receive buffer.
+ absl::SleepFor(absl::Milliseconds(10)); // 10ms.
+ }
+ // The last error should have been EWOULDBLOCK.
+ ASSERT_EQ(errno, EWOULDBLOCK);
+
+ // Now increase the socket send buffer.
+ buf_size = buf_size * 2;
+ ASSERT_THAT(
+ setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buf_size, sizeof(buf_size)),
+ SyscallSucceeds());
+
+ // The send should succeed again.
+ ASSERT_THAT(RetryEINTR(send)(sock, buf.data(), buf.size(), 0),
+ SyscallSucceeds());
+}
+
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/socket_unix_seqpacket.cc b/test/syscalls/linux/socket_unix_seqpacket.cc
index 6d03df4d9..eb373373d 100644
--- a/test/syscalls/linux/socket_unix_seqpacket.cc
+++ b/test/syscalls/linux/socket_unix_seqpacket.cc
@@ -18,6 +18,8 @@
#include <sys/un.h>
#include "gtest/gtest.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/test_util.h"
@@ -61,6 +63,39 @@ TEST_P(SeqpacketUnixSocketPairTest, Sendto) {
SyscallSucceedsWithValue(3));
}
+TEST_P(SeqpacketUnixSocketPairTest, IncreasedSocketSendBufUnblocksWrites) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ int sock = sockets->first_fd();
+ int buf_size = 0;
+ socklen_t buf_size_len = sizeof(buf_size);
+ ASSERT_THAT(getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buf_size, &buf_size_len),
+ SyscallSucceeds());
+ int opts;
+ ASSERT_THAT(opts = fcntl(sock, F_GETFL), SyscallSucceeds());
+ opts |= O_NONBLOCK;
+ ASSERT_THAT(fcntl(sock, F_SETFL, opts), SyscallSucceeds());
+
+ std::vector<char> buf(buf_size / 4);
+ // Write till the socket buffer is full.
+ while (RetryEINTR(send)(sock, buf.data(), buf.size(), 0) != -1) {
+ // Sleep to give linux a chance to move data from the send buffer to the
+ // receive buffer.
+ absl::SleepFor(absl::Milliseconds(10)); // 10ms.
+ }
+ // The last error should have been EWOULDBLOCK.
+ ASSERT_EQ(errno, EWOULDBLOCK);
+
+ // Now increase the socket send buffer.
+ buf_size = buf_size * 2;
+ ASSERT_THAT(
+ setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buf_size, sizeof(buf_size)),
+ SyscallSucceeds());
+
+ // The send should succeed again.
+ ASSERT_THAT(RetryEINTR(send)(sock, buf.data(), buf.size(), 0),
+ SyscallSucceeds());
+}
+
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/socket_unix_stream.cc b/test/syscalls/linux/socket_unix_stream.cc
index ad9c4bf37..3ff810914 100644
--- a/test/syscalls/linux/socket_unix_stream.cc
+++ b/test/syscalls/linux/socket_unix_stream.cc
@@ -17,6 +17,8 @@
#include <sys/un.h>
#include "gtest/gtest.h"
+#include "absl/time/clock.h"
+#include "absl/time/time.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
#include "test/util/test_util.h"
@@ -134,6 +136,84 @@ TEST_P(StreamUnixSocketPairTest, GetSocketAcceptConn) {
EXPECT_EQ(got, 0);
}
+TEST_P(StreamUnixSocketPairTest, SetSocketSendBuf) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ auto s = sockets->first_fd();
+ int max = 0;
+ int min = 0;
+ {
+ // Discover maxmimum buffer size by setting to a really large value.
+ constexpr int kRcvBufSz = INT_MAX;
+ ASSERT_THAT(
+ setsockopt(s, SOL_SOCKET, SO_SNDBUF, &kRcvBufSz, sizeof(kRcvBufSz)),
+ SyscallSucceeds());
+
+ max = 0;
+ socklen_t max_len = sizeof(max);
+ ASSERT_THAT(getsockopt(s, SOL_SOCKET, SO_SNDBUF, &max, &max_len),
+ SyscallSucceeds());
+ }
+
+ {
+ // Discover minimum buffer size by setting it to zero.
+ constexpr int kRcvBufSz = 0;
+ ASSERT_THAT(
+ setsockopt(s, SOL_SOCKET, SO_SNDBUF, &kRcvBufSz, sizeof(kRcvBufSz)),
+ SyscallSucceeds());
+
+ socklen_t min_len = sizeof(min);
+ ASSERT_THAT(getsockopt(s, SOL_SOCKET, SO_SNDBUF, &min, &min_len),
+ SyscallSucceeds());
+ }
+
+ int quarter_sz = min + (max - min) / 4;
+ ASSERT_THAT(
+ setsockopt(s, SOL_SOCKET, SO_SNDBUF, &quarter_sz, sizeof(quarter_sz)),
+ SyscallSucceeds());
+
+ int val = 0;
+ socklen_t val_len = sizeof(val);
+ ASSERT_THAT(getsockopt(s, SOL_SOCKET, SO_SNDBUF, &val, &val_len),
+ SyscallSucceeds());
+
+ // Linux doubles the value set by SO_SNDBUF/SO_SNDBUF.
+ quarter_sz *= 2;
+ ASSERT_EQ(quarter_sz, val);
+}
+
+TEST_P(StreamUnixSocketPairTest, IncreasedSocketSendBufUnblocksWrites) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ int sock = sockets->first_fd();
+ int buf_size = 0;
+ socklen_t buf_size_len = sizeof(buf_size);
+ ASSERT_THAT(getsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buf_size, &buf_size_len),
+ SyscallSucceeds());
+ int opts;
+ ASSERT_THAT(opts = fcntl(sock, F_GETFL), SyscallSucceeds());
+ opts |= O_NONBLOCK;
+ ASSERT_THAT(fcntl(sock, F_SETFL, opts), SyscallSucceeds());
+
+ std::vector<char> buf(buf_size / 4);
+ // Write till the socket buffer is full.
+ while (RetryEINTR(send)(sock, buf.data(), buf.size(), 0) != -1) {
+ // Sleep to give linux a chance to move data from the send buffer to the
+ // receive buffer.
+ absl::SleepFor(absl::Milliseconds(10)); // 10ms.
+ }
+ // The last error should have been EWOULDBLOCK.
+ ASSERT_EQ(errno, EWOULDBLOCK);
+
+ // Now increase the socket send buffer.
+ buf_size = buf_size * 2;
+ ASSERT_THAT(
+ setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &buf_size, sizeof(buf_size)),
+ SyscallSucceeds());
+
+ // The send should succeed again.
+ ASSERT_THAT(RetryEINTR(send)(sock, buf.data(), buf.size(), 0),
+ SyscallSucceeds());
+}
+
INSTANTIATE_TEST_SUITE_P(
AllUnixDomainSockets, StreamUnixSocketPairTest,
::testing::ValuesIn(IncludeReversals(VecCat<SocketPairKind>(
diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc
index 650f12350..50f589708 100644
--- a/test/syscalls/linux/udp_socket.cc
+++ b/test/syscalls/linux/udp_socket.cc
@@ -2061,11 +2061,92 @@ TEST_P(UdpSocketTest, SendToZeroPort) {
SyscallSucceedsWithValue(sizeof(buf)));
}
+TEST_P(UdpSocketTest, ConnectToZeroPortUnbound) {
+ struct sockaddr_storage addr = InetLoopbackAddr();
+ SetPort(&addr, 0);
+ ASSERT_THAT(
+ connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen_),
+ SyscallSucceeds());
+}
+
+TEST_P(UdpSocketTest, ConnectToZeroPortBound) {
+ struct sockaddr_storage addr = InetLoopbackAddr();
+ ASSERT_NO_ERRNO(
+ BindSocket(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr)));
+
+ SetPort(&addr, 0);
+ ASSERT_THAT(
+ connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen_),
+ SyscallSucceeds());
+ socklen_t len = sizeof(sockaddr_storage);
+ ASSERT_THAT(
+ getsockname(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), &len),
+ SyscallSucceeds());
+ ASSERT_EQ(len, addrlen_);
+}
+
+TEST_P(UdpSocketTest, ConnectToZeroPortConnected) {
+ struct sockaddr_storage addr = InetLoopbackAddr();
+ ASSERT_NO_ERRNO(
+ BindSocket(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr)));
+
+ // Connect to an address with non-zero port should succeed.
+ ASSERT_THAT(
+ connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen_),
+ SyscallSucceeds());
+ sockaddr_storage peername;
+ socklen_t peerlen = sizeof(peername);
+ ASSERT_THAT(
+ getpeername(sock_.get(), reinterpret_cast<struct sockaddr*>(&peername),
+ &peerlen),
+ SyscallSucceeds());
+ ASSERT_EQ(peerlen, addrlen_);
+ ASSERT_EQ(memcmp(&peername, &addr, addrlen_), 0);
+
+ // However connect() to an address with port 0 will make the following
+ // getpeername() fail.
+ SetPort(&addr, 0);
+ ASSERT_THAT(
+ connect(sock_.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen_),
+ SyscallSucceeds());
+ ASSERT_THAT(
+ getpeername(sock_.get(), reinterpret_cast<struct sockaddr*>(&peername),
+ &peerlen),
+ SyscallFailsWithErrno(ENOTCONN));
+}
+
INSTANTIATE_TEST_SUITE_P(AllInetTests, UdpSocketTest,
::testing::Values(AddressFamily::kIpv4,
AddressFamily::kIpv6,
AddressFamily::kDualStack));
+TEST(UdpInet6SocketTest, ConnectInet4Sockaddr) {
+ // glibc getaddrinfo expects the invariant expressed by this test to be held.
+ const sockaddr_in connect_sockaddr = {
+ .sin_family = AF_INET, .sin_addr = {.s_addr = htonl(INADDR_LOOPBACK)}};
+ auto sock_ =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP));
+ ASSERT_THAT(
+ connect(sock_.get(),
+ reinterpret_cast<const struct sockaddr*>(&connect_sockaddr),
+ sizeof(sockaddr_in)),
+ SyscallSucceeds());
+ socklen_t len;
+ sockaddr_storage sockname;
+ ASSERT_THAT(getsockname(sock_.get(),
+ reinterpret_cast<struct sockaddr*>(&sockname), &len),
+ SyscallSucceeds());
+ ASSERT_EQ(sockname.ss_family, AF_INET6);
+ ASSERT_EQ(len, sizeof(sockaddr_in6));
+ auto sin6 = reinterpret_cast<struct sockaddr_in6*>(&sockname);
+ char addr_buf[INET6_ADDRSTRLEN];
+ const char* addr;
+ ASSERT_NE(addr = inet_ntop(sockname.ss_family, &sockname, addr_buf,
+ sizeof(addr_buf)),
+ nullptr);
+ ASSERT_TRUE(IN6_IS_ADDR_V4MAPPED(sin6->sin6_addr.s6_addr)) << addr;
+}
+
} // namespace
} // namespace testing