diff options
Diffstat (limited to 'test/syscalls/linux')
21 files changed, 843 insertions, 140 deletions
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 572f39a5d..c94c1d5bd 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -1285,6 +1285,7 @@ cc_binary( "//test/util:mount_util", "//test/util:multiprocess_util", "//test/util:posix_error", + "//test/util:save_util", "//test/util:temp_path", "//test/util:test_main", "//test/util:test_util", @@ -2434,6 +2435,7 @@ cc_library( "@com_google_absl//absl/memory", gtest, "//test/util:posix_error", + "//test/util:save_util", "//test/util:test_util", ], alwayslink = 1, @@ -3441,6 +3443,7 @@ cc_binary( "@com_google_absl//absl/strings", gtest, "//test/util:posix_error", + "//test/util:save_util", "//test/util:temp_path", "//test/util:test_main", "//test/util:test_util", diff --git a/test/syscalls/linux/mknod.cc b/test/syscalls/linux/mknod.cc index b96907b30..1635c6d0c 100644 --- a/test/syscalls/linux/mknod.cc +++ b/test/syscalls/linux/mknod.cc @@ -125,6 +125,16 @@ TEST(MknodTest, Socket) { ASSERT_THAT(unlink(filename.c_str()), SyscallSucceeds()); } +PosixErrorOr<FileDescriptor> OpenRetryEINTR(std::string const& path, int flags, + mode_t mode = 0) { + while (true) { + auto maybe_fd = Open(path, flags, mode); + if (maybe_fd.ok() || maybe_fd.error().errno_value() != EINTR) { + return maybe_fd; + } + } +} + TEST(MknodTest, Fifo) { const std::string fifo = NewTempAbsPath(); ASSERT_THAT(mknod(fifo.c_str(), S_IFIFO | S_IRUSR | S_IWUSR, 0), @@ -139,14 +149,16 @@ TEST(MknodTest, Fifo) { // Read-end of the pipe. ScopedThread t([&fifo, &buf, &msg]() { - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(fifo.c_str(), O_RDONLY)); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(OpenRetryEINTR(fifo.c_str(), O_RDONLY)); EXPECT_THAT(ReadFd(fd.get(), buf.data(), buf.size()), SyscallSucceedsWithValue(msg.length())); EXPECT_EQ(msg, std::string(buf.data())); }); // Write-end of the pipe. - FileDescriptor wfd = ASSERT_NO_ERRNO_AND_VALUE(Open(fifo.c_str(), O_WRONLY)); + FileDescriptor wfd = + ASSERT_NO_ERRNO_AND_VALUE(OpenRetryEINTR(fifo.c_str(), O_WRONLY)); EXPECT_THAT(WriteFd(wfd.get(), msg.c_str(), msg.length()), SyscallSucceedsWithValue(msg.length())); } @@ -164,15 +176,16 @@ TEST(MknodTest, FifoOtrunc) { std::vector<char> buf(512); // Read-end of the pipe. ScopedThread t([&fifo, &buf, &msg]() { - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(fifo.c_str(), O_RDONLY)); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(OpenRetryEINTR(fifo.c_str(), O_RDONLY)); EXPECT_THAT(ReadFd(fd.get(), buf.data(), buf.size()), SyscallSucceedsWithValue(msg.length())); EXPECT_EQ(msg, std::string(buf.data())); }); // Write-end of the pipe. - FileDescriptor wfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(fifo.c_str(), O_WRONLY | O_TRUNC)); + FileDescriptor wfd = ASSERT_NO_ERRNO_AND_VALUE( + OpenRetryEINTR(fifo.c_str(), O_WRONLY | O_TRUNC)); EXPECT_THAT(WriteFd(wfd.get(), msg.c_str(), msg.length()), SyscallSucceedsWithValue(msg.length())); } @@ -192,14 +205,15 @@ TEST(MknodTest, FifoTruncNoOp) { std::vector<char> buf(512); // Read-end of the pipe. ScopedThread t([&fifo, &buf, &msg]() { - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(fifo.c_str(), O_RDONLY)); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(OpenRetryEINTR(fifo.c_str(), O_RDONLY)); EXPECT_THAT(ReadFd(fd.get(), buf.data(), buf.size()), SyscallSucceedsWithValue(msg.length())); EXPECT_EQ(msg, std::string(buf.data())); }); - FileDescriptor wfd = - ASSERT_NO_ERRNO_AND_VALUE(Open(fifo.c_str(), O_WRONLY | O_TRUNC)); + FileDescriptor wfd = ASSERT_NO_ERRNO_AND_VALUE( + OpenRetryEINTR(fifo.c_str(), O_WRONLY | O_TRUNC)); EXPECT_THAT(ftruncate(wfd.get(), 0), SyscallFailsWithErrno(EINVAL)); EXPECT_THAT(WriteFd(wfd.get(), msg.c_str(), msg.length()), SyscallSucceedsWithValue(msg.length())); diff --git a/test/syscalls/linux/mount.cc b/test/syscalls/linux/mount.cc index 3aab25b23..d65b7d031 100644 --- a/test/syscalls/linux/mount.cc +++ b/test/syscalls/linux/mount.cc @@ -34,6 +34,7 @@ #include "test/util/mount_util.h" #include "test/util/multiprocess_util.h" #include "test/util/posix_error.h" +#include "test/util/save_util.h" #include "test/util/temp_path.h" #include "test/util/test_util.h" #include "test/util/thread_util.h" @@ -131,7 +132,9 @@ TEST(MountTest, UmountDetach) { ASSERT_NO_ERRNO_AND_VALUE(Mount("", dir.path(), "tmpfs", 0, "mode=0700", /* umountflags= */ MNT_DETACH)); const struct stat after = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); - EXPECT_NE(before.st_ino, after.st_ino); + EXPECT_FALSE(before.st_dev == after.st_dev && before.st_ino == after.st_ino) + << "mount point has device number " << before.st_dev + << " and inode number " << before.st_ino << " before and after mount"; // Create files in the new mount. constexpr char kContents[] = "no no no"; @@ -147,12 +150,14 @@ TEST(MountTest, UmountDetach) { // Unmount the tmpfs. mount.Release()(); - // Only check for inode number equality if the directory is not in overlayfs. - // If xino option is not enabled and if all overlayfs layers do not belong to - // the same filesystem then "the value of st_ino for directory objects may not - // be persistent and could change even while the overlay filesystem is - // mounted." -- Documentation/filesystems/overlayfs.txt - if (!ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(dir.path()))) { + // Inode numbers for gofer-accessed files may change across save/restore. + // + // For overlayfs, if xino option is not enabled and if all overlayfs layers do + // not belong to the same filesystem then "the value of st_ino for directory + // objects may not be persistent and could change even while the overlay + // filesystem is mounted." -- Documentation/filesystems/overlayfs.txt + if (!IsRunningWithSaveRestore() && + !ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(dir.path()))) { const struct stat after2 = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); EXPECT_EQ(before.st_ino, after2.st_ino); } @@ -214,18 +219,23 @@ TEST(MountTest, MountTmpfs) { const struct stat s = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); EXPECT_EQ(s.st_mode, S_IFDIR | 0700); - EXPECT_NE(s.st_ino, before.st_ino); + EXPECT_FALSE(before.st_dev == s.st_dev && before.st_ino == s.st_ino) + << "mount point has device number " << before.st_dev + << " and inode number " << before.st_ino << " before and after mount"; EXPECT_NO_ERRNO(Open(JoinPath(dir.path(), "foo"), O_CREAT | O_RDWR, 0777)); } // Now that dir is unmounted again, we should have the old inode back. - // Only check for inode number equality if the directory is not in overlayfs. - // If xino option is not enabled and if all overlayfs layers do not belong to - // the same filesystem then "the value of st_ino for directory objects may not - // be persistent and could change even while the overlay filesystem is - // mounted." -- Documentation/filesystems/overlayfs.txt - if (!ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(dir.path()))) { + // + // Inode numbers for gofer-accessed files may change across save/restore. + // + // For overlayfs, if xino option is not enabled and if all overlayfs layers do + // not belong to the same filesystem then "the value of st_ino for directory + // objects may not be persistent and could change even while the overlay + // filesystem is mounted." -- Documentation/filesystems/overlayfs.txt + if (!IsRunningWithSaveRestore() && + !ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(dir.path()))) { const struct stat after = ASSERT_NO_ERRNO_AND_VALUE(Stat(dir.path())); EXPECT_EQ(before.st_ino, after.st_ino); } diff --git a/test/syscalls/linux/packet_socket_raw.cc b/test/syscalls/linux/packet_socket_raw.cc index b558e3a01..a7c46adbf 100644 --- a/test/syscalls/linux/packet_socket_raw.cc +++ b/test/syscalls/linux/packet_socket_raw.cc @@ -664,6 +664,17 @@ TEST_P(RawPacketTest, SetAndGetSocketLinger) { EXPECT_EQ(0, memcmp(&sl, &got_linger, length)); } +TEST_P(RawPacketTest, GetSocketAcceptConn) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + + int got = -1; + socklen_t length = sizeof(got); + ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ACCEPTCONN, &got, &length), + SyscallSucceedsWithValue(0)); + + ASSERT_EQ(length, sizeof(got)); + EXPECT_EQ(got, 0); +} INSTANTIATE_TEST_SUITE_P(AllInetTests, RawPacketTest, ::testing::Values(ETH_P_IP, ETH_P_ALL)); diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc index e8fcc4439..7a0f33dff 100644 --- a/test/syscalls/linux/proc.cc +++ b/test/syscalls/linux/proc.cc @@ -26,6 +26,7 @@ #include <string.h> #include <sys/mman.h> #include <sys/prctl.h> +#include <sys/ptrace.h> #include <sys/stat.h> #include <sys/statfs.h> #include <sys/utsname.h> @@ -512,6 +513,414 @@ TEST(ProcSelfAuxv, EntryValues) { EXPECT_EQ(i, proc_auxv.size()); } +// Just open and read a part of /proc/self/mem, check that we can read an item. +TEST(ProcPidMem, Read) { + auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/mem", O_RDONLY)); + char input[] = "hello-world"; + char output[sizeof(input)]; + ASSERT_THAT(pread(memfd.get(), output, sizeof(output), + reinterpret_cast<off_t>(input)), + SyscallSucceedsWithValue(sizeof(input))); + ASSERT_STREQ(input, output); +} + +// Perform read on an unmapped region. +TEST(ProcPidMem, Unmapped) { + // Strategy: map then unmap, so we have a guaranteed unmapped region + auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/mem", O_RDONLY)); + Mapping mapping = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + // Fill it with things + memset(mapping.ptr(), 'x', mapping.len()); + char expected = 'x', output; + ASSERT_THAT(pread(memfd.get(), &output, sizeof(output), + reinterpret_cast<off_t>(mapping.ptr())), + SyscallSucceedsWithValue(sizeof(output))); + ASSERT_EQ(expected, output); + + // Unmap region again + ASSERT_THAT(munmap(mapping.ptr(), mapping.len()), SyscallSucceeds()); + + // Now we want EIO error + ASSERT_THAT(pread(memfd.get(), &output, sizeof(output), + reinterpret_cast<off_t>(mapping.ptr())), + SyscallFailsWithErrno(EIO)); +} + +// Perform read repeatedly to verify offset change. +TEST(ProcPidMem, RepeatedRead) { + auto const num_reads = 3; + char expected[] = "01234567890abcdefghijkl"; + char output[sizeof(expected) / num_reads]; + + auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/mem", O_RDONLY)); + ASSERT_THAT(lseek(memfd.get(), reinterpret_cast<off_t>(&expected), SEEK_SET), + SyscallSucceedsWithValue(reinterpret_cast<off_t>(&expected))); + for (auto i = 0; i < num_reads; i++) { + ASSERT_THAT(read(memfd.get(), &output, sizeof(output)), + SyscallSucceedsWithValue(sizeof(output))); + ASSERT_EQ(strncmp(&expected[i * sizeof(output)], output, sizeof(output)), + 0); + } +} + +// Perform seek operations repeatedly. +TEST(ProcPidMem, RepeatedSeek) { + auto const num_reads = 3; + char expected[] = "01234567890abcdefghijkl"; + char output[sizeof(expected) / num_reads]; + + auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/mem", O_RDONLY)); + ASSERT_THAT(lseek(memfd.get(), reinterpret_cast<off_t>(&expected), SEEK_SET), + SyscallSucceedsWithValue(reinterpret_cast<off_t>(&expected))); + // Read from start + ASSERT_THAT(read(memfd.get(), &output, sizeof(output)), + SyscallSucceedsWithValue(sizeof(output))); + ASSERT_EQ(strncmp(&expected[0 * sizeof(output)], output, sizeof(output)), 0); + // Skip ahead one read + ASSERT_THAT(lseek(memfd.get(), sizeof(output), SEEK_CUR), + SyscallSucceedsWithValue(reinterpret_cast<off_t>(&expected) + + sizeof(output) * 2)); + // Do read again + ASSERT_THAT(read(memfd.get(), &output, sizeof(output)), + SyscallSucceedsWithValue(sizeof(output))); + ASSERT_EQ(strncmp(&expected[2 * sizeof(output)], output, sizeof(output)), 0); + // Skip back three reads + ASSERT_THAT(lseek(memfd.get(), -3 * sizeof(output), SEEK_CUR), + SyscallSucceedsWithValue(reinterpret_cast<off_t>(&expected))); + // Do read again + ASSERT_THAT(read(memfd.get(), &output, sizeof(output)), + SyscallSucceedsWithValue(sizeof(output))); + ASSERT_EQ(strncmp(&expected[0 * sizeof(output)], output, sizeof(output)), 0); + // Check that SEEK_END does not work + ASSERT_THAT(lseek(memfd.get(), 0, SEEK_END), SyscallFailsWithErrno(EINVAL)); +} + +// Perform read past an allocated memory region. +TEST(ProcPidMem, PartialRead) { + // Strategy: map large region, then do unmap and remap smaller region + auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/mem", O_RDONLY)); + + Mapping mapping = ASSERT_NO_ERRNO_AND_VALUE( + MmapAnon(2 * kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + ASSERT_THAT(munmap(mapping.ptr(), mapping.len()), SyscallSucceeds()); + Mapping smaller_mapping = ASSERT_NO_ERRNO_AND_VALUE( + Mmap(mapping.ptr(), kPageSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)); + + // Fill it with things + memset(smaller_mapping.ptr(), 'x', smaller_mapping.len()); + + // Now we want no error + char expected[] = {'x'}; + std::unique_ptr<char[]> output(new char[kPageSize]); + off_t read_offset = + reinterpret_cast<off_t>(smaller_mapping.ptr()) + kPageSize - 1; + ASSERT_THAT( + pread(memfd.get(), output.get(), sizeof(output.get()), read_offset), + SyscallSucceedsWithValue(sizeof(expected))); + // Since output is larger, than expected we have to do manual compare + ASSERT_EQ(expected[0], (output).get()[0]); +} + +// Perform read on /proc/[pid]/mem after exit. +TEST(ProcPidMem, AfterExit) { + int pfd1[2] = {}; + int pfd2[2] = {}; + + char expected[] = "hello-world"; + + ASSERT_THAT(pipe(pfd1), SyscallSucceeds()); + ASSERT_THAT(pipe(pfd2), SyscallSucceeds()); + + // Create child process + pid_t const child_pid = fork(); + if (child_pid == 0) { + // Close reading end of first pipe + close(pfd1[0]); + + // Tell parent about location of input + char ok = 1; + TEST_CHECK(WriteFd(pfd1[1], &ok, sizeof(ok)) == sizeof(ok)); + TEST_PCHECK(close(pfd1[1]) == 0); + + // Close writing end of second pipe + TEST_PCHECK(close(pfd2[1]) == 0); + + // Await parent OK to die + ok = 0; + TEST_CHECK(ReadFd(pfd2[0], &ok, sizeof(ok)) == sizeof(ok)); + + // Close rest pipes + TEST_PCHECK(close(pfd2[0]) == 0); + _exit(0); + } + + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Close writing end of first pipe + EXPECT_THAT(close(pfd1[1]), SyscallSucceeds()); + + // Wait for child to be alive and well + char ok = 0; + EXPECT_THAT(ReadFd(pfd1[0], &ok, sizeof(ok)), + SyscallSucceedsWithValue(sizeof(ok))); + // Close reading end of first pipe + EXPECT_THAT(close(pfd1[0]), SyscallSucceeds()); + + // Open /proc/pid/mem fd + std::string mempath = absl::StrCat("/proc/", child_pid, "/mem"); + auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open(mempath, O_RDONLY)); + + // Expect that we can read + char output[sizeof(expected)]; + EXPECT_THAT(pread(memfd.get(), &output, sizeof(output), + reinterpret_cast<off_t>(&expected)), + SyscallSucceedsWithValue(sizeof(output))); + EXPECT_STREQ(expected, output); + + // Tell proc its ok to go + EXPECT_THAT(close(pfd2[0]), SyscallSucceeds()); + ok = 1; + EXPECT_THAT(WriteFd(pfd2[1], &ok, sizeof(ok)), + SyscallSucceedsWithValue(sizeof(ok))); + EXPECT_THAT(close(pfd2[1]), SyscallSucceeds()); + + // Expect termination + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), SyscallSucceeds()); + + // Expect that we can't read anymore + EXPECT_THAT(pread(memfd.get(), &output, sizeof(output), + reinterpret_cast<off_t>(&expected)), + SyscallSucceedsWithValue(0)); +} + +// Read from /proc/[pid]/mem with different UID/GID and attached state. +TEST(ProcPidMem, DifferentUserAttached) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_DAC_OVERRIDE))); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_PTRACE))); + + int pfd1[2] = {}; + int pfd2[2] = {}; + + ASSERT_THAT(pipe(pfd1), SyscallSucceeds()); + ASSERT_THAT(pipe(pfd2), SyscallSucceeds()); + + // Create child process + pid_t const child_pid = fork(); + if (child_pid == 0) { + // Close reading end of first pipe + close(pfd1[0]); + + // Tell parent about location of input + char input[] = "hello-world"; + off_t input_location = reinterpret_cast<off_t>(input); + TEST_CHECK(WriteFd(pfd1[1], &input_location, sizeof(input_location)) == + sizeof(input_location)); + TEST_PCHECK(close(pfd1[1]) == 0); + + // Close writing end of second pipe + TEST_PCHECK(close(pfd2[1]) == 0); + + // Await parent OK to die + char ok = 0; + TEST_CHECK(ReadFd(pfd2[0], &ok, sizeof(ok)) == sizeof(ok)); + + // Close rest pipes + TEST_PCHECK(close(pfd2[0]) == 0); + _exit(0); + } + + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Close writing end of first pipe + EXPECT_THAT(close(pfd1[1]), SyscallSucceeds()); + + // Read target location from child + off_t target_location; + EXPECT_THAT(ReadFd(pfd1[0], &target_location, sizeof(target_location)), + SyscallSucceedsWithValue(sizeof(target_location))); + // Close reading end of first pipe + EXPECT_THAT(close(pfd1[0]), SyscallSucceeds()); + + ScopedThread([&] { + // Attach to child subprocess without stopping it + EXPECT_THAT(ptrace(PTRACE_SEIZE, child_pid, NULL, NULL), SyscallSucceeds()); + + // Keep capabilities after setuid + EXPECT_THAT(prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0), SyscallSucceeds()); + constexpr int kNobody = 65534; + EXPECT_THAT(syscall(SYS_setuid, kNobody), SyscallSucceeds()); + + // Only restore CAP_SYS_PTRACE and CAP_DAC_OVERRIDE + EXPECT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, true)); + EXPECT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, true)); + + // Open /proc/pid/mem fd + std::string mempath = absl::StrCat("/proc/", child_pid, "/mem"); + auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open(mempath, O_RDONLY)); + char expected[] = "hello-world"; + char output[sizeof(expected)]; + EXPECT_THAT(pread(memfd.get(), output, sizeof(output), + reinterpret_cast<off_t>(target_location)), + SyscallSucceedsWithValue(sizeof(output))); + EXPECT_STREQ(expected, output); + + // Tell proc its ok to go + EXPECT_THAT(close(pfd2[0]), SyscallSucceeds()); + char ok = 1; + EXPECT_THAT(WriteFd(pfd2[1], &ok, sizeof(ok)), + SyscallSucceedsWithValue(sizeof(ok))); + EXPECT_THAT(close(pfd2[1]), SyscallSucceeds()); + + // Expect termination + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), SyscallSucceeds()); + EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) + << " status " << status; + }); +} + +// Attempt to read from /proc/[pid]/mem with different UID/GID. +TEST(ProcPidMem, DifferentUser) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SETUID))); + + int pfd1[2] = {}; + int pfd2[2] = {}; + + ASSERT_THAT(pipe(pfd1), SyscallSucceeds()); + ASSERT_THAT(pipe(pfd2), SyscallSucceeds()); + + // Create child process + pid_t const child_pid = fork(); + if (child_pid == 0) { + // Close reading end of first pipe + close(pfd1[0]); + + // Tell parent about location of input + char input[] = "hello-world"; + off_t input_location = reinterpret_cast<off_t>(input); + TEST_CHECK(WriteFd(pfd1[1], &input_location, sizeof(input_location)) == + sizeof(input_location)); + TEST_PCHECK(close(pfd1[1]) == 0); + + // Close writing end of second pipe + TEST_PCHECK(close(pfd2[1]) == 0); + + // Await parent OK to die + char ok = 0; + TEST_CHECK(ReadFd(pfd2[0], &ok, sizeof(ok)) == sizeof(ok)); + + // Close rest pipes + TEST_PCHECK(close(pfd2[0]) == 0); + _exit(0); + } + + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Close writing end of first pipe + EXPECT_THAT(close(pfd1[1]), SyscallSucceeds()); + + // Read target location from child + off_t target_location; + EXPECT_THAT(ReadFd(pfd1[0], &target_location, sizeof(target_location)), + SyscallSucceedsWithValue(sizeof(target_location))); + // Close reading end of first pipe + EXPECT_THAT(close(pfd1[0]), SyscallSucceeds()); + + ScopedThread([&] { + constexpr int kNobody = 65534; + EXPECT_THAT(syscall(SYS_setuid, kNobody), SyscallSucceeds()); + + // Attempt to open /proc/[child_pid]/mem + std::string mempath = absl::StrCat("/proc/", child_pid, "/mem"); + EXPECT_THAT(open(mempath.c_str(), O_RDONLY), SyscallFailsWithErrno(EACCES)); + + // Tell proc its ok to go + EXPECT_THAT(close(pfd2[0]), SyscallSucceeds()); + char ok = 1; + EXPECT_THAT(WriteFd(pfd2[1], &ok, sizeof(ok)), + SyscallSucceedsWithValue(sizeof(ok))); + EXPECT_THAT(close(pfd2[1]), SyscallSucceeds()); + + // Expect termination + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), SyscallSucceeds()); + }); +} + +// Perform read on /proc/[pid]/mem with same UID/GID. +TEST(ProcPidMem, SameUser) { + int pfd1[2] = {}; + int pfd2[2] = {}; + + ASSERT_THAT(pipe(pfd1), SyscallSucceeds()); + ASSERT_THAT(pipe(pfd2), SyscallSucceeds()); + + // Create child process + pid_t const child_pid = fork(); + if (child_pid == 0) { + // Close reading end of first pipe + close(pfd1[0]); + + // Tell parent about location of input + char input[] = "hello-world"; + off_t input_location = reinterpret_cast<off_t>(input); + TEST_CHECK(WriteFd(pfd1[1], &input_location, sizeof(input_location)) == + sizeof(input_location)); + TEST_PCHECK(close(pfd1[1]) == 0); + + // Close writing end of second pipe + TEST_PCHECK(close(pfd2[1]) == 0); + + // Await parent OK to die + char ok = 0; + TEST_CHECK(ReadFd(pfd2[0], &ok, sizeof(ok)) == sizeof(ok)); + + // Close rest pipes + TEST_PCHECK(close(pfd2[0]) == 0); + _exit(0); + } + // In parent process. + ASSERT_THAT(child_pid, SyscallSucceeds()); + + // Close writing end of first pipe + EXPECT_THAT(close(pfd1[1]), SyscallSucceeds()); + + // Read target location from child + off_t target_location; + EXPECT_THAT(ReadFd(pfd1[0], &target_location, sizeof(target_location)), + SyscallSucceedsWithValue(sizeof(target_location))); + // Close reading end of first pipe + EXPECT_THAT(close(pfd1[0]), SyscallSucceeds()); + + // Open /proc/pid/mem fd + std::string mempath = absl::StrCat("/proc/", child_pid, "/mem"); + auto memfd = ASSERT_NO_ERRNO_AND_VALUE(Open(mempath, O_RDONLY)); + char expected[] = "hello-world"; + char output[sizeof(expected)]; + EXPECT_THAT(pread(memfd.get(), output, sizeof(output), + reinterpret_cast<off_t>(target_location)), + SyscallSucceedsWithValue(sizeof(output))); + EXPECT_STREQ(expected, output); + + // Tell proc its ok to go + EXPECT_THAT(close(pfd2[0]), SyscallSucceeds()); + char ok = 1; + EXPECT_THAT(WriteFd(pfd2[1], &ok, sizeof(ok)), + SyscallSucceedsWithValue(sizeof(ok))); + EXPECT_THAT(close(pfd2[1]), SyscallSucceeds()); + + // Expect termination + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), SyscallSucceeds()); +} + // Just open and read /proc/self/maps, check that we can find [stack] TEST(ProcSelfMaps, Basic) { auto proc_self_maps = diff --git a/test/syscalls/linux/proc_pid_smaps.cc b/test/syscalls/linux/proc_pid_smaps.cc index 9fb1b3a2c..738923822 100644 --- a/test/syscalls/linux/proc_pid_smaps.cc +++ b/test/syscalls/linux/proc_pid_smaps.cc @@ -191,7 +191,7 @@ PosixErrorOr<std::vector<ProcPidSmapsEntry>> ParseProcPidSmaps( // amount of whitespace). if (!entry) { std::cerr << "smaps line not considered a maps line: " - << maybe_maps_entry.error_message() << std::endl; + << maybe_maps_entry.error().message() << std::endl; return PosixError( EINVAL, absl::StrCat("smaps field line without preceding maps line: ", l)); diff --git a/test/syscalls/linux/raw_socket_icmp.cc b/test/syscalls/linux/raw_socket_icmp.cc index 1b9dbc584..bd779da92 100644 --- a/test/syscalls/linux/raw_socket_icmp.cc +++ b/test/syscalls/linux/raw_socket_icmp.cc @@ -438,6 +438,19 @@ TEST_F(RawSocketICMPTest, SetAndGetSocketLinger) { EXPECT_EQ(0, memcmp(&sl, &got_linger, length)); } +// Test getsockopt for SO_ACCEPTCONN. +TEST_F(RawSocketICMPTest, GetSocketAcceptConn) { + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW))); + + int got = -1; + socklen_t length = sizeof(got); + ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ACCEPTCONN, &got, &length), + SyscallSucceedsWithValue(0)); + + ASSERT_EQ(length, sizeof(got)); + EXPECT_EQ(got, 0); +} + void RawSocketICMPTest::ExpectICMPSuccess(const struct icmphdr& icmp) { // We're going to receive both the echo request and reply, but the order is // indeterminate. diff --git a/test/syscalls/linux/semaphore.cc b/test/syscalls/linux/semaphore.cc index e9b131ca9..ed6a1c2aa 100644 --- a/test/syscalls/linux/semaphore.cc +++ b/test/syscalls/linux/semaphore.cc @@ -486,6 +486,62 @@ TEST(SemaphoreTest, SemIpcSet) { ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallFailsWithErrno(EACCES)); } +TEST(SemaphoreTest, SemCtlIpcStat) { + // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. + ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); + const uid_t kUid = getuid(); + const gid_t kGid = getgid(); + time_t start_time = time(nullptr); + + AutoSem sem(semget(IPC_PRIVATE, 10, 0600 | IPC_CREAT)); + ASSERT_THAT(sem.get(), SyscallSucceeds()); + + struct semid_ds ds; + EXPECT_THAT(semctl(sem.get(), 0, IPC_STAT, &ds), SyscallSucceeds()); + + EXPECT_EQ(ds.sem_perm.__key, IPC_PRIVATE); + EXPECT_EQ(ds.sem_perm.uid, kUid); + EXPECT_EQ(ds.sem_perm.gid, kGid); + EXPECT_EQ(ds.sem_perm.cuid, kUid); + EXPECT_EQ(ds.sem_perm.cgid, kGid); + EXPECT_EQ(ds.sem_perm.mode, 0600); + // Last semop time is not set on creation. + EXPECT_EQ(ds.sem_otime, 0); + EXPECT_GE(ds.sem_ctime, start_time); + EXPECT_EQ(ds.sem_nsems, 10); + + // The timestamps only have a resolution of seconds; slow down so we actually + // see the timestamps change. + absl::SleepFor(absl::Seconds(1)); + + // Set semid_ds structure of the set. + auto last_ctime = ds.sem_ctime; + start_time = time(nullptr); + struct semid_ds semid_to_set = {}; + semid_to_set.sem_perm.uid = kUid; + semid_to_set.sem_perm.gid = kGid; + semid_to_set.sem_perm.mode = 0666; + ASSERT_THAT(semctl(sem.get(), 0, IPC_SET, &semid_to_set), SyscallSucceeds()); + struct sembuf buf = {}; + buf.sem_op = 1; + ASSERT_THAT(semop(sem.get(), &buf, 1), SyscallSucceeds()); + + EXPECT_THAT(semctl(sem.get(), 0, IPC_STAT, &ds), SyscallSucceeds()); + EXPECT_EQ(ds.sem_perm.mode, 0666); + EXPECT_GE(ds.sem_otime, start_time); + EXPECT_GT(ds.sem_ctime, last_ctime); + + // An invalid semid fails the syscall with errno EINVAL. + EXPECT_THAT(semctl(sem.get() + 1, 0, IPC_STAT, &ds), + SyscallFailsWithErrno(EINVAL)); + + // Make semaphore not readable and check the signal fails. + semid_to_set.sem_perm.mode = 0200; + ASSERT_THAT(semctl(sem.get(), 0, IPC_SET, &semid_to_set), SyscallSucceeds()); + EXPECT_THAT(semctl(sem.get(), 0, IPC_STAT, &ds), + SyscallFailsWithErrno(EACCES)); +} + } // namespace } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc index 11fcec443..39a68c5a5 100644 --- a/test/syscalls/linux/socket_inet_loopback.cc +++ b/test/syscalls/linux/socket_inet_loopback.cc @@ -350,6 +350,10 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdownListen) { sockaddr_storage conn_addr = connector.addr; ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); + // TODO(b/157236388): Remove Disable save after bug is fixed. S/R test can + // fail because the last socket may not be delivered to the accept queue + // by the time connect returns. + DisableSave ds; for (int i = 0; i < kBacklog; i++) { auto client = ASSERT_NO_ERRNO_AND_VALUE( Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP)); @@ -554,7 +558,11 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdownWhileConnect) { }); } -TEST_P(SocketInetLoopbackTest, TCPbacklog) { +// TODO(b/157236388): Remove _NoRandomSave once bug is fixed. Test fails w/ +// random save as established connections which can't be delivered to the accept +// queue because the queue is full are not correctly delivered after restore +// causing the last accept to timeout on the restore. +TEST_P(SocketInetLoopbackTest, TCPbacklog_NoRandomSave) { auto const& param = GetParam(); TestAddress const& listener = param.listener; @@ -567,7 +575,8 @@ TEST_P(SocketInetLoopbackTest, TCPbacklog) { ASSERT_THAT(bind(listen_fd.get(), reinterpret_cast<sockaddr*>(&listen_addr), listener.addr_len), SyscallSucceeds()); - ASSERT_THAT(listen(listen_fd.get(), 2), SyscallSucceeds()); + constexpr int kBacklogSize = 2; + ASSERT_THAT(listen(listen_fd.get(), kBacklogSize), SyscallSucceeds()); // Get the port bound by the listening socket. socklen_t addrlen = listener.addr_len; @@ -1143,6 +1152,9 @@ TEST_P(SocketInetLoopbackTest, TCPAcceptAfterReset) { sockaddr_storage conn_addr = connector.addr; ASSERT_NO_ERRNO(SetAddrPort(connector.family(), &conn_addr, port)); + + // TODO(b/157236388): Reenable Cooperative S/R once bug is fixed. + DisableSave ds; ASSERT_THAT(RetryEINTR(connect)(conn_fd.get(), reinterpret_cast<sockaddr*>(&conn_addr), connector.addr_len), diff --git a/test/syscalls/linux/socket_ip_udp_generic.cc b/test/syscalls/linux/socket_ip_udp_generic.cc index 3f2c0fdf2..f69f8f99f 100644 --- a/test/syscalls/linux/socket_ip_udp_generic.cc +++ b/test/syscalls/linux/socket_ip_udp_generic.cc @@ -472,5 +472,19 @@ TEST_P(UDPSocketPairTest, SetAndGetSocketLinger) { EXPECT_EQ(0, memcmp(&sl, &got_linger, length)); } +// Test getsockopt for SO_ACCEPTCONN on udp socket. +TEST_P(UDPSocketPairTest, GetSocketAcceptConn) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int got = -1; + socklen_t length = sizeof(got); + ASSERT_THAT( + getsockopt(sockets->first_fd(), SOL_SOCKET, SO_ACCEPTCONN, &got, &length), + SyscallSucceedsWithValue(0)); + + ASSERT_EQ(length, sizeof(got)); + EXPECT_EQ(got, 0); +} + } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.cc b/test/syscalls/linux/socket_ipv4_udp_unbound.cc index a72c76c97..b3f54e7f6 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound.cc +++ b/test/syscalls/linux/socket_ipv4_udp_unbound.cc @@ -28,6 +28,7 @@ #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 { @@ -75,7 +76,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackNoGroup) { // Check that we did not receive the multicast packet. char recv_buf[sizeof(send_buf)] = {}; EXPECT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), PosixErrorIs(EAGAIN, ::testing::_)); } @@ -209,7 +210,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackAddr) { // Check that we received the multicast packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); @@ -265,7 +266,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackNic) { // Check that we received the multicast packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); @@ -321,7 +322,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddr) { // Check that we received the multicast packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); @@ -377,7 +378,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNic) { // Check that we received the multicast packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); @@ -437,7 +438,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddrConnect) { // Check that we received the multicast packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); @@ -497,7 +498,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicConnect) { // Check that we received the multicast packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); @@ -553,7 +554,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddrSelf) { // Check that we received the multicast packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); @@ -609,7 +610,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelf) { // Check that we received the multicast packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); @@ -669,7 +670,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddrSelfConnect) { // Check that we did not receive the multicast packet. char recv_buf[sizeof(send_buf)] = {}; EXPECT_THAT( - RecvMsgTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), PosixErrorIs(EAGAIN, ::testing::_)); } @@ -727,7 +728,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfConnect) { // Check that we did not receive the multicast packet. char recv_buf[sizeof(send_buf)] = {}; EXPECT_THAT( - RecvMsgTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), PosixErrorIs(EAGAIN, ::testing::_)); } @@ -785,7 +786,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddrSelfNoLoop) { // Check that we received the multicast packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); @@ -845,7 +846,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfNoLoop) { // Check that we received the multicast packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); @@ -919,7 +920,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastDropAddr) { // Check that we did not receive the multicast packet. char recv_buf[sizeof(send_buf)] = {}; EXPECT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), PosixErrorIs(EAGAIN, ::testing::_)); } @@ -977,7 +978,7 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastDropNic) { // Check that we did not receive the multicast packet. char recv_buf[sizeof(send_buf)] = {}; EXPECT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), PosixErrorIs(EAGAIN, ::testing::_)); } @@ -1330,8 +1331,8 @@ TEST_P(IPv4UDPUnboundSocketTest, TestMcastReceptionOnTwoSockets) { // Check that we received the multicast packet on both sockets. for (auto& sockets : socket_pairs) { char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT(RecvMsgTimeout(sockets->second_fd(), recv_buf, - sizeof(recv_buf), 1 /*timeout*/), + ASSERT_THAT(RecvTimeout(sockets->second_fd(), recv_buf, sizeof(recv_buf), + 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); } @@ -1409,8 +1410,8 @@ TEST_P(IPv4UDPUnboundSocketTest, TestMcastReceptionWhenDroppingMemberships) { // Check that we received the multicast packet on both sockets. for (auto& sockets : socket_pairs) { char recv_buf[sizeof(send_buf)] = {}; - ASSERT_THAT(RecvMsgTimeout(sockets->second_fd(), recv_buf, - sizeof(recv_buf), 1 /*timeout*/), + ASSERT_THAT(RecvTimeout(sockets->second_fd(), recv_buf, sizeof(recv_buf), + 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); } @@ -1432,8 +1433,8 @@ TEST_P(IPv4UDPUnboundSocketTest, TestMcastReceptionWhenDroppingMemberships) { char recv_buf[sizeof(send_buf)] = {}; for (auto& sockets : socket_pairs) { - ASSERT_THAT(RecvMsgTimeout(sockets->second_fd(), recv_buf, - sizeof(recv_buf), 1 /*timeout*/), + ASSERT_THAT(RecvTimeout(sockets->second_fd(), recv_buf, sizeof(recv_buf), + 1 /*timeout*/), PosixErrorIs(EAGAIN, ::testing::_)); } } @@ -1486,7 +1487,7 @@ TEST_P(IPv4UDPUnboundSocketTest, TestBindToMcastThenJoinThenReceive) { // Check that we received the multicast packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); } @@ -1530,7 +1531,7 @@ TEST_P(IPv4UDPUnboundSocketTest, TestBindToMcastThenNoJoinThenNoReceive) { // Check that we don't receive the multicast packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), PosixErrorIs(EAGAIN, ::testing::_)); } @@ -1580,7 +1581,7 @@ TEST_P(IPv4UDPUnboundSocketTest, TestBindToMcastThenSend) { // Check that we received the packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); } @@ -1627,7 +1628,7 @@ TEST_P(IPv4UDPUnboundSocketTest, TestBindToBcastThenReceive) { // Check that we received the multicast packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); } @@ -1678,7 +1679,7 @@ TEST_P(IPv4UDPUnboundSocketTest, TestBindToBcastThenSend) { // Check that we received the packet. char recv_buf[sizeof(send_buf)] = {}; ASSERT_THAT( - RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(recv_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); } @@ -1737,7 +1738,7 @@ TEST_P(IPv4UDPUnboundSocketTest, ReuseAddrDistribution_NoRandomSave) { // of the other sockets to have received it, but we will check that later. char recv_buf[sizeof(send_buf)] = {}; EXPECT_THAT( - RecvMsgTimeout(last->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + RecvTimeout(last->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(send_buf))); EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf))); } @@ -1745,9 +1746,9 @@ TEST_P(IPv4UDPUnboundSocketTest, ReuseAddrDistribution_NoRandomSave) { // Verify that no other messages were received. for (auto& socket : sockets) { char recv_buf[kMessageSize] = {}; - EXPECT_THAT(RecvMsgTimeout(socket->get(), recv_buf, sizeof(recv_buf), - 1 /*timeout*/), - PosixErrorIs(EAGAIN, ::testing::_)); + EXPECT_THAT( + RecvTimeout(socket->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + PosixErrorIs(EAGAIN, ::testing::_)); } } @@ -2108,6 +2109,9 @@ TEST_P(IPv4UDPUnboundSocketTest, ReuseAddrReusePortDistribution) { constexpr int kMessageSize = 10; + // Saving during each iteration of the following loop is too expensive. + DisableSave ds; + for (int i = 0; i < 100; ++i) { // Send a new message to the REUSEADDR/REUSEPORT group. We use a new socket // each time so that a new ephemerial port will be used each time. This @@ -2120,16 +2124,18 @@ TEST_P(IPv4UDPUnboundSocketTest, ReuseAddrReusePortDistribution) { SyscallSucceedsWithValue(sizeof(send_buf))); } + ds.reset(); + // Check that both receivers got messages. This checks that we are using load // balancing (REUSEPORT) instead of the most recently bound socket // (REUSEADDR). char recv_buf[kMessageSize] = {}; - EXPECT_THAT(RecvMsgTimeout(receiver1->get(), recv_buf, sizeof(recv_buf), - 1 /*timeout*/), - IsPosixErrorOkAndHolds(kMessageSize)); - EXPECT_THAT(RecvMsgTimeout(receiver2->get(), recv_buf, sizeof(recv_buf), - 1 /*timeout*/), - IsPosixErrorOkAndHolds(kMessageSize)); + EXPECT_THAT( + RecvTimeout(receiver1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + IsPosixErrorOkAndHolds(kMessageSize)); + EXPECT_THAT( + RecvTimeout(receiver2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/), + IsPosixErrorOkAndHolds(kMessageSize)); } // Test that socket will receive packet info control message. @@ -2193,8 +2199,8 @@ TEST_P(IPv4UDPUnboundSocketTest, SetAndReceiveIPPKTINFO) { received_msg.msg_controllen = CMSG_LEN(cmsg_data_len); received_msg.msg_control = received_cmsg_buf; - ASSERT_THAT(RetryEINTR(recvmsg)(receiver->get(), &received_msg, 0), - SyscallSucceedsWithValue(kDataLength)); + ASSERT_THAT(RecvMsgTimeout(receiver->get(), &received_msg, 1 /*timeout*/), + IsPosixErrorOkAndHolds(kDataLength)); cmsghdr* cmsg = CMSG_FIRSTHDR(&received_msg); ASSERT_NE(cmsg, nullptr); diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc b/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc index 49a0f06d9..875016812 100644 --- a/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc +++ b/test/syscalls/linux/socket_ipv4_udp_unbound_netlink.cc @@ -40,17 +40,9 @@ TEST_P(IPv4UDPUnboundSocketNetlinkTest, JoinSubnet) { /*prefixlen=*/24, &addr, sizeof(addr))); Cleanup defer_addr_removal = Cleanup( [loopback_link = std::move(loopback_link), addr = std::move(addr)] { - if (IsRunningOnGvisor()) { - // TODO(gvisor.dev/issue/3921): Remove this once deleting addresses - // via netlink is supported. - EXPECT_THAT(LinkDelLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, sizeof(addr)), - PosixErrorIs(EOPNOTSUPP, ::testing::_)); - } else { - EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, - sizeof(addr))); - } + EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, + sizeof(addr))); }); auto snd_sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket()); @@ -124,17 +116,9 @@ TEST_P(IPv4UDPUnboundSocketNetlinkTest, ReuseAddrSubnetDirectedBroadcast) { 24 /* prefixlen */, &addr, sizeof(addr))); Cleanup defer_addr_removal = Cleanup( [loopback_link = std::move(loopback_link), addr = std::move(addr)] { - if (IsRunningOnGvisor()) { - // TODO(gvisor.dev/issue/3921): Remove this once deleting addresses - // via netlink is supported. - EXPECT_THAT(LinkDelLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, sizeof(addr)), - PosixErrorIs(EOPNOTSUPP, ::testing::_)); - } else { - EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, - /*prefixlen=*/24, &addr, - sizeof(addr))); - } + EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, + sizeof(addr))); }); TestAddress broadcast_address("SubnetBroadcastAddress"); diff --git a/test/syscalls/linux/socket_netlink_route.cc b/test/syscalls/linux/socket_netlink_route.cc index 241ddad74..e83f0d81f 100644 --- a/test/syscalls/linux/socket_netlink_route.cc +++ b/test/syscalls/linux/socket_netlink_route.cc @@ -511,53 +511,42 @@ TEST(NetlinkRouteTest, LookupAll) { ASSERT_GT(count, 0); } -TEST(NetlinkRouteTest, AddAddr) { +TEST(NetlinkRouteTest, AddAndRemoveAddr) { SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); + // Don't do cooperative save/restore because netstack state is not restored. + // TODO(gvisor.dev/issue/4595): enable cooperative save tests. + const DisableSave ds; Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE)); - - struct request { - struct nlmsghdr hdr; - struct ifaddrmsg ifa; - struct rtattr rtattr; - struct in_addr addr; - char pad[NLMSG_ALIGNTO + RTA_ALIGNTO]; - }; - - struct request req = {}; - req.hdr.nlmsg_type = RTM_NEWADDR; - req.hdr.nlmsg_seq = kSeq; - req.ifa.ifa_family = AF_INET; - req.ifa.ifa_prefixlen = 24; - req.ifa.ifa_flags = 0; - req.ifa.ifa_scope = 0; - req.ifa.ifa_index = loopback_link.index; - req.rtattr.rta_type = IFA_LOCAL; - req.rtattr.rta_len = RTA_LENGTH(sizeof(req.addr)); - inet_pton(AF_INET, "10.0.0.1", &req.addr); - req.hdr.nlmsg_len = - NLMSG_LENGTH(sizeof(req.ifa)) + NLMSG_ALIGN(req.rtattr.rta_len); + struct in_addr addr; + ASSERT_EQ(inet_pton(AF_INET, "10.0.0.1", &addr), 1); // Create should succeed, as no such address in kernel. - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK; - EXPECT_NO_ERRNO( - NetlinkRequestAckOrError(fd, req.hdr.nlmsg_seq, &req, req.hdr.nlmsg_len)); + ASSERT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, sizeof(addr))); + + Cleanup defer_addr_removal = Cleanup( + [loopback_link = std::move(loopback_link), addr = std::move(addr)] { + // First delete should succeed, as address exists. + EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, + sizeof(addr))); + + // Second delete should fail, as address no longer exists. + EXPECT_THAT(LinkDelLocalAddr(loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, sizeof(addr)), + PosixErrorIs(EINVAL, ::testing::_)); + }); // Replace an existing address should succeed. - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK; - req.hdr.nlmsg_seq++; - EXPECT_NO_ERRNO( - NetlinkRequestAckOrError(fd, req.hdr.nlmsg_seq, &req, req.hdr.nlmsg_len)); + ASSERT_NO_ERRNO(LinkReplaceLocalAddr(loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, sizeof(addr))); // Create exclusive should fail, as we created the address above. - req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK; - req.hdr.nlmsg_seq++; - EXPECT_THAT( - NetlinkRequestAckOrError(fd, req.hdr.nlmsg_seq, &req, req.hdr.nlmsg_len), - PosixErrorIs(EEXIST, ::testing::_)); + EXPECT_THAT(LinkAddExclusiveLocalAddr(loopback_link.index, AF_INET, + /*prefixlen=*/24, &addr, sizeof(addr)), + PosixErrorIs(EEXIST, ::testing::_)); } // GetRouteDump tests a RTM_GETROUTE + NLM_F_DUMP request. diff --git a/test/syscalls/linux/socket_netlink_route_util.cc b/test/syscalls/linux/socket_netlink_route_util.cc index 7a0bad4cb..46f749c7c 100644 --- a/test/syscalls/linux/socket_netlink_route_util.cc +++ b/test/syscalls/linux/socket_netlink_route_util.cc @@ -29,6 +29,8 @@ constexpr uint32_t kSeq = 12345; // Types of address modifications that may be performed on an interface. enum class LinkAddrModification { kAdd, + kAddExclusive, + kReplace, kDelete, }; @@ -40,6 +42,14 @@ PosixError PopulateNlmsghdr(LinkAddrModification modification, hdr->nlmsg_type = RTM_NEWADDR; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; return NoError(); + case LinkAddrModification::kAddExclusive: + hdr->nlmsg_type = RTM_NEWADDR; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_ACK; + return NoError(); + case LinkAddrModification::kReplace: + hdr->nlmsg_type = RTM_NEWADDR; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK; + return NoError(); case LinkAddrModification::kDelete: hdr->nlmsg_type = RTM_DELADDR; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; @@ -144,6 +154,18 @@ PosixError LinkAddLocalAddr(int index, int family, int prefixlen, LinkAddrModification::kAdd); } +PosixError LinkAddExclusiveLocalAddr(int index, int family, int prefixlen, + const void* addr, int addrlen) { + return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, + LinkAddrModification::kAddExclusive); +} + +PosixError LinkReplaceLocalAddr(int index, int family, int prefixlen, + const void* addr, int addrlen) { + return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, + LinkAddrModification::kReplace); +} + PosixError LinkDelLocalAddr(int index, int family, int prefixlen, const void* addr, int addrlen) { return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, diff --git a/test/syscalls/linux/socket_netlink_route_util.h b/test/syscalls/linux/socket_netlink_route_util.h index e5badca70..eaa91ad79 100644 --- a/test/syscalls/linux/socket_netlink_route_util.h +++ b/test/syscalls/linux/socket_netlink_route_util.h @@ -39,10 +39,19 @@ PosixErrorOr<std::vector<Link>> DumpLinks(); // Returns the loopback link on the system. ENOENT if not found. PosixErrorOr<Link> LoopbackLink(); -// LinkAddLocalAddr sets IFA_LOCAL attribute on the interface. +// LinkAddLocalAddr adds a new IFA_LOCAL address to the interface. PosixError LinkAddLocalAddr(int index, int family, int prefixlen, const void* addr, int addrlen); +// LinkAddExclusiveLocalAddr adds a new IFA_LOCAL address with NLM_F_EXCL flag +// to the interface. +PosixError LinkAddExclusiveLocalAddr(int index, int family, int prefixlen, + const void* addr, int addrlen); + +// LinkReplaceLocalAddr replaces an IFA_LOCAL address on the interface. +PosixError LinkReplaceLocalAddr(int index, int family, int prefixlen, + const void* addr, int addrlen); + // LinkDelLocalAddr removes IFA_LOCAL attribute on the interface. PosixError LinkDelLocalAddr(int index, int family, int prefixlen, const void* addr, int addrlen); diff --git a/test/syscalls/linux/socket_test_util.cc b/test/syscalls/linux/socket_test_util.cc index e11792309..a760581b5 100644 --- a/test/syscalls/linux/socket_test_util.cc +++ b/test/syscalls/linux/socket_test_util.cc @@ -753,8 +753,7 @@ PosixErrorOr<int> SendMsg(int sock, msghdr* msg, char buf[], int buf_size) { return ret; } -PosixErrorOr<int> RecvMsgTimeout(int sock, char buf[], int buf_size, - int timeout) { +PosixErrorOr<int> RecvTimeout(int sock, char buf[], int buf_size, int timeout) { fd_set rfd; struct timeval to = {.tv_sec = timeout, .tv_usec = 0}; FD_ZERO(&rfd); @@ -767,6 +766,19 @@ PosixErrorOr<int> RecvMsgTimeout(int sock, char buf[], int buf_size, return ret; } +PosixErrorOr<int> RecvMsgTimeout(int sock, struct msghdr* msg, int timeout) { + fd_set rfd; + struct timeval to = {.tv_sec = timeout, .tv_usec = 0}; + FD_ZERO(&rfd); + FD_SET(sock, &rfd); + + int ret; + RETURN_ERROR_IF_SYSCALL_FAIL(ret = select(1, &rfd, NULL, NULL, &to)); + RETURN_ERROR_IF_SYSCALL_FAIL( + ret = RetryEINTR(recvmsg)(sock, msg, MSG_DONTWAIT)); + return ret; +} + void RecvNoData(int sock) { char data = 0; struct iovec iov; diff --git a/test/syscalls/linux/socket_test_util.h b/test/syscalls/linux/socket_test_util.h index 468bc96e0..5e205339f 100644 --- a/test/syscalls/linux/socket_test_util.h +++ b/test/syscalls/linux/socket_test_util.h @@ -467,9 +467,12 @@ PosixError FreeAvailablePort(int port); // SendMsg converts a buffer to an iovec and adds it to msg before sending it. PosixErrorOr<int> SendMsg(int sock, msghdr* msg, char buf[], int buf_size); -// RecvMsgTimeout calls select on sock with timeout and then calls recv on sock. -PosixErrorOr<int> RecvMsgTimeout(int sock, char buf[], int buf_size, - int timeout); +// RecvTimeout calls select on sock with timeout and then calls recv on sock. +PosixErrorOr<int> RecvTimeout(int sock, char buf[], int buf_size, int timeout); + +// RecvMsgTimeout calls select on sock with timeout and then calls recvmsg on +// sock. +PosixErrorOr<int> RecvMsgTimeout(int sock, msghdr* msg, int timeout); // RecvNoData checks that no data is receivable on sock. void RecvNoData(int sock); diff --git a/test/syscalls/linux/socket_unix_stream.cc b/test/syscalls/linux/socket_unix_stream.cc index 1edcb15a7..ad9c4bf37 100644 --- a/test/syscalls/linux/socket_unix_stream.cc +++ b/test/syscalls/linux/socket_unix_stream.cc @@ -121,6 +121,19 @@ TEST_P(StreamUnixSocketPairTest, SetAndGetSocketLinger) { EXPECT_EQ(0, memcmp(&got_linger, &sl, length)); } +TEST_P(StreamUnixSocketPairTest, GetSocketAcceptConn) { + auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair()); + + int got = -1; + socklen_t length = sizeof(got); + ASSERT_THAT( + getsockopt(sockets->first_fd(), SOL_SOCKET, SO_ACCEPTCONN, &got, &length), + SyscallSucceedsWithValue(0)); + + ASSERT_EQ(length, sizeof(got)); + EXPECT_EQ(got, 0); +} + INSTANTIATE_TEST_SUITE_P( AllUnixDomainSockets, StreamUnixSocketPairTest, ::testing::ValuesIn(IncludeReversals(VecCat<SocketPairKind>( diff --git a/test/syscalls/linux/stat.cc b/test/syscalls/linux/stat.cc index 92260b1e1..6e7142a42 100644 --- a/test/syscalls/linux/stat.cc +++ b/test/syscalls/linux/stat.cc @@ -31,6 +31,7 @@ #include "test/util/cleanup.h" #include "test/util/file_descriptor.h" #include "test/util/fs_util.h" +#include "test/util/save_util.h" #include "test/util/temp_path.h" #include "test/util/test_util.h" @@ -328,7 +329,10 @@ TEST_F(StatTest, LeadingDoubleSlash) { ASSERT_THAT(lstat(double_slash_path.c_str(), &double_slash_st), SyscallSucceeds()); EXPECT_EQ(st.st_dev, double_slash_st.st_dev); - EXPECT_EQ(st.st_ino, double_slash_st.st_ino); + // Inode numbers for gofer-accessed files may change across save/restore. + if (!IsRunningWithSaveRestore()) { + EXPECT_EQ(st.st_ino, double_slash_st.st_ino); + } } // Test that a rename doesn't change the underlying file. @@ -346,8 +350,14 @@ TEST_F(StatTest, StatDoesntChangeAfterRename) { EXPECT_EQ(st_old.st_nlink, st_new.st_nlink); EXPECT_EQ(st_old.st_dev, st_new.st_dev); + // Inode numbers for gofer-accessed files on which no reference is held may + // change across save/restore because the information that the gofer client + // uses to track file identity (9P QID path) is inconsistent between gofer + // processes, which are restarted across save/restore. + // // Overlay filesystems may synthesize directory inode numbers on the fly. - if (!ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir()))) { + if (!IsRunningWithSaveRestore() && + !ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir()))) { EXPECT_EQ(st_old.st_ino, st_new.st_ino); } EXPECT_EQ(st_old.st_mode, st_new.st_mode); @@ -541,6 +551,26 @@ TEST_F(StatTest, LstatELOOPPath) { ASSERT_THAT(lstat(path.c_str(), &s), SyscallFailsWithErrno(ELOOP)); } +TEST(SimpleStatTest, DifferentFilesHaveDifferentDeviceInodeNumberPairs) { + // TODO(gvisor.dev/issue/1624): This test case fails in VFS1 save/restore + // tests because VFS1 gofer inode number assignment restarts after + // save/restore, such that the inodes for file1 and file2 (which are + // unreferenced and therefore not retained in sentry checkpoints before the + // calls to lstat()) are assigned the same inode number. + SKIP_IF(IsRunningWithVFS1() && IsRunningWithSaveRestore()); + + TempPath file1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + TempPath file2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + + MaybeSave(); + struct stat st1 = ASSERT_NO_ERRNO_AND_VALUE(Lstat(file1.path())); + MaybeSave(); + struct stat st2 = ASSERT_NO_ERRNO_AND_VALUE(Lstat(file2.path())); + EXPECT_FALSE(st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino) + << "both files have device number " << st1.st_dev << " and inode number " + << st1.st_ino; +} + // Ensure that inode allocation for anonymous devices work correctly across // save/restore. In particular, inode numbers should be unique across S/R. TEST(SimpleStatTest, AnonDeviceAllocatesUniqueInodesAcrossSaveRestore) { diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc index 9f522f833..ebd873068 100644 --- a/test/syscalls/linux/tcp_socket.cc +++ b/test/syscalls/linux/tcp_socket.cc @@ -1725,6 +1725,63 @@ TEST_P(SimpleTcpSocketTest, CloseNonConnectedLingerOption) { ASSERT_LT((end_time - start_time), absl::Seconds(kLingerTimeout)); } +// Tests that SO_ACCEPTCONN returns non zero value for listening sockets. +TEST_P(TcpSocketTest, GetSocketAcceptConnListener) { + int got = -1; + socklen_t length = sizeof(got); + ASSERT_THAT(getsockopt(listener_, SOL_SOCKET, SO_ACCEPTCONN, &got, &length), + SyscallSucceeds()); + ASSERT_EQ(length, sizeof(got)); + EXPECT_EQ(got, 1); +} + +// Tests that SO_ACCEPTCONN returns zero value for not listening sockets. +TEST_P(TcpSocketTest, GetSocketAcceptConnNonListener) { + int got = -1; + socklen_t length = sizeof(got); + ASSERT_THAT(getsockopt(s_, SOL_SOCKET, SO_ACCEPTCONN, &got, &length), + SyscallSucceeds()); + ASSERT_EQ(length, sizeof(got)); + EXPECT_EQ(got, 0); + + ASSERT_THAT(getsockopt(t_, SOL_SOCKET, SO_ACCEPTCONN, &got, &length), + SyscallSucceeds()); + ASSERT_EQ(length, sizeof(got)); + EXPECT_EQ(got, 0); +} + +TEST_P(SimpleTcpSocketTest, GetSocketAcceptConnWithShutdown) { + // TODO(b/171345701): Fix the TCP state for listening socket on shutdown. + SKIP_IF(IsRunningOnGvisor()); + + FileDescriptor s = + ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP)); + + // Initialize address to the loopback one. + sockaddr_storage addr = + ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(GetParam())); + socklen_t addrlen = sizeof(addr); + + // Bind to some port then start listening. + ASSERT_THAT(bind(s.get(), reinterpret_cast<struct sockaddr*>(&addr), addrlen), + SyscallSucceeds()); + + ASSERT_THAT(listen(s.get(), SOMAXCONN), SyscallSucceeds()); + + int got = -1; + socklen_t length = sizeof(got); + ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_ACCEPTCONN, &got, &length), + SyscallSucceeds()); + ASSERT_EQ(length, sizeof(got)); + EXPECT_EQ(got, 1); + + EXPECT_THAT(shutdown(s.get(), SHUT_RD), SyscallSucceeds()); + ASSERT_THAT(getsockopt(s.get(), SOL_SOCKET, SO_ACCEPTCONN, &got, &length), + SyscallSucceeds()); + ASSERT_EQ(length, sizeof(got)); + EXPECT_EQ(got, 0); +} + INSTANTIATE_TEST_SUITE_P(AllInetTests, SimpleTcpSocketTest, ::testing::Values(AF_INET, AF_INET6)); diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc index 1a7673317..bc5bd9218 100644 --- a/test/syscalls/linux/udp_socket.cc +++ b/test/syscalls/linux/udp_socket.cc @@ -679,6 +679,43 @@ TEST_P(UdpSocketTest, SendToAddressOtherThanConnected) { SyscallSucceedsWithValue(sizeof(buf))); } +TEST_P(UdpSocketTest, ConnectAndSendNoReceiver) { + ASSERT_NO_ERRNO(BindLoopback()); + // Close the socket to release the port so that we get an ICMP error. + ASSERT_THAT(close(bind_.release()), 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*. + ASSERT_THAT(connect(sock_.get(), bind_addr_, addrlen_), SyscallSucceeds()); + + char buf[512]; + EXPECT_THAT(send(sock_.get(), buf, sizeof(buf), 0), + SyscallSucceedsWithValue(sizeof(buf))); + + constexpr int kTimeout = 1000; + // Poll to make sure we get the ICMP error back before issuing more writes. + struct pollfd pfd = {sock_.get(), POLLERR, 0}; + ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); + + // Next write should fail with ECONNREFUSED due to the ICMP error generated in + // response to the previous write. + ASSERT_THAT(send(sock_.get(), buf, sizeof(buf), 0), + SyscallFailsWithErrno(ECONNREFUSED)); + + // The next write should succeed again since the last write call would have + // retrieved and cleared the socket error. + ASSERT_THAT(send(sock_.get(), buf, sizeof(buf), 0), SyscallSucceeds()); + + // Poll to make sure we get the ICMP error back before issuing more writes. + ASSERT_THAT(RetryEINTR(poll)(&pfd, 1, kTimeout), SyscallSucceedsWithValue(1)); + + // Next write should fail with ECONNREFUSED due to the ICMP error generated in + // response to the previous write. + ASSERT_THAT(send(sock_.get(), buf, sizeof(buf), 0), + SyscallFailsWithErrno(ECONNREFUSED)); +} + TEST_P(UdpSocketTest, ZerolengthWriteAllowed) { // TODO(gvisor.dev/issue/1202): Hostinet does not support zero length writes. SKIP_IF(IsRunningWithHostinet()); @@ -838,7 +875,7 @@ TEST_P(UdpSocketTest, ReceiveBeforeConnect) { // Receive the data. It works because it was sent before the connect. char received[sizeof(buf)]; EXPECT_THAT( - RecvMsgTimeout(bind_.get(), received, sizeof(received), 1 /*timeout*/), + RecvTimeout(bind_.get(), received, sizeof(received), 1 /*timeout*/), IsPosixErrorOkAndHolds(sizeof(received))); EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0); @@ -928,9 +965,8 @@ TEST_P(UdpSocketTest, ReadShutdownNonblockPendingData) { SyscallSucceedsWithValue(1)); // We should get the data even though read has been shutdown. - EXPECT_THAT( - RecvMsgTimeout(bind_.get(), received, 2 /*buf_size*/, 1 /*timeout*/), - IsPosixErrorOkAndHolds(2)); + EXPECT_THAT(RecvTimeout(bind_.get(), received, 2 /*buf_size*/, 1 /*timeout*/), + IsPosixErrorOkAndHolds(2)); // Because we read less than the entire packet length, since it's a packet // based socket any subsequent reads should return EWOULDBLOCK. @@ -1698,8 +1734,8 @@ TEST_P(UdpSocketTest, RecvBufLimitsEmptyRcvBuf) { sendto(sock_.get(), buf.data(), buf.size(), 0, bind_addr_, addrlen_), SyscallSucceedsWithValue(buf.size())); std::vector<char> received(buf.size()); - EXPECT_THAT(RecvMsgTimeout(bind_.get(), received.data(), received.size(), - 1 /*timeout*/), + EXPECT_THAT(RecvTimeout(bind_.get(), received.data(), received.size(), + 1 /*timeout*/), IsPosixErrorOkAndHolds(received.size())); } @@ -1714,8 +1750,8 @@ TEST_P(UdpSocketTest, RecvBufLimitsEmptyRcvBuf) { SyscallSucceedsWithValue(buf.size())); std::vector<char> received(buf.size()); - ASSERT_THAT(RecvMsgTimeout(bind_.get(), received.data(), received.size(), - 1 /*timeout*/), + ASSERT_THAT(RecvTimeout(bind_.get(), received.data(), received.size(), + 1 /*timeout*/), IsPosixErrorOkAndHolds(received.size())); } } @@ -1785,8 +1821,8 @@ TEST_P(UdpSocketTest, RecvBufLimits) { for (int i = 0; i < sent - 1; i++) { // Receive the data. std::vector<char> received(buf.size()); - EXPECT_THAT(RecvMsgTimeout(bind_.get(), received.data(), received.size(), - 1 /*timeout*/), + EXPECT_THAT(RecvTimeout(bind_.get(), received.data(), received.size(), + 1 /*timeout*/), IsPosixErrorOkAndHolds(received.size())); EXPECT_EQ(memcmp(buf.data(), received.data(), buf.size()), 0); } |