summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls')
-rw-r--r--test/syscalls/BUILD7
-rw-r--r--test/syscalls/linux/BUILD40
-rw-r--r--test/syscalls/linux/getdents.cc3
-rw-r--r--test/syscalls/linux/mount.cc36
-rw-r--r--test/syscalls/linux/open.cc12
-rw-r--r--test/syscalls/linux/pipe.cc10
-rw-r--r--test/syscalls/linux/proc.cc27
-rw-r--r--test/syscalls/linux/proc_net.cc8
-rw-r--r--test/syscalls/linux/proc_net_unix.cc2
-rw-r--r--test/syscalls/linux/proc_pid_uid_gid_map.cc9
-rw-r--r--test/syscalls/linux/raw_socket.cc45
-rw-r--r--test/syscalls/linux/semaphore.cc164
-rw-r--r--test/syscalls/linux/socket.cc6
-rw-r--r--test/syscalls/linux/socket_bind_to_device_distribution.cc8
-rw-r--r--test/syscalls/linux/socket_generic.cc16
-rw-r--r--test/syscalls/linux/socket_ip_udp_generic.cc57
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound.cc84
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc4
-rw-r--r--test/syscalls/linux/socket_ipv6_udp_unbound.cc131
-rw-r--r--test/syscalls/linux/socket_ipv6_udp_unbound.h29
-rw-r--r--test/syscalls/linux/socket_ipv6_udp_unbound_loopback.cc32
-rw-r--r--test/syscalls/linux/socket_unix_unbound_filesystem.cc16
-rw-r--r--test/syscalls/linux/tuntap.cc10
-rw-r--r--test/syscalls/linux/udp_socket.cc90
24 files changed, 771 insertions, 75 deletions
diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD
index 135d58ae6..a5b9233f7 100644
--- a/test/syscalls/BUILD
+++ b/test/syscalls/BUILD
@@ -647,6 +647,7 @@ syscall_test(
syscall_test(
size = "medium",
+ add_hostinet = True,
test = "//test/syscalls/linux:socket_ip_tcp_loopback_non_blocking_test",
)
@@ -658,12 +659,14 @@ syscall_test(
syscall_test(
size = "medium",
+ add_hostinet = True,
shard_count = most_shards,
test = "//test/syscalls/linux:socket_ip_tcp_udp_generic_loopback_test",
)
syscall_test(
size = "medium",
+ add_hostinet = True,
test = "//test/syscalls/linux:socket_ip_udp_loopback_non_blocking_test",
)
@@ -680,6 +683,7 @@ syscall_test(
syscall_test(
size = "medium",
+ add_hostinet = True,
shard_count = more_shards,
# Takes too long under gotsan to run.
tags = ["nogotsan"],
@@ -728,6 +732,7 @@ syscall_test(
)
syscall_test(
+ add_hostinet = True,
test = "//test/syscalls/linux:socket_non_stream_blocking_local_test",
)
@@ -903,6 +908,7 @@ syscall_test(
)
syscall_test(
+ add_hostinet = True,
test = "//test/syscalls/linux:udp_bind_test",
)
@@ -967,6 +973,7 @@ syscall_test(
)
syscall_test(
+ add_hostinet = True,
test = "//test/syscalls/linux:proc_net_tcp_test",
)
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD
index a9e0b070a..4e0c8a574 100644
--- a/test/syscalls/linux/BUILD
+++ b/test/syscalls/linux/BUILD
@@ -21,6 +21,7 @@ exports_files(
"socket_ip_unbound.cc",
"socket_ipv4_udp_unbound_external_networking_test.cc",
"socket_ipv4_udp_unbound_loopback.cc",
+ "socket_ipv6_udp_unbound_loopback.cc",
"socket_ipv4_udp_unbound_loopback_nogotsan.cc",
"tcp_socket.cc",
"udp_bind.cc",
@@ -944,6 +945,7 @@ cc_binary(
"//test/util:eventfd_util",
"//test/util:file_descriptor",
"//test/util:fs_util",
+ "@com_google_absl//absl/container:node_hash_map",
"@com_google_absl//absl/container:node_hash_set",
"@com_google_absl//absl/strings",
gtest,
@@ -2451,6 +2453,27 @@ cc_library(
)
cc_library(
+ name = "socket_ipv6_udp_unbound_test_cases",
+ testonly = 1,
+ srcs = [
+ "socket_ipv6_udp_unbound.cc",
+ ],
+ hdrs = [
+ "socket_ipv6_udp_unbound.h",
+ ],
+ deps = [
+ ":ip_socket_test_util",
+ ":socket_test_util",
+ "@com_google_absl//absl/memory",
+ gtest,
+ "//test/util:posix_error",
+ "//test/util:save_util",
+ "//test/util:test_util",
+ ],
+ alwayslink = 1,
+)
+
+cc_library(
name = "socket_ipv4_udp_unbound_netlink_test_cases",
testonly = 1,
srcs = [
@@ -2790,6 +2813,22 @@ cc_binary(
)
cc_binary(
+ name = "socket_ipv6_udp_unbound_loopback_test",
+ testonly = 1,
+ srcs = [
+ "socket_ipv6_udp_unbound_loopback.cc",
+ ],
+ linkstatic = 1,
+ deps = [
+ ":ip_socket_test_util",
+ ":socket_ipv6_udp_unbound_test_cases",
+ ":socket_test_util",
+ "//test/util:test_main",
+ "//test/util:test_util",
+ ],
+)
+
+cc_binary(
name = "socket_ipv4_udp_unbound_loopback_nogotsan_test",
testonly = 1,
srcs = [
@@ -3286,6 +3325,7 @@ cc_binary(
":socket_test_util",
":unix_domain_socket_test_util",
gtest,
+ "//test/util:file_descriptor",
"//test/util:test_main",
"//test/util:test_util",
],
diff --git a/test/syscalls/linux/getdents.cc b/test/syscalls/linux/getdents.cc
index b040cdcf7..93c692dd6 100644
--- a/test/syscalls/linux/getdents.cc
+++ b/test/syscalls/linux/getdents.cc
@@ -32,6 +32,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/container/node_hash_map.h"
#include "absl/container/node_hash_set.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
@@ -381,7 +382,7 @@ TYPED_TEST(GetdentsTest, PartialBuffer) {
// getdents iterates correctly despite mutation of /proc/self/fd.
TYPED_TEST(GetdentsTest, ProcSelfFd) {
constexpr size_t kNfds = 10;
- std::unordered_map<int, FileDescriptor> fds;
+ absl::node_hash_map<int, FileDescriptor> fds;
fds.reserve(kNfds);
for (size_t i = 0; i < kNfds; i++) {
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD());
diff --git a/test/syscalls/linux/mount.cc b/test/syscalls/linux/mount.cc
index d65b7d031..15b645fb7 100644
--- a/test/syscalls/linux/mount.cc
+++ b/test/syscalls/linux/mount.cc
@@ -345,42 +345,6 @@ TEST(MountTest, RenameRemoveMountPoint) {
ASSERT_THAT(rmdir(dir.path().c_str()), SyscallFailsWithErrno(EBUSY));
}
-TEST(MountTest, MountFuseFilesystemNoDevice) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
- SKIP_IF(IsRunningOnGvisor() && !IsFUSEEnabled());
-
- auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
-
- // Before kernel version 4.16-rc6, FUSE mount is protected by
- // capable(CAP_SYS_ADMIN). After this version, it uses
- // ns_capable(CAP_SYS_ADMIN) to protect. Before the 4.16 kernel, it was not
- // allowed to mount fuse file systems without the global CAP_SYS_ADMIN.
- int res = mount("", dir.path().c_str(), "fuse", 0, "");
- SKIP_IF(!IsRunningOnGvisor() && res == -1 && errno == EPERM);
-
- EXPECT_THAT(mount("", dir.path().c_str(), "fuse", 0, ""),
- SyscallFailsWithErrno(EINVAL));
-}
-
-TEST(MountTest, MountFuseFilesystem) {
- SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN)));
- SKIP_IF(IsRunningOnGvisor() && !IsFUSEEnabled());
-
- const FileDescriptor fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open("/dev/fuse", O_WRONLY));
- std::string mopts = "fd=" + std::to_string(fd.get());
-
- auto const dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
-
- // See comments in MountFuseFilesystemNoDevice for the reason why we skip
- // EPERM when running on Linux.
- int res = mount("", dir.path().c_str(), "fuse", 0, "");
- SKIP_IF(!IsRunningOnGvisor() && res == -1 && errno == EPERM);
-
- auto const mount =
- ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir.path(), "fuse", 0, mopts, 0));
-}
-
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/open.cc b/test/syscalls/linux/open.cc
index 77f390f3c..fcd162ca2 100644
--- a/test/syscalls/linux/open.cc
+++ b/test/syscalls/linux/open.cc
@@ -505,6 +505,18 @@ TEST_F(OpenTest, OpenNonDirectoryWithTrailingSlash) {
EXPECT_THAT(open(bad_path.c_str(), O_RDONLY), SyscallFailsWithErrno(ENOTDIR));
}
+TEST_F(OpenTest, OpenWithStrangeFlags) {
+ // VFS1 incorrectly allows read/write operations on such file descriptors.
+ SKIP_IF(IsRunningWithVFS1());
+
+ const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ const FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY | O_RDWR));
+ EXPECT_THAT(write(fd.get(), "x", 1), SyscallFailsWithErrno(EBADF));
+ char c;
+ EXPECT_THAT(read(fd.get(), &c, 1), SyscallFailsWithErrno(EBADF));
+}
+
} // namespace
} // namespace testing
diff --git a/test/syscalls/linux/pipe.cc b/test/syscalls/linux/pipe.cc
index 06d9dbf65..01ccbdcd2 100644
--- a/test/syscalls/linux/pipe.cc
+++ b/test/syscalls/linux/pipe.cc
@@ -71,13 +71,13 @@ class PipeTest : public ::testing::TestWithParam<PipeCreator> {
// Returns true iff the pipe represents a named pipe.
bool IsNamedPipe() const { return named_pipe_; }
- int Size() const {
+ size_t Size() const {
int s1 = fcntl(rfd_.get(), F_GETPIPE_SZ);
int s2 = fcntl(wfd_.get(), F_GETPIPE_SZ);
EXPECT_GT(s1, 0);
EXPECT_GT(s2, 0);
EXPECT_EQ(s1, s2);
- return s1;
+ return static_cast<size_t>(s1);
}
static void TearDownTestSuite() {
@@ -568,7 +568,7 @@ TEST_P(PipeTest, Streaming) {
DisableSave ds;
// Size() requires 2 syscalls, call it once and remember the value.
- const int pipe_size = Size();
+ const size_t pipe_size = Size();
const size_t streamed_bytes = 4 * pipe_size;
absl::Notification notify;
@@ -576,7 +576,7 @@ TEST_P(PipeTest, Streaming) {
std::vector<char> buf(1024);
// Don't start until it's full.
notify.WaitForNotification();
- ssize_t total = 0;
+ size_t total = 0;
while (total < streamed_bytes) {
ASSERT_THAT(read(rfd_.get(), buf.data(), buf.size()),
SyscallSucceedsWithValue(buf.size()));
@@ -593,7 +593,7 @@ TEST_P(PipeTest, Streaming) {
// page) for the check for notify.Notify() below to be correct.
std::vector<char> buf(1024);
RandomizeBuffer(buf.data(), buf.size());
- ssize_t total = 0;
+ size_t total = 0;
while (total < streamed_bytes) {
ASSERT_THAT(write(wfd_.get(), buf.data(), buf.size()),
SyscallSucceedsWithValue(buf.size()));
diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc
index 575be014c..e508ce27f 100644
--- a/test/syscalls/linux/proc.cc
+++ b/test/syscalls/linux/proc.cc
@@ -1802,6 +1802,33 @@ TEST(ProcPidCmdline, SubprocessForkSameCmdline) {
}
}
+TEST(ProcPidCmdline, SubprocessSeekCmdline) {
+ FileDescriptor fd;
+ ASSERT_NO_ERRNO(WithSubprocess(
+ [&](int pid) -> PosixError {
+ // Running. Open /proc/pid/cmdline.
+ ASSIGN_OR_RETURN_ERRNO(
+ fd, Open(absl::StrCat("/proc/", pid, "/cmdline"), O_RDONLY));
+ return NoError();
+ },
+ [&](int pid) -> PosixError {
+ // Zombie, but seek should still succeed.
+ int ret = lseek(fd.get(), 0x801, 0);
+ if (ret < 0) {
+ return PosixError(errno);
+ }
+ return NoError();
+ },
+ [&](int pid) -> PosixError {
+ // Exited.
+ int ret = lseek(fd.get(), 0x801, 0);
+ if (ret < 0) {
+ return PosixError(errno);
+ }
+ return NoError();
+ }));
+}
+
// Test whether /proc/PID/ symlinks can be read for a running process.
TEST(ProcPidSymlink, SubprocessRunning) {
char buf[1];
diff --git a/test/syscalls/linux/proc_net.cc b/test/syscalls/linux/proc_net.cc
index 23677e296..1cc700fe7 100644
--- a/test/syscalls/linux/proc_net.cc
+++ b/test/syscalls/linux/proc_net.cc
@@ -420,14 +420,14 @@ TEST(ProcNetSnmp, CheckNetStat) {
int name_count = 0;
int value_count = 0;
std::vector<absl::string_view> lines = absl::StrSplit(contents, '\n');
- for (int i = 0; i + 1 < lines.size(); i += 2) {
+ for (long unsigned int i = 0; i + 1 < lines.size(); i += 2) {
std::vector<absl::string_view> names =
absl::StrSplit(lines[i], absl::ByAnyChar("\t "));
std::vector<absl::string_view> values =
absl::StrSplit(lines[i + 1], absl::ByAnyChar("\t "));
EXPECT_EQ(names.size(), values.size()) << " mismatch in lines '" << lines[i]
<< "' and '" << lines[i + 1] << "'";
- for (int j = 0; j < names.size() && j < values.size(); ++j) {
+ for (long unsigned int j = 0; j < names.size() && j < values.size(); ++j) {
if (names[j] == "TCPOrigDataSent" || names[j] == "TCPSynRetrans" ||
names[j] == "TCPDSACKRecv" || names[j] == "TCPDSACKOfoRecv") {
++name_count;
@@ -457,14 +457,14 @@ TEST(ProcNetSnmp, CheckSnmp) {
int name_count = 0;
int value_count = 0;
std::vector<absl::string_view> lines = absl::StrSplit(contents, '\n');
- for (int i = 0; i + 1 < lines.size(); i += 2) {
+ for (long unsigned int i = 0; i + 1 < lines.size(); i += 2) {
std::vector<absl::string_view> names =
absl::StrSplit(lines[i], absl::ByAnyChar("\t "));
std::vector<absl::string_view> values =
absl::StrSplit(lines[i + 1], absl::ByAnyChar("\t "));
EXPECT_EQ(names.size(), values.size()) << " mismatch in lines '" << lines[i]
<< "' and '" << lines[i + 1] << "'";
- for (int j = 0; j < names.size() && j < values.size(); ++j) {
+ for (long unsigned int j = 0; j < names.size() && j < values.size(); ++j) {
if (names[j] == "RetransSegs") {
++name_count;
int64_t val;
diff --git a/test/syscalls/linux/proc_net_unix.cc b/test/syscalls/linux/proc_net_unix.cc
index a63067586..662c6feb2 100644
--- a/test/syscalls/linux/proc_net_unix.cc
+++ b/test/syscalls/linux/proc_net_unix.cc
@@ -181,7 +181,7 @@ PosixErrorOr<std::vector<UnixEntry>> ProcNetUnixEntries() {
// Returns true on match, and sets 'match' to point to the matching entry.
bool FindBy(std::vector<UnixEntry> entries, UnixEntry* match,
std::function<bool(const UnixEntry&)> predicate) {
- for (int i = 0; i < entries.size(); ++i) {
+ for (long unsigned int i = 0; i < entries.size(); ++i) {
if (predicate(entries[i])) {
*match = entries[i];
return true;
diff --git a/test/syscalls/linux/proc_pid_uid_gid_map.cc b/test/syscalls/linux/proc_pid_uid_gid_map.cc
index 748f7be58..af052a63c 100644
--- a/test/syscalls/linux/proc_pid_uid_gid_map.cc
+++ b/test/syscalls/linux/proc_pid_uid_gid_map.cc
@@ -203,7 +203,8 @@ TEST_P(ProcSelfUidGidMapTest, IdentityMapOwnID) {
EXPECT_THAT(
InNewUserNamespaceWithMapFD([&](int fd) {
DenySelfSetgroups();
- TEST_PCHECK(write(fd, line.c_str(), line.size()) == line.size());
+ TEST_PCHECK(static_cast<long unsigned int>(
+ write(fd, line.c_str(), line.size())) == line.size());
}),
IsPosixErrorOkAndHolds(0));
}
@@ -220,7 +221,8 @@ TEST_P(ProcSelfUidGidMapTest, TrailingNewlineAndNULIgnored) {
DenySelfSetgroups();
// The write should return the full size of the write, even though
// characters after the NUL were ignored.
- TEST_PCHECK(write(fd, line.c_str(), line.size()) == line.size());
+ TEST_PCHECK(static_cast<long unsigned int>(
+ write(fd, line.c_str(), line.size())) == line.size());
}),
IsPosixErrorOkAndHolds(0));
}
@@ -233,7 +235,8 @@ TEST_P(ProcSelfUidGidMapTest, NonIdentityMapOwnID) {
EXPECT_THAT(
InNewUserNamespaceWithMapFD([&](int fd) {
DenySelfSetgroups();
- TEST_PCHECK(write(fd, line.c_str(), line.size()) == line.size());
+ TEST_PCHECK(static_cast<long unsigned int>(
+ write(fd, line.c_str(), line.size())) == line.size());
}),
IsPosixErrorOkAndHolds(0));
}
diff --git a/test/syscalls/linux/raw_socket.cc b/test/syscalls/linux/raw_socket.cc
index 54709371c..955bcee4b 100644
--- a/test/syscalls/linux/raw_socket.cc
+++ b/test/syscalls/linux/raw_socket.cc
@@ -852,6 +852,51 @@ TEST(RawSocketTest, IPv6ProtoRaw) {
SyscallFailsWithErrno(EINVAL));
}
+TEST(RawSocketTest, IPv6SendMsg) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ int sock;
+ ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_TCP),
+ SyscallSucceeds());
+
+ char kBuf[] = "hello";
+ struct iovec iov = {};
+ iov.iov_base = static_cast<void*>(const_cast<char*>(kBuf));
+ iov.iov_len = static_cast<size_t>(sizeof(kBuf));
+
+ struct sockaddr_storage addr = {};
+ struct sockaddr_in* sin = reinterpret_cast<struct sockaddr_in*>(&addr);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ struct msghdr msg = {};
+ msg.msg_name = static_cast<void*>(&addr);
+ msg.msg_namelen = sizeof(sockaddr_in);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ ASSERT_THAT(sendmsg(sock, &msg, 0), SyscallFailsWithErrno(EINVAL));
+}
+
+TEST_P(RawSocketTest, ConnectOnIPv6Socket) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ int sock;
+ ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_TCP),
+ SyscallSucceeds());
+
+ struct sockaddr_storage addr = {};
+ struct sockaddr_in* sin = reinterpret_cast<struct sockaddr_in*>(&addr);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ ASSERT_THAT(connect(sock, reinterpret_cast<struct sockaddr*>(&addr),
+ sizeof(sockaddr_in6)),
+ SyscallFailsWithErrno(EAFNOSUPPORT));
+}
+
INSTANTIATE_TEST_SUITE_P(
AllInetTests, RawSocketTest,
::testing::Combine(::testing::Values(IPPROTO_TCP, IPPROTO_UDP),
diff --git a/test/syscalls/linux/semaphore.cc b/test/syscalls/linux/semaphore.cc
index 1c1bf6a57..0530fce44 100644
--- a/test/syscalls/linux/semaphore.cc
+++ b/test/syscalls/linux/semaphore.cc
@@ -20,6 +20,7 @@
#include <atomic>
#include <cerrno>
#include <ctime>
+#include <set>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@@ -35,6 +36,17 @@ namespace gvisor {
namespace testing {
namespace {
+constexpr int kSemMap = 1024000000;
+constexpr int kSemMni = 32000;
+constexpr int kSemMns = 1024000000;
+constexpr int kSemMnu = 1024000000;
+constexpr int kSemMsl = 32000;
+constexpr int kSemOpm = 500;
+constexpr int kSemUme = 500;
+constexpr int kSemUsz = 20;
+constexpr int kSemVmx = 32767;
+constexpr int kSemAem = 32767;
+
class AutoSem {
public:
explicit AutoSem(int id) : id_(id) {}
@@ -586,7 +598,7 @@ TEST(SemaphoreTest, SemopGetzcnt) {
buf.sem_num = 0;
buf.sem_op = 0;
constexpr size_t kLoops = 10;
- for (auto i = 0; i < kLoops; i++) {
+ for (size_t i = 0; i < kLoops; i++) {
auto child_pid = fork();
if (child_pid == 0) {
TEST_PCHECK(RetryEINTR(semop)(sem.get(), &buf, 1) == 0);
@@ -693,7 +705,7 @@ TEST(SemaphoreTest, SemopGetncnt) {
buf.sem_num = 0;
buf.sem_op = -1;
constexpr size_t kLoops = 10;
- for (auto i = 0; i < kLoops; i++) {
+ for (size_t i = 0; i < kLoops; i++) {
auto child_pid = fork();
if (child_pid == 0) {
TEST_PCHECK(RetryEINTR(semop)(sem.get(), &buf, 1) == 0);
@@ -774,18 +786,148 @@ TEST(SemaphoreTest, SemopGetncntOnSignal_NoRandomSave) {
}
TEST(SemaphoreTest, IpcInfo) {
+ constexpr int kLoops = 5;
+ std::set<int> sem_ids;
struct seminfo info;
+ // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions.
+ ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false));
+ for (int i = 0; i < kLoops; i++) {
+ AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT));
+ ASSERT_THAT(sem.get(), SyscallSucceeds());
+ sem_ids.insert(sem.release());
+ }
+ ASSERT_EQ(sem_ids.size(), kLoops);
+
+ int max_used_index = 0;
+ EXPECT_THAT(max_used_index = semctl(0, 0, IPC_INFO, &info),
+ SyscallSucceeds());
+
+ std::set<int> sem_ids_before_max_index;
+ for (int i = 0; i <= max_used_index; i++) {
+ struct semid_ds ds = {};
+ int sem_id = semctl(i, 0, SEM_STAT, &ds);
+ // Only if index i is used within the registry.
+ if (sem_ids.find(sem_id) != sem_ids.end()) {
+ struct semid_ds ipc_stat_ds;
+ ASSERT_THAT(semctl(sem_id, 0, IPC_STAT, &ipc_stat_ds), SyscallSucceeds());
+ EXPECT_EQ(ds.sem_perm.__key, ipc_stat_ds.sem_perm.__key);
+ EXPECT_EQ(ds.sem_perm.uid, ipc_stat_ds.sem_perm.uid);
+ EXPECT_EQ(ds.sem_perm.gid, ipc_stat_ds.sem_perm.gid);
+ EXPECT_EQ(ds.sem_perm.cuid, ipc_stat_ds.sem_perm.cuid);
+ EXPECT_EQ(ds.sem_perm.cgid, ipc_stat_ds.sem_perm.cgid);
+ EXPECT_EQ(ds.sem_perm.mode, ipc_stat_ds.sem_perm.mode);
+ EXPECT_EQ(ds.sem_otime, ipc_stat_ds.sem_otime);
+ EXPECT_EQ(ds.sem_ctime, ipc_stat_ds.sem_ctime);
+ EXPECT_EQ(ds.sem_nsems, ipc_stat_ds.sem_nsems);
+
+ // Remove the semaphore set's read permission.
+ struct semid_ds ipc_set_ds;
+ ipc_set_ds.sem_perm.uid = getuid();
+ ipc_set_ds.sem_perm.gid = getgid();
+ // Keep the semaphore set's write permission so that it could be removed.
+ ipc_set_ds.sem_perm.mode = 0200;
+ ASSERT_THAT(semctl(sem_id, 0, IPC_SET, &ipc_set_ds), SyscallSucceeds());
+ ASSERT_THAT(semctl(i, 0, SEM_STAT, &ds), SyscallFailsWithErrno(EACCES));
+
+ sem_ids_before_max_index.insert(sem_id);
+ }
+ }
+ EXPECT_EQ(sem_ids_before_max_index.size(), kLoops);
+ for (const int sem_id : sem_ids) {
+ ASSERT_THAT(semctl(sem_id, 0, IPC_RMID), SyscallSucceeds());
+ }
+
ASSERT_THAT(semctl(0, 0, IPC_INFO, &info), SyscallSucceeds());
+ EXPECT_EQ(info.semmap, kSemMap);
+ EXPECT_EQ(info.semmni, kSemMni);
+ EXPECT_EQ(info.semmns, kSemMns);
+ EXPECT_EQ(info.semmnu, kSemMnu);
+ EXPECT_EQ(info.semmsl, kSemMsl);
+ EXPECT_EQ(info.semopm, kSemOpm);
+ EXPECT_EQ(info.semume, kSemUme);
+ EXPECT_EQ(info.semusz, kSemUsz);
+ EXPECT_EQ(info.semvmx, kSemVmx);
+ EXPECT_EQ(info.semaem, kSemAem);
+}
+
+TEST(SemaphoreTest, SemInfo) {
+ constexpr int kLoops = 5;
+ constexpr int kSemSetSize = 3;
+ std::set<int> sem_ids;
+ struct seminfo info;
+ // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions.
+ ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false));
+ for (int i = 0; i < kLoops; i++) {
+ AutoSem sem(semget(IPC_PRIVATE, kSemSetSize, 0600 | IPC_CREAT));
+ ASSERT_THAT(sem.get(), SyscallSucceeds());
+ sem_ids.insert(sem.release());
+ }
+ ASSERT_EQ(sem_ids.size(), kLoops);
+ int max_used_index = 0;
+ EXPECT_THAT(max_used_index = semctl(0, 0, SEM_INFO, &info),
+ SyscallSucceeds());
+ EXPECT_EQ(info.semmap, kSemMap);
+ EXPECT_EQ(info.semmni, kSemMni);
+ EXPECT_EQ(info.semmns, kSemMns);
+ EXPECT_EQ(info.semmnu, kSemMnu);
+ EXPECT_EQ(info.semmsl, kSemMsl);
+ EXPECT_EQ(info.semopm, kSemOpm);
+ EXPECT_EQ(info.semume, kSemUme);
+ // There could be semaphores existing in the system during the test, which
+ // prevents the test from getting a exact number, but the test could expect at
+ // least the number of sempahroes it creates in the begining of the test.
+ EXPECT_GE(info.semusz, sem_ids.size());
+ EXPECT_EQ(info.semvmx, kSemVmx);
+ EXPECT_GE(info.semaem, sem_ids.size() * kSemSetSize);
+
+ std::set<int> sem_ids_before_max_index;
+ for (int i = 0; i <= max_used_index; i++) {
+ struct semid_ds ds = {};
+ int sem_id = semctl(i, 0, SEM_STAT, &ds);
+ // Only if index i is used within the registry.
+ if (sem_ids.find(sem_id) != sem_ids.end()) {
+ struct semid_ds ipc_stat_ds;
+ ASSERT_THAT(semctl(sem_id, 0, IPC_STAT, &ipc_stat_ds), SyscallSucceeds());
+ EXPECT_EQ(ds.sem_perm.__key, ipc_stat_ds.sem_perm.__key);
+ EXPECT_EQ(ds.sem_perm.uid, ipc_stat_ds.sem_perm.uid);
+ EXPECT_EQ(ds.sem_perm.gid, ipc_stat_ds.sem_perm.gid);
+ EXPECT_EQ(ds.sem_perm.cuid, ipc_stat_ds.sem_perm.cuid);
+ EXPECT_EQ(ds.sem_perm.cgid, ipc_stat_ds.sem_perm.cgid);
+ EXPECT_EQ(ds.sem_perm.mode, ipc_stat_ds.sem_perm.mode);
+ EXPECT_EQ(ds.sem_otime, ipc_stat_ds.sem_otime);
+ EXPECT_EQ(ds.sem_ctime, ipc_stat_ds.sem_ctime);
+ EXPECT_EQ(ds.sem_nsems, ipc_stat_ds.sem_nsems);
+
+ // Remove the semaphore set's read permission.
+ struct semid_ds ipc_set_ds;
+ ipc_set_ds.sem_perm.uid = getuid();
+ ipc_set_ds.sem_perm.gid = getgid();
+ // Keep the semaphore set's write permission so that it could be removed.
+ ipc_set_ds.sem_perm.mode = 0200;
+ ASSERT_THAT(semctl(sem_id, 0, IPC_SET, &ipc_set_ds), SyscallSucceeds());
+ ASSERT_THAT(semctl(i, 0, SEM_STAT, &ds), SyscallFailsWithErrno(EACCES));
+
+ sem_ids_before_max_index.insert(sem_id);
+ }
+ }
+ EXPECT_EQ(sem_ids_before_max_index.size(), kLoops);
+ for (const int sem_id : sem_ids) {
+ ASSERT_THAT(semctl(sem_id, 0, IPC_RMID), SyscallSucceeds());
+ }
- EXPECT_EQ(info.semmap, 1024000000);
- EXPECT_EQ(info.semmni, 32000);
- EXPECT_EQ(info.semmns, 1024000000);
- EXPECT_EQ(info.semmnu, 1024000000);
- EXPECT_EQ(info.semmsl, 32000);
- EXPECT_EQ(info.semopm, 500);
- EXPECT_EQ(info.semume, 500);
- EXPECT_EQ(info.semvmx, 32767);
- EXPECT_EQ(info.semaem, 32767);
+ ASSERT_THAT(semctl(0, 0, SEM_INFO, &info), SyscallSucceeds());
+ EXPECT_EQ(info.semmap, kSemMap);
+ EXPECT_EQ(info.semmni, kSemMni);
+ EXPECT_EQ(info.semmns, kSemMns);
+ EXPECT_EQ(info.semmnu, kSemMnu);
+ EXPECT_EQ(info.semmsl, kSemMsl);
+ EXPECT_EQ(info.semopm, kSemOpm);
+ EXPECT_EQ(info.semume, kSemUme);
+ // Apart from semapahores that are not created by the test, we can't determine
+ // the exact number of semaphore sets and semaphores, as a result, semusz and
+ // semaem range from 0 to a random number. Since the numbers are always
+ // non-negative, the test will not check the reslts of semusz and semaem.
+ EXPECT_EQ(info.semvmx, kSemVmx);
}
} // namespace
diff --git a/test/syscalls/linux/socket.cc b/test/syscalls/linux/socket.cc
index e680d3dd7..32f583581 100644
--- a/test/syscalls/linux/socket.cc
+++ b/test/syscalls/linux/socket.cc
@@ -46,7 +46,7 @@ TEST(SocketTest, ProtocolUnix) {
{AF_UNIX, SOCK_SEQPACKET, PF_UNIX},
{AF_UNIX, SOCK_DGRAM, PF_UNIX},
};
- for (int i = 0; i < ABSL_ARRAYSIZE(tests); i++) {
+ for (long unsigned int i = 0; i < ABSL_ARRAYSIZE(tests); i++) {
ASSERT_NO_ERRNO_AND_VALUE(
Socket(tests[i].domain, tests[i].type, tests[i].protocol));
}
@@ -59,7 +59,7 @@ TEST(SocketTest, ProtocolInet) {
{AF_INET, SOCK_DGRAM, IPPROTO_UDP},
{AF_INET, SOCK_STREAM, IPPROTO_TCP},
};
- for (int i = 0; i < ABSL_ARRAYSIZE(tests); i++) {
+ for (long unsigned int i = 0; i < ABSL_ARRAYSIZE(tests); i++) {
ASSERT_NO_ERRNO_AND_VALUE(
Socket(tests[i].domain, tests[i].type, tests[i].protocol));
}
@@ -87,7 +87,7 @@ TEST(SocketTest, UnixSocketStat) {
ASSERT_THAT(stat(addr.sun_path, &statbuf), SyscallSucceeds());
// Mode should be S_IFSOCK.
- EXPECT_EQ(statbuf.st_mode, S_IFSOCK | sock_perm & ~mask);
+ EXPECT_EQ(statbuf.st_mode, S_IFSOCK | (sock_perm & ~mask));
// Timestamps should be equal and non-zero.
// TODO(b/158882152): Sockets currently don't implement timestamps.
diff --git a/test/syscalls/linux/socket_bind_to_device_distribution.cc b/test/syscalls/linux/socket_bind_to_device_distribution.cc
index 5ed57625c..06419772f 100644
--- a/test/syscalls/linux/socket_bind_to_device_distribution.cc
+++ b/test/syscalls/linux/socket_bind_to_device_distribution.cc
@@ -168,7 +168,7 @@ TEST_P(BindToDeviceDistributionTest, Tcp) {
std::vector<std::unique_ptr<ScopedThread>> listen_threads(
listener_fds.size());
- for (int i = 0; i < listener_fds.size(); i++) {
+ for (long unsigned int i = 0; i < listener_fds.size(); i++) {
listen_threads[i] = absl::make_unique<ScopedThread>(
[&listener_fds, &accept_counts, &connects_received, i,
kConnectAttempts]() {
@@ -235,7 +235,7 @@ TEST_P(BindToDeviceDistributionTest, Tcp) {
listen_thread->Join();
}
// Check that connections are distributed correctly among listening sockets.
- for (int i = 0; i < accept_counts.size(); i++) {
+ for (long unsigned int i = 0; i < accept_counts.size(); i++) {
EXPECT_THAT(
accept_counts[i],
EquivalentWithin(static_cast<int>(kConnectAttempts *
@@ -308,7 +308,7 @@ TEST_P(BindToDeviceDistributionTest, Udp) {
std::vector<std::unique_ptr<ScopedThread>> receiver_threads(
listener_fds.size());
- for (int i = 0; i < listener_fds.size(); i++) {
+ for (long unsigned int i = 0; i < listener_fds.size(); i++) {
receiver_threads[i] = absl::make_unique<ScopedThread>(
[&listener_fds, &packets_per_socket, &packets_received, i]() {
do {
@@ -366,7 +366,7 @@ TEST_P(BindToDeviceDistributionTest, Udp) {
receiver_thread->Join();
}
// Check that packets are distributed correctly among listening sockets.
- for (int i = 0; i < packets_per_socket.size(); i++) {
+ for (long unsigned int i = 0; i < packets_per_socket.size(); i++) {
EXPECT_THAT(
packets_per_socket[i],
EquivalentWithin(static_cast<int>(kConnectAttempts *
diff --git a/test/syscalls/linux/socket_generic.cc b/test/syscalls/linux/socket_generic.cc
index 70cc86b16..a28ee2233 100644
--- a/test/syscalls/linux/socket_generic.cc
+++ b/test/syscalls/linux/socket_generic.cc
@@ -853,5 +853,21 @@ TEST_P(AllSocketPairTest, SetAndGetBooleanSocketOptions) {
}
}
+TEST_P(AllSocketPairTest, GetSocketOutOfBandInlineOption) {
+ // We do not support disabling this option. It is always enabled.
+ SKIP_IF(!IsRunningOnGvisor());
+
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+ int enable = -1;
+ socklen_t enableLen = sizeof(enable);
+
+ int want = 1;
+ ASSERT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_OOBINLINE, &enable,
+ &enableLen),
+ SyscallSucceeds());
+ ASSERT_EQ(enableLen, sizeof(enable));
+ EXPECT_EQ(enable, want);
+}
+
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ip_udp_generic.cc b/test/syscalls/linux/socket_ip_udp_generic.cc
index f69f8f99f..1694e188a 100644
--- a/test/syscalls/linux/socket_ip_udp_generic.cc
+++ b/test/syscalls/linux/socket_ip_udp_generic.cc
@@ -15,6 +15,9 @@
#include "test/syscalls/linux/socket_ip_udp_generic.h"
#include <errno.h>
+#ifdef __linux__
+#include <linux/in6.h>
+#endif // __linux__
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <poll.h>
@@ -356,6 +359,58 @@ TEST_P(UDPSocketPairTest, SetAndGetIPPKTINFO) {
EXPECT_EQ(get_len, sizeof(get));
}
+// Test getsockopt for a socket which is not set with IP_RECVORIGDSTADDR option.
+TEST_P(UDPSocketPairTest, ReceiveOrigDstAddrDefault) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+ int level = SOL_IP;
+ int type = IP_RECVORIGDSTADDR;
+ if (sockets->first_addr()->sa_family == AF_INET6) {
+ level = SOL_IPV6;
+ type = IPV6_RECVORIGDSTADDR;
+ }
+ ASSERT_THAT(getsockopt(sockets->first_fd(), level, type, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get_len, sizeof(get));
+ EXPECT_EQ(get, kSockOptOff);
+}
+
+// Test setsockopt and getsockopt for a socket with IP_RECVORIGDSTADDR option.
+TEST_P(UDPSocketPairTest, SetAndGetReceiveOrigDstAddr) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ int level = SOL_IP;
+ int type = IP_RECVORIGDSTADDR;
+ if (sockets->first_addr()->sa_family == AF_INET6) {
+ level = SOL_IPV6;
+ type = IPV6_RECVORIGDSTADDR;
+ }
+
+ // Check getsockopt before IP_PKTINFO is set.
+ int get = -1;
+ socklen_t get_len = sizeof(get);
+
+ ASSERT_THAT(setsockopt(sockets->first_fd(), level, type, &kSockOptOn,
+ sizeof(kSockOptOn)),
+ SyscallSucceedsWithValue(0));
+
+ ASSERT_THAT(getsockopt(sockets->first_fd(), level, type, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get, kSockOptOn);
+ EXPECT_EQ(get_len, sizeof(get));
+
+ ASSERT_THAT(setsockopt(sockets->first_fd(), level, type, &kSockOptOff,
+ sizeof(kSockOptOff)),
+ SyscallSucceedsWithValue(0));
+
+ ASSERT_THAT(getsockopt(sockets->first_fd(), level, type, &get, &get_len),
+ SyscallSucceedsWithValue(0));
+ EXPECT_EQ(get, kSockOptOff);
+ EXPECT_EQ(get_len, sizeof(get));
+}
+
// Holds TOS or TClass information for IPv4 or IPv6 respectively.
struct RecvTosOption {
int level;
@@ -438,7 +493,7 @@ TEST_P(UDPSocketPairTest, TClassRecvMismatch) {
// This should only test AF_INET6 sockets for the mismatch behavior.
SKIP_IF(GetParam().domain != AF_INET6);
// IPV6_RECVTCLASS is only valid for SOCK_DGRAM and SOCK_RAW.
- SKIP_IF(GetParam().type != SOCK_DGRAM | GetParam().type != SOCK_RAW);
+ SKIP_IF((GetParam().type != SOCK_DGRAM) | (GetParam().type != SOCK_RAW));
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.cc b/test/syscalls/linux/socket_ipv4_udp_unbound.cc
index b3f54e7f6..e557572a7 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound.cc
@@ -2222,6 +2222,90 @@ TEST_P(IPv4UDPUnboundSocketTest, SetAndReceiveIPPKTINFO) {
EXPECT_EQ(received_pktinfo.ipi_addr.s_addr, htonl(INADDR_LOOPBACK));
}
+// Test that socket will receive IP_RECVORIGDSTADDR control message.
+TEST_P(IPv4UDPUnboundSocketTest, SetAndReceiveIPReceiveOrigDstAddr) {
+ auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto receiver_addr = V4Loopback();
+ int level = SOL_IP;
+ int type = IP_RECVORIGDSTADDR;
+
+ ASSERT_THAT(
+ bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr),
+ receiver_addr.addr_len),
+ SyscallSucceeds());
+
+ // Retrieve the port bound by the receiver.
+ socklen_t receiver_addr_len = receiver_addr.addr_len;
+ ASSERT_THAT(getsockname(receiver->get(),
+ reinterpret_cast<sockaddr*>(&receiver_addr.addr),
+ &receiver_addr_len),
+ SyscallSucceeds());
+ EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
+
+ ASSERT_THAT(
+ connect(sender->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr),
+ receiver_addr.addr_len),
+ SyscallSucceeds());
+
+ // Get address and port bound by the sender.
+ sockaddr_storage sender_addr_storage;
+ socklen_t sender_addr_len = sizeof(sender_addr_storage);
+ ASSERT_THAT(getsockname(sender->get(),
+ reinterpret_cast<sockaddr*>(&sender_addr_storage),
+ &sender_addr_len),
+ SyscallSucceeds());
+ ASSERT_EQ(sender_addr_len, sizeof(struct sockaddr_in));
+
+ // Enable IP_RECVORIGDSTADDR on socket so that we get the original destination
+ // address of the datagram as auxiliary information in the control message.
+ ASSERT_THAT(
+ setsockopt(receiver->get(), level, type, &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ // Prepare message to send.
+ constexpr size_t kDataLength = 1024;
+ msghdr sent_msg = {};
+ iovec sent_iov = {};
+ char sent_data[kDataLength];
+ sent_iov.iov_base = sent_data;
+ sent_iov.iov_len = kDataLength;
+ sent_msg.msg_iov = &sent_iov;
+ sent_msg.msg_iovlen = 1;
+ sent_msg.msg_flags = 0;
+
+ ASSERT_THAT(RetryEINTR(sendmsg)(sender->get(), &sent_msg, 0),
+ SyscallSucceedsWithValue(kDataLength));
+
+ msghdr received_msg = {};
+ iovec received_iov = {};
+ char received_data[kDataLength];
+ char received_cmsg_buf[CMSG_SPACE(sizeof(sockaddr_in))] = {};
+ size_t cmsg_data_len = sizeof(sockaddr_in);
+ received_iov.iov_base = received_data;
+ received_iov.iov_len = kDataLength;
+ received_msg.msg_iov = &received_iov;
+ received_msg.msg_iovlen = 1;
+ received_msg.msg_controllen = CMSG_LEN(cmsg_data_len);
+ received_msg.msg_control = received_cmsg_buf;
+
+ ASSERT_THAT(RecvMsgTimeout(receiver->get(), &received_msg, 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(kDataLength));
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&received_msg);
+ ASSERT_NE(cmsg, nullptr);
+ EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(cmsg_data_len));
+ EXPECT_EQ(cmsg->cmsg_level, level);
+ EXPECT_EQ(cmsg->cmsg_type, type);
+
+ // Check the data
+ sockaddr_in received_addr = {};
+ memcpy(&received_addr, CMSG_DATA(cmsg), sizeof(received_addr));
+ auto orig_receiver_addr = reinterpret_cast<sockaddr_in*>(&receiver_addr.addr);
+ EXPECT_EQ(received_addr.sin_addr.s_addr, orig_receiver_addr->sin_addr.s_addr);
+ EXPECT_EQ(received_addr.sin_port, orig_receiver_addr->sin_port);
+}
+
// Check that setting SO_RCVBUF below min is clamped to the minimum
// receive buffer size.
TEST_P(IPv4UDPUnboundSocketTest, SetSocketRecvBufBelowMin) {
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc
index 875016812..9a9ddc297 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc
@@ -177,7 +177,7 @@ TEST_P(IPv4UDPUnboundSocketNetlinkTest, ReuseAddrSubnetDirectedBroadcast) {
// Broadcasts from each socket should be received by every socket (including
// the sending socket).
- for (int w = 0; w < socks.size(); w++) {
+ for (long unsigned int w = 0; w < socks.size(); w++) {
auto& w_sock = socks[w];
ASSERT_THAT(
RetryEINTR(sendto)(w_sock->get(), send_buf, kSendBufSize, 0,
@@ -187,7 +187,7 @@ TEST_P(IPv4UDPUnboundSocketNetlinkTest, ReuseAddrSubnetDirectedBroadcast) {
<< "write socks[" << w << "]";
// Check that we received the packet on all sockets.
- for (int r = 0; r < socks.size(); r++) {
+ for (long unsigned int r = 0; r < socks.size(); r++) {
auto& r_sock = socks[r];
struct pollfd poll_fd = {r_sock->get(), POLLIN, 0};
diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound.cc b/test/syscalls/linux/socket_ipv6_udp_unbound.cc
new file mode 100644
index 000000000..08526468e
--- /dev/null
+++ b/test/syscalls/linux/socket_ipv6_udp_unbound.cc
@@ -0,0 +1,131 @@
+// Copyright 2020 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 "test/syscalls/linux/socket_ipv6_udp_unbound.h"
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#ifdef __linux__
+#include <linux/in6.h>
+#endif // __linux__
+#include <net/if.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <cstdio>
+#include <cstring>
+
+#include "gtest/gtest.h"
+#include "absl/memory/memory.h"
+#include "test/syscalls/linux/ip_socket_test_util.h"
+#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/posix_error.h"
+#include "test/util/save_util.h"
+#include "test/util/test_util.h"
+
+namespace gvisor {
+namespace testing {
+
+// Test that socket will receive IP_RECVORIGDSTADDR control message.
+TEST_P(IPv6UDPUnboundSocketTest, SetAndReceiveIPReceiveOrigDstAddr) {
+ auto sender = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto receiver = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
+ auto receiver_addr = V6Loopback();
+ int level = SOL_IPV6;
+ int type = IPV6_RECVORIGDSTADDR;
+
+ ASSERT_THAT(
+ bind(receiver->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr),
+ receiver_addr.addr_len),
+ SyscallSucceeds());
+
+ // Retrieve the port bound by the receiver.
+ socklen_t receiver_addr_len = receiver_addr.addr_len;
+ ASSERT_THAT(getsockname(receiver->get(),
+ reinterpret_cast<sockaddr*>(&receiver_addr.addr),
+ &receiver_addr_len),
+ SyscallSucceeds());
+ EXPECT_EQ(receiver_addr_len, receiver_addr.addr_len);
+
+ ASSERT_THAT(
+ connect(sender->get(), reinterpret_cast<sockaddr*>(&receiver_addr.addr),
+ receiver_addr.addr_len),
+ SyscallSucceeds());
+
+ // Get address and port bound by the sender.
+ sockaddr_storage sender_addr_storage;
+ socklen_t sender_addr_len = sizeof(sender_addr_storage);
+ ASSERT_THAT(getsockname(sender->get(),
+ reinterpret_cast<sockaddr*>(&sender_addr_storage),
+ &sender_addr_len),
+ SyscallSucceeds());
+ ASSERT_EQ(sender_addr_len, sizeof(struct sockaddr_in6));
+
+ // Enable IP_RECVORIGDSTADDR on socket so that we get the original destination
+ // address of the datagram as auxiliary information in the control message.
+ ASSERT_THAT(
+ setsockopt(receiver->get(), level, type, &kSockOptOn, sizeof(kSockOptOn)),
+ SyscallSucceeds());
+
+ // Prepare message to send.
+ constexpr size_t kDataLength = 1024;
+ msghdr sent_msg = {};
+ iovec sent_iov = {};
+ char sent_data[kDataLength];
+ sent_iov.iov_base = sent_data;
+ sent_iov.iov_len = kDataLength;
+ sent_msg.msg_iov = &sent_iov;
+ sent_msg.msg_iovlen = 1;
+ sent_msg.msg_flags = 0;
+
+ ASSERT_THAT(RetryEINTR(sendmsg)(sender->get(), &sent_msg, 0),
+ SyscallSucceedsWithValue(kDataLength));
+
+ msghdr received_msg = {};
+ iovec received_iov = {};
+ char received_data[kDataLength];
+ char received_cmsg_buf[CMSG_SPACE(sizeof(sockaddr_in6))] = {};
+ size_t cmsg_data_len = sizeof(sockaddr_in6);
+ received_iov.iov_base = received_data;
+ received_iov.iov_len = kDataLength;
+ received_msg.msg_iov = &received_iov;
+ received_msg.msg_iovlen = 1;
+ received_msg.msg_controllen = CMSG_LEN(cmsg_data_len);
+ received_msg.msg_control = received_cmsg_buf;
+
+ ASSERT_THAT(RecvMsgTimeout(receiver->get(), &received_msg, 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(kDataLength));
+
+ cmsghdr* cmsg = CMSG_FIRSTHDR(&received_msg);
+ ASSERT_NE(cmsg, nullptr);
+ EXPECT_EQ(cmsg->cmsg_len, CMSG_LEN(cmsg_data_len));
+ EXPECT_EQ(cmsg->cmsg_level, level);
+ EXPECT_EQ(cmsg->cmsg_type, type);
+
+ // Check that the received address in the control message matches the expected
+ // receiver's address.
+ sockaddr_in6 received_addr = {};
+ memcpy(&received_addr, CMSG_DATA(cmsg), sizeof(received_addr));
+ auto orig_receiver_addr =
+ reinterpret_cast<sockaddr_in6*>(&receiver_addr.addr);
+ EXPECT_EQ(memcmp(&received_addr.sin6_addr, &orig_receiver_addr->sin6_addr,
+ sizeof(in6_addr)),
+ 0);
+ EXPECT_EQ(received_addr.sin6_port, orig_receiver_addr->sin6_port);
+}
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound.h b/test/syscalls/linux/socket_ipv6_udp_unbound.h
new file mode 100644
index 000000000..71e160f73
--- /dev/null
+++ b/test/syscalls/linux/socket_ipv6_udp_unbound.h
@@ -0,0 +1,29 @@
+// Copyright 2020 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.
+
+#ifndef GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_H_
+#define GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_H_
+
+#include "test/syscalls/linux/socket_test_util.h"
+
+namespace gvisor {
+namespace testing {
+
+// Test fixture for tests that apply to IPv6 UDP sockets.
+using IPv6UDPUnboundSocketTest = SimpleSocketTest;
+
+} // namespace testing
+} // namespace gvisor
+
+#endif // GVISOR_TEST_SYSCALLS_LINUX_SOCKET_IPV6_UDP_UNBOUND_H_
diff --git a/test/syscalls/linux/socket_ipv6_udp_unbound_loopback.cc b/test/syscalls/linux/socket_ipv6_udp_unbound_loopback.cc
new file mode 100644
index 000000000..058336ecc
--- /dev/null
+++ b/test/syscalls/linux/socket_ipv6_udp_unbound_loopback.cc
@@ -0,0 +1,32 @@
+// Copyright 2020 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 <vector>
+
+#include "test/syscalls/linux/ip_socket_test_util.h"
+#include "test/syscalls/linux/socket_ipv6_udp_unbound.h"
+#include "test/syscalls/linux/socket_test_util.h"
+#include "test/util/test_util.h"
+
+namespace gvisor {
+namespace testing {
+
+INSTANTIATE_TEST_SUITE_P(
+ IPv6UDPSockets, IPv6UDPUnboundSocketTest,
+ ::testing::ValuesIn(ApplyVec<SocketKind>(IPv6UDPUnboundSocket,
+ AllBitwiseCombinations(List<int>{
+ 0, SOCK_NONBLOCK}))));
+
+} // namespace testing
+} // namespace gvisor
diff --git a/test/syscalls/linux/socket_unix_unbound_filesystem.cc b/test/syscalls/linux/socket_unix_unbound_filesystem.cc
index cab912152..a035fb095 100644
--- a/test/syscalls/linux/socket_unix_unbound_filesystem.cc
+++ b/test/syscalls/linux/socket_unix_unbound_filesystem.cc
@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <fcntl.h>
#include <stdio.h>
#include <sys/un.h>
#include "gtest/gtest.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/syscalls/linux/unix_domain_socket_test_util.h"
+#include "test/util/file_descriptor.h"
#include "test/util/test_util.h"
namespace gvisor {
@@ -70,6 +72,20 @@ TEST_P(UnboundFilesystemUnixSocketPairTest, GetSockNameLength) {
strlen(want_addr.sun_path) + 1 + sizeof(want_addr.sun_family));
}
+TEST_P(UnboundFilesystemUnixSocketPairTest, OpenSocketWithTruncate) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ ASSERT_THAT(bind(sockets->first_fd(), sockets->first_addr(),
+ sockets->first_addr_size()),
+ SyscallSucceeds());
+
+ const struct sockaddr_un *addr =
+ reinterpret_cast<const struct sockaddr_un *>(sockets->first_addr());
+ EXPECT_THAT(chmod(addr->sun_path, 0777), SyscallSucceeds());
+ EXPECT_THAT(open(addr->sun_path, O_RDONLY | O_TRUNC),
+ SyscallFailsWithErrno(ENXIO));
+}
+
INSTANTIATE_TEST_SUITE_P(
AllUnixDomainSockets, UnboundFilesystemUnixSocketPairTest,
::testing::ValuesIn(ApplyVec<SocketPairKind>(
diff --git a/test/syscalls/linux/tuntap.cc b/test/syscalls/linux/tuntap.cc
index 97d554e72..5ac337d1a 100644
--- a/test/syscalls/linux/tuntap.cc
+++ b/test/syscalls/linux/tuntap.cc
@@ -324,8 +324,9 @@ TEST_F(TuntapTest, PingKernel) {
};
while (1) {
inpkt r = {};
- int n = read(fd.get(), &r, sizeof(r));
- EXPECT_THAT(n, SyscallSucceeds());
+ int nread = read(fd.get(), &r, sizeof(r));
+ EXPECT_THAT(nread, SyscallSucceeds());
+ long unsigned int n = static_cast<long unsigned int>(nread);
if (n < sizeof(pihdr)) {
std::cerr << "Ignored packet, protocol: " << r.pi.pi_protocol
@@ -383,8 +384,9 @@ TEST_F(TuntapTest, SendUdpTriggersArpResolution) {
};
while (1) {
inpkt r = {};
- int n = read(fd.get(), &r, sizeof(r));
- EXPECT_THAT(n, SyscallSucceeds());
+ int nread = read(fd.get(), &r, sizeof(r));
+ EXPECT_THAT(nread, SyscallSucceeds());
+ long unsigned int n = static_cast<long unsigned int>(nread);
if (n < sizeof(pihdr)) {
std::cerr << "Ignored packet, protocol: " << r.pi.pi_protocol
diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc
index 90ef8bf21..21727a2e7 100644
--- a/test/syscalls/linux/udp_socket.cc
+++ b/test/syscalls/linux/udp_socket.cc
@@ -14,6 +14,8 @@
#include <arpa/inet.h>
#include <fcntl.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip_icmp.h>
#include <ctime>
@@ -779,6 +781,94 @@ TEST_P(UdpSocketTest, ConnectAndSendNoReceiver) {
SyscallFailsWithErrno(ECONNREFUSED));
}
+#ifdef __linux__
+TEST_P(UdpSocketTest, RecvErrorConnRefused) {
+ // We will simulate an ICMP error and verify that we do receive that error via
+ // recvmsg(MSG_ERRQUEUE).
+ ASSERT_NO_ERRNO(BindLoopback());
+ // Close the socket to release the port so that we get an ICMP error.
+ ASSERT_THAT(close(bind_.release()), SyscallSucceeds());
+
+ // Set IP_RECVERR socket option to enable error queueing.
+ int v = kSockOptOn;
+ socklen_t optlen = sizeof(v);
+ int opt_level = SOL_IP;
+ int opt_type = IP_RECVERR;
+ if (GetParam() != AddressFamily::kIpv4) {
+ opt_level = SOL_IPV6;
+ opt_type = IPV6_RECVERR;
+ }
+ ASSERT_THAT(setsockopt(sock_.get(), opt_level, opt_type, &v, optlen),
+ SyscallSucceeds());
+
+ // Connect to loopback:bind_addr_ which should *hopefully* not be bound by an
+ // UDP socket. There is no easy way to ensure that the UDP port is not bound
+ // by another conncurrently running test. *This is potentially flaky*.
+ const int kBufLen = 300;
+ ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds());
+ char buf[kBufLen];
+ RandomizeBuffer(buf, sizeof(buf));
+ // Send from sock_ to an unbound port. This should cause ECONNREFUSED.
+ EXPECT_THAT(send(sock_.get(), buf, sizeof(buf), 0),
+ SyscallSucceedsWithValue(sizeof(buf)));
+
+ // Dequeue error using recvmsg(MSG_ERRQUEUE).
+ char got[kBufLen];
+ struct iovec iov;
+ iov.iov_base = reinterpret_cast<void*>(got);
+ iov.iov_len = kBufLen;
+
+ size_t control_buf_len = CMSG_SPACE(sizeof(sock_extended_err) + addrlen_);
+ char* control_buf = static_cast<char*>(calloc(1, control_buf_len));
+ struct sockaddr_storage remote;
+ memset(&remote, 0, sizeof(remote));
+ struct msghdr msg = {};
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ msg.msg_control = control_buf;
+ msg.msg_controllen = control_buf_len;
+ msg.msg_name = reinterpret_cast<void*>(&remote);
+ msg.msg_namelen = addrlen_;
+ ASSERT_THAT(recvmsg(sock_.get(), &msg, MSG_ERRQUEUE),
+ SyscallSucceedsWithValue(kBufLen));
+
+ // Check the contents of msg.
+ EXPECT_EQ(memcmp(got, buf, sizeof(buf)), 0); // iovec check
+ EXPECT_NE(msg.msg_flags & MSG_ERRQUEUE, 0);
+ EXPECT_EQ(memcmp(&remote, bind_addr_, addrlen_), 0);
+
+ // Check the contents of the control message.
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ ASSERT_NE(cmsg, nullptr);
+ EXPECT_EQ(CMSG_NXTHDR(&msg, cmsg), nullptr);
+ EXPECT_EQ(cmsg->cmsg_level, opt_level);
+ EXPECT_EQ(cmsg->cmsg_type, opt_type);
+
+ // Check the contents of socket error.
+ struct sock_extended_err* sock_err =
+ (struct sock_extended_err*)CMSG_DATA(cmsg);
+ EXPECT_EQ(sock_err->ee_errno, ECONNREFUSED);
+ if (GetParam() == AddressFamily::kIpv4) {
+ EXPECT_EQ(sock_err->ee_origin, SO_EE_ORIGIN_ICMP);
+ EXPECT_EQ(sock_err->ee_type, ICMP_DEST_UNREACH);
+ EXPECT_EQ(sock_err->ee_code, ICMP_PORT_UNREACH);
+ } else {
+ EXPECT_EQ(sock_err->ee_origin, SO_EE_ORIGIN_ICMP6);
+ EXPECT_EQ(sock_err->ee_type, ICMP6_DST_UNREACH);
+ EXPECT_EQ(sock_err->ee_code, ICMP6_DST_UNREACH_NOPORT);
+ }
+
+ // Now verify that the socket error was cleared by recvmsg(MSG_ERRQUEUE).
+ int err;
+ optlen = sizeof(err);
+ ASSERT_THAT(getsockopt(sock_.get(), SOL_SOCKET, SO_ERROR, &err, &optlen),
+ SyscallSucceeds());
+ ASSERT_EQ(err, 0);
+ ASSERT_EQ(optlen, sizeof(err));
+}
+#endif // __linux__
+
TEST_P(UdpSocketTest, ZerolengthWriteAllowed) {
// TODO(gvisor.dev/issue/1202): Hostinet does not support zero length writes.
SKIP_IF(IsRunningWithHostinet());