diff options
Diffstat (limited to 'test/syscalls')
-rw-r--r-- | test/syscalls/linux/BUILD | 3 | ||||
-rw-r--r-- | test/syscalls/linux/fcntl.cc | 199 | ||||
-rw-r--r-- | test/syscalls/linux/inotify.cc | 94 | ||||
-rw-r--r-- | test/syscalls/linux/packet_socket_raw.cc | 8 | ||||
-rw-r--r-- | test/syscalls/linux/raw_socket.cc | 8 | ||||
-rw-r--r-- | test/syscalls/linux/shm.cc | 28 | ||||
-rw-r--r-- | test/syscalls/linux/socket.cc | 75 | ||||
-rw-r--r-- | test/syscalls/linux/socket_ipv4_udp_unbound.cc | 6 | ||||
-rw-r--r-- | test/syscalls/linux/socket_unix_cmsg.cc | 2 | ||||
-rw-r--r-- | test/syscalls/linux/tcp_socket.cc | 23 |
10 files changed, 379 insertions, 67 deletions
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index b37716c48..0da295e2d 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -797,6 +797,7 @@ cc_binary( linkstatic = 1, deps = [ ":socket_test_util", + "//test/util:capability_util", "//test/util:cleanup", "//test/util:eventfd_util", "//test/util:file_descriptor", @@ -807,6 +808,7 @@ cc_binary( "@com_google_absl//absl/strings", "@com_google_absl//absl/time", gtest, + "//test/util:memory_util", "//test/util:multiprocess_util", "//test/util:posix_error", "//test/util:save_util", @@ -978,6 +980,7 @@ cc_binary( "//test/util:epoll_util", "//test/util:file_descriptor", "//test/util:fs_util", + "//test/util:multiprocess_util", "//test/util:posix_error", "//test/util:temp_path", "//test/util:test_main", diff --git a/test/syscalls/linux/fcntl.cc b/test/syscalls/linux/fcntl.cc index 4b581045b..75a5c9f17 100644 --- a/test/syscalls/linux/fcntl.cc +++ b/test/syscalls/linux/fcntl.cc @@ -15,6 +15,7 @@ #include <fcntl.h> #include <signal.h> #include <sys/epoll.h> +#include <sys/mman.h> #include <sys/types.h> #include <syscall.h> #include <unistd.h> @@ -35,10 +36,12 @@ #include "absl/time/clock.h" #include "absl/time/time.h" #include "test/syscalls/linux/socket_test_util.h" +#include "test/util/capability_util.h" #include "test/util/cleanup.h" #include "test/util/eventfd_util.h" #include "test/util/file_descriptor.h" #include "test/util/fs_util.h" +#include "test/util/memory_util.h" #include "test/util/multiprocess_util.h" #include "test/util/posix_error.h" #include "test/util/save_util.h" @@ -1642,6 +1645,202 @@ TEST(FcntlTest, SetFlSetOwnSetSigDoNotRace) { } } +TEST_F(FcntlLockTest, GetLockOnNothing) { + SKIP_IF(IsRunningWithVFS1()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds()); + ASSERT_TRUE(fl.l_type == F_UNLCK); +} + +TEST_F(FcntlLockTest, GetLockOnLockSameProcess) { + SKIP_IF(IsRunningWithVFS1()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds()); + ASSERT_TRUE(fl.l_type == F_UNLCK); + + fl.l_type = F_WRLCK; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds()); + ASSERT_TRUE(fl.l_type == F_UNLCK); +} + +TEST_F(FcntlLockTest, GetReadLockOnReadLock) { + SKIP_IF(IsRunningWithVFS1()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + pid_t child_pid = fork(); + if (child_pid == 0) { + TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); + TEST_CHECK(fl.l_type == F_UNLCK); + _exit(0); + } + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); +} + +TEST_F(FcntlLockTest, GetReadLockOnWriteLock) { + SKIP_IF(IsRunningWithVFS1()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + fl.l_type = F_RDLCK; + pid_t child_pid = fork(); + if (child_pid == 0) { + TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); + TEST_CHECK(fl.l_type == F_WRLCK); + TEST_CHECK(fl.l_pid == getppid()); + _exit(0); + } + + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); +} + +TEST_F(FcntlLockTest, GetWriteLockOnReadLock) { + SKIP_IF(IsRunningWithVFS1()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + fl.l_type = F_WRLCK; + pid_t child_pid = fork(); + if (child_pid == 0) { + TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); + TEST_CHECK(fl.l_type == F_RDLCK); + TEST_CHECK(fl.l_pid == getppid()); + _exit(0); + } + + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); +} + +TEST_F(FcntlLockTest, GetWriteLockOnWriteLock) { + SKIP_IF(IsRunningWithVFS1()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + pid_t child_pid = fork(); + if (child_pid == 0) { + TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); + TEST_CHECK(fl.l_type == F_WRLCK); + TEST_CHECK(fl.l_pid == getppid()); + _exit(0); + } + + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); +} + +// Tests that the pid returned from F_GETLK is relative to the caller's PID +// namespace. +TEST_F(FcntlLockTest, GetLockRespectsPIDNamespace) { + SKIP_IF(IsRunningWithVFS1()); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + std::string filename = file.path(); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_RDWR, 0666)); + + // Lock in the parent process. + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + auto child_getlk = [](void* filename) { + int fd = open((char*)filename, O_RDWR, 0666); + TEST_CHECK(fd >= 0); + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + TEST_CHECK(fcntl(fd, F_GETLK, &fl) >= 0); + TEST_CHECK(fl.l_type == F_WRLCK); + // Parent PID should be 0 in the child PID namespace. + TEST_CHECK(fl.l_pid == 0); + close(fd); + return 0; + }; + + // Set up child process in a new PID namespace. + constexpr int kStackSize = 4096; + Mapping stack = ASSERT_NO_ERRNO_AND_VALUE( + Mmap(nullptr, kStackSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0)); + pid_t child_pid; + ASSERT_THAT( + child_pid = clone(child_getlk, (char*)stack.ptr() + stack.len(), + CLONE_NEWPID | SIGCHLD, (void*)filename.c_str()), + SyscallSucceeds()); + + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); +} + } // namespace } // namespace testing diff --git a/test/syscalls/linux/inotify.cc b/test/syscalls/linux/inotify.cc index 8137f0e29..a88c89e20 100644 --- a/test/syscalls/linux/inotify.cc +++ b/test/syscalls/linux/inotify.cc @@ -36,6 +36,7 @@ #include "test/util/epoll_util.h" #include "test/util/file_descriptor.h" #include "test/util/fs_util.h" +#include "test/util/multiprocess_util.h" #include "test/util/posix_error.h" #include "test/util/temp_path.h" #include "test/util/test_util.h" @@ -315,8 +316,7 @@ PosixErrorOr<std::vector<Event>> DrainEvents(int fd) { } PosixErrorOr<FileDescriptor> InotifyInit1(int flags) { - int fd; - EXPECT_THAT(fd = inotify_init1(flags), SyscallSucceeds()); + int fd = inotify_init1(flags); if (fd < 0) { return PosixError(errno, "inotify_init1() failed"); } @@ -325,9 +325,7 @@ PosixErrorOr<FileDescriptor> InotifyInit1(int flags) { PosixErrorOr<int> InotifyAddWatch(int fd, const std::string& path, uint32_t mask) { - int wd; - EXPECT_THAT(wd = inotify_add_watch(fd, path.c_str(), mask), - SyscallSucceeds()); + int wd = inotify_add_watch(fd, path.c_str(), mask); if (wd < 0) { return PosixError(errno, "inotify_add_watch() failed"); } @@ -784,6 +782,38 @@ TEST(Inotify, MoveWatchedTargetGeneratesEvents) { EXPECT_EQ(events[0].cookie, events[1].cookie); } +// Tests that close events are only emitted when a file description drops its +// last reference. +TEST(Inotify, DupFD) { + SKIP_IF(IsRunningWithVFS1()); + + const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + const FileDescriptor inotify_fd = + ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); + + const int wd = ASSERT_NO_ERRNO_AND_VALUE( + InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS)); + + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDONLY)); + FileDescriptor fd2 = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup()); + + std::vector<Event> events = + ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); + EXPECT_THAT(events, Are({ + Event(IN_OPEN, wd), + })); + + fd.reset(); + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); + EXPECT_THAT(events, Are({})); + + fd2.reset(); + events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); + EXPECT_THAT(events, Are({ + Event(IN_CLOSE_NOWRITE, wd), + })); +} + TEST(Inotify, CoalesceEvents) { const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); const FileDescriptor fd = @@ -1779,11 +1809,9 @@ TEST(Inotify, Sendfile) { EXPECT_THAT(out_events, Are({Event(IN_MODIFY, out_wd)})); } -// On Linux, inotify behavior is not very consistent with splice(2). We try our -// best to emulate Linux for very basic calls to splice. TEST(Inotify, SpliceOnWatchTarget) { - int pipes[2]; - ASSERT_THAT(pipe2(pipes, O_NONBLOCK), SyscallSucceeds()); + int pipefds[2]; + ASSERT_THAT(pipe2(pipefds, O_NONBLOCK), SyscallSucceeds()); const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); const FileDescriptor inotify_fd = @@ -1798,15 +1826,20 @@ TEST(Inotify, SpliceOnWatchTarget) { const int file_wd = ASSERT_NO_ERRNO_AND_VALUE( InotifyAddWatch(inotify_fd.get(), file.path(), IN_ALL_EVENTS)); - EXPECT_THAT(splice(fd.get(), nullptr, pipes[1], nullptr, 1, /*flags=*/0), + EXPECT_THAT(splice(fd.get(), nullptr, pipefds[1], nullptr, 1, /*flags=*/0), SyscallSucceedsWithValue(1)); - // Surprisingly, events are not generated in Linux if we read from a file. + // Surprisingly, events may not be generated in Linux if we read from a file. + // fs/splice.c:generic_file_splice_read, which is used most often, does not + // generate events, whereas fs/splice.c:default_file_splice_read does. std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); - ASSERT_THAT(events, Are({})); + if (IsRunningOnGvisor() && !IsRunningWithVFS1()) { + ASSERT_THAT(events, Are({Event(IN_ACCESS, dir_wd, Basename(file.path())), + Event(IN_ACCESS, file_wd)})); + } - EXPECT_THAT(splice(pipes[0], nullptr, fd.get(), nullptr, 1, /*flags=*/0), + EXPECT_THAT(splice(pipefds[0], nullptr, fd.get(), nullptr, 1, /*flags=*/0), SyscallSucceedsWithValue(1)); events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get())); @@ -1817,8 +1850,8 @@ TEST(Inotify, SpliceOnWatchTarget) { } TEST(Inotify, SpliceOnInotifyFD) { - int pipes[2]; - ASSERT_THAT(pipe2(pipes, O_NONBLOCK), SyscallSucceeds()); + int pipefds[2]; + ASSERT_THAT(pipe2(pipefds, O_NONBLOCK), SyscallSucceeds()); const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); const FileDescriptor fd = @@ -1834,11 +1867,11 @@ TEST(Inotify, SpliceOnInotifyFD) { char buf; EXPECT_THAT(read(file1_fd.get(), &buf, 1), SyscallSucceeds()); - EXPECT_THAT(splice(fd.get(), nullptr, pipes[1], nullptr, + EXPECT_THAT(splice(fd.get(), nullptr, pipefds[1], nullptr, sizeof(struct inotify_event) + 1, SPLICE_F_NONBLOCK), SyscallSucceedsWithValue(sizeof(struct inotify_event))); - const FileDescriptor read_fd(pipes[0]); + const FileDescriptor read_fd(pipefds[0]); const std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(read_fd.get())); ASSERT_THAT(events, Are({Event(IN_ACCESS, watcher)})); @@ -1936,24 +1969,29 @@ TEST(Inotify, Xattr) { } TEST(Inotify, Exec) { - const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - const TempPath bin = ASSERT_NO_ERRNO_AND_VALUE( - TempPath::CreateSymlinkTo(dir.path(), "/bin/true")); - + SKIP_IF(IsRunningWithVFS1()); const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK)); const int wd = ASSERT_NO_ERRNO_AND_VALUE( - InotifyAddWatch(fd.get(), bin.path(), IN_ALL_EVENTS)); + InotifyAddWatch(fd.get(), "/bin/true", IN_ALL_EVENTS)); // Perform exec. - ScopedThread t([&bin]() { - ASSERT_THAT(execl(bin.path().c_str(), bin.path().c_str(), (char*)nullptr), - SyscallSucceeds()); - }); - t.Join(); + pid_t child = -1; + int execve_errno = -1; + auto kill = ASSERT_NO_ERRNO_AND_VALUE( + ForkAndExec("/bin/true", {}, {}, nullptr, &child, &execve_errno)); + ASSERT_EQ(0, execve_errno); + + int status; + ASSERT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); + EXPECT_EQ(0, status); + + // Process cleanup no longer needed. + kill.Release(); std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get())); - EXPECT_THAT(events, Are({Event(IN_OPEN, wd), Event(IN_ACCESS, wd)})); + EXPECT_THAT(events, Are({Event(IN_OPEN, wd), Event(IN_ACCESS, wd), + Event(IN_CLOSE_NOWRITE, wd)})); } // Watches without IN_EXCL_UNLINK, should continue to emit events for file diff --git a/test/syscalls/linux/packet_socket_raw.cc b/test/syscalls/linux/packet_socket_raw.cc index 2ed4f6f9c..d25be0e30 100644 --- a/test/syscalls/linux/packet_socket_raw.cc +++ b/test/syscalls/linux/packet_socket_raw.cc @@ -548,13 +548,7 @@ TEST_P(RawPacketTest, SetSocketSendBuf) { ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &val, &val_len), SyscallSucceeds()); - // Linux doubles the value set by SO_SNDBUF/SO_RCVBUF. - // TODO(gvisor.dev/issue/2926): Remove the gvisor special casing when Netstack - // matches linux behavior. - if (!IsRunningOnGvisor()) { - quarter_sz *= 2; - } - + quarter_sz *= 2; ASSERT_EQ(quarter_sz, val); } diff --git a/test/syscalls/linux/raw_socket.cc b/test/syscalls/linux/raw_socket.cc index 955bcee4b..32924466f 100644 --- a/test/syscalls/linux/raw_socket.cc +++ b/test/syscalls/linux/raw_socket.cc @@ -621,13 +621,7 @@ TEST_P(RawSocketTest, SetSocketSendBuf) { ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_SNDBUF, &val, &val_len), SyscallSucceeds()); - // Linux doubles the value set by SO_SNDBUF/SO_RCVBUF. - // TODO(gvisor.dev/issue/2926): Remove the gvisor special casing when Netstack - // matches linux behavior. - if (!IsRunningOnGvisor()) { - quarter_sz *= 2; - } - + quarter_sz *= 2; ASSERT_EQ(quarter_sz, val); } diff --git a/test/syscalls/linux/shm.cc b/test/syscalls/linux/shm.cc index d6e8b3e59..6aabd79e7 100644 --- a/test/syscalls/linux/shm.cc +++ b/test/syscalls/linux/shm.cc @@ -256,32 +256,26 @@ TEST(ShmTest, IpcInfo) { } TEST(ShmTest, ShmInfo) { - struct shm_info info; - - // We generally can't know what other processes on a linux machine - // does with shared memory segments, so we can't test specific - // numbers on Linux. When running under gvisor, we're guaranteed to - // be the only ones using shm, so we can easily verify machine-wide - // numbers. - if (IsRunningOnGvisor()) { - ASSERT_NO_ERRNO(Shmctl(0, SHM_INFO, &info)); - EXPECT_EQ(info.used_ids, 0); - EXPECT_EQ(info.shm_tot, 0); - EXPECT_EQ(info.shm_rss, 0); - EXPECT_EQ(info.shm_swp, 0); - } + // Take a snapshot of the system before the test runs. + struct shm_info snap; + ASSERT_NO_ERRNO(Shmctl(0, SHM_INFO, &snap)); const ShmSegment shm = ASSERT_NO_ERRNO_AND_VALUE( Shmget(IPC_PRIVATE, kAllocSize, IPC_CREAT | 0777)); const char* addr = ASSERT_NO_ERRNO_AND_VALUE(Shmat(shm.id(), nullptr, 0)); + struct shm_info info; ASSERT_NO_ERRNO(Shmctl(1, SHM_INFO, &info)); + // We generally can't know what other processes on a linux machine do with + // shared memory segments, so we can't test specific numbers on Linux. When + // running under gvisor, we're guaranteed to be the only ones using shm, so + // we can easily verify machine-wide numbers. if (IsRunningOnGvisor()) { ASSERT_NO_ERRNO(Shmctl(shm.id(), SHM_INFO, &info)); - EXPECT_EQ(info.used_ids, 1); - EXPECT_EQ(info.shm_tot, kAllocSize / kPageSize); - EXPECT_EQ(info.shm_rss, kAllocSize / kPageSize); + EXPECT_EQ(info.used_ids, snap.used_ids + 1); + EXPECT_EQ(info.shm_tot, snap.shm_tot + (kAllocSize / kPageSize)); + EXPECT_EQ(info.shm_rss, snap.shm_rss + (kAllocSize / kPageSize)); EXPECT_EQ(info.shm_swp, 0); // Gvisor currently never swaps. } diff --git a/test/syscalls/linux/socket.cc b/test/syscalls/linux/socket.cc index 32f583581..b616c2c87 100644 --- a/test/syscalls/linux/socket.cc +++ b/test/syscalls/linux/socket.cc @@ -16,6 +16,7 @@ #include <sys/stat.h> #include <sys/statfs.h> #include <sys/types.h> +#include <sys/wait.h> #include <unistd.h> #include "gtest/gtest.h" @@ -90,8 +91,7 @@ TEST(SocketTest, UnixSocketStat) { 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. - if (!IsRunningOnGvisor()) { + if (!IsRunningWithVFS1()) { EXPECT_NE(statbuf.st_atime, 0); EXPECT_EQ(statbuf.st_atime, statbuf.st_mtime); EXPECT_EQ(statbuf.st_atime, statbuf.st_ctime); @@ -111,6 +111,77 @@ TEST(SocketTest, UnixSocketStatFS) { EXPECT_EQ(st.f_namelen, NAME_MAX); } +TEST(SocketTest, UnixSCMRightsOnlyPassedOnce_NoRandomSave) { + const DisableSave ds; + + int sockets[2]; + ASSERT_THAT(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), SyscallSucceeds()); + // Send more than what will fit inside the send/receive buffers, so that it is + // split into multiple messages. + constexpr int kBufSize = 0x100000; + + pid_t pid = fork(); + if (pid == 0) { + TEST_PCHECK(close(sockets[0]) == 0); + + // Construct a message with some control message. + struct msghdr msg = {}; + char control[CMSG_SPACE(sizeof(int))] = {}; + std::vector<char> buf(kBufSize); + struct iovec iov = {}; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + + struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + ((int*)CMSG_DATA(cmsg))[0] = sockets[1]; + + iov.iov_base = buf.data(); + iov.iov_len = kBufSize; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + int n = sendmsg(sockets[1], &msg, 0); + TEST_PCHECK(n == kBufSize); + TEST_PCHECK(shutdown(sockets[1], SHUT_RDWR) == 0); + TEST_PCHECK(close(sockets[1]) == 0); + _exit(0); + } + + close(sockets[1]); + + struct msghdr msg = {}; + char control[CMSG_SPACE(sizeof(int))] = {}; + std::vector<char> buf(kBufSize); + struct iovec iov = {}; + msg.msg_control = &control; + msg.msg_controllen = sizeof(control); + + iov.iov_base = buf.data(); + iov.iov_len = kBufSize; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + // The control message should only be present in the first message received. + int n; + ASSERT_THAT(n = recvmsg(sockets[0], &msg, 0), SyscallSucceeds()); + ASSERT_GT(n, 0); + ASSERT_EQ(msg.msg_controllen, CMSG_SPACE(sizeof(int))); + + while (n > 0) { + ASSERT_THAT(n = recvmsg(sockets[0], &msg, 0), SyscallSucceeds()); + ASSERT_EQ(msg.msg_controllen, 0); + } + + close(sockets[0]); + + int status; + ASSERT_THAT(waitpid(pid, &status, 0), SyscallSucceedsWithValue(pid)); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); +} + using SocketOpenTest = ::testing::TestWithParam<int>; // UDS cannot be opened. diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.cc b/test/syscalls/linux/socket_ipv4_udp_unbound.cc index e557572a7..8eec31a46 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound.cc +++ b/test/syscalls/linux/socket_ipv4_udp_unbound.cc @@ -2513,11 +2513,7 @@ TEST_P(IPv4UDPUnboundSocketTest, SetSocketSendBuf) { ASSERT_THAT(getsockopt(s->get(), SOL_SOCKET, SO_SNDBUF, &val, &val_len), SyscallSucceeds()); - // Linux doubles the value set by SO_SNDBUF/SO_RCVBUF. - if (!IsRunningOnGvisor()) { - quarter_sz *= 2; - } - + quarter_sz *= 2; ASSERT_EQ(quarter_sz, val); } diff --git a/test/syscalls/linux/socket_unix_cmsg.cc b/test/syscalls/linux/socket_unix_cmsg.cc index a16899493..22a4ee0d1 100644 --- a/test/syscalls/linux/socket_unix_cmsg.cc +++ b/test/syscalls/linux/socket_unix_cmsg.cc @@ -362,7 +362,7 @@ TEST_P(UnixSocketPairCmsgTest, BasicThreeFDPassTruncationMsgCtrunc) { // BasicFDPassUnalignedRecv starts off by sending a single FD just like // BasicFDPass. The difference is that when calling recvmsg, the length of the -// receive data is only aligned on a 4 byte boundry instead of the normal 8. +// receive data is only aligned on a 4 byte boundary instead of the normal 8. TEST_P(UnixSocketPairCmsgTest, BasicFDPassUnalignedRecv) { auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc index 714848b8e..9028ab024 100644 --- a/test/syscalls/linux/tcp_socket.cc +++ b/test/syscalls/linux/tcp_socket.cc @@ -2008,6 +2008,29 @@ TEST_P(SimpleTcpSocketTest, GetSocketAcceptConnWithShutdown) { EXPECT_EQ(got, 0); } +// Tests that connecting to an unspecified address results in ECONNREFUSED. +TEST_P(SimpleTcpSocketTest, ConnectUnspecifiedAddress) { + sockaddr_storage addr; + socklen_t addrlen = sizeof(addr); + memset(&addr, 0, addrlen); + addr.ss_family = GetParam(); + auto do_connect = [&addr, addrlen]() { + FileDescriptor s = ASSERT_NO_ERRNO_AND_VALUE( + Socket(addr.ss_family, SOCK_STREAM, IPPROTO_TCP)); + ASSERT_THAT( + RetryEINTR(connect)(s.get(), reinterpret_cast<struct sockaddr*>(&addr), + addrlen), + SyscallFailsWithErrno(ECONNREFUSED)); + }; + do_connect(); + // Test the v4 mapped address as well. + if (GetParam() == AF_INET6) { + auto sin6 = reinterpret_cast<struct sockaddr_in6*>(&addr); + sin6->sin6_addr.s6_addr[10] = sin6->sin6_addr.s6_addr[11] = 0xff; + do_connect(); + } +} + INSTANTIATE_TEST_SUITE_P(AllInetTests, SimpleTcpSocketTest, ::testing::Values(AF_INET, AF_INET6)); |