summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls')
-rw-r--r--test/syscalls/BUILD2
-rw-r--r--test/syscalls/linux/BUILD4
-rw-r--r--test/syscalls/linux/fallocate.cc6
-rw-r--r--test/syscalls/linux/inotify.cc126
-rw-r--r--test/syscalls/linux/ip6tables.cc30
-rw-r--r--test/syscalls/linux/iptables.cc26
-rw-r--r--test/syscalls/linux/kcov.cc140
-rw-r--r--test/syscalls/linux/mknod.cc9
-rw-r--r--test/syscalls/linux/packet_socket_raw.cc21
-rw-r--r--test/syscalls/linux/proc.cc41
-rw-r--r--test/syscalls/linux/proc_net.cc41
-rw-r--r--test/syscalls/linux/raw_socket_icmp.cc22
-rw-r--r--test/syscalls/linux/rename.cc3
-rw-r--r--test/syscalls/linux/socket_ip_udp_generic.cc12
-rw-r--r--test/syscalls/linux/socket_ipv4_udp_unbound.cc151
-rw-r--r--test/syscalls/linux/socket_netlink_route_util.cc2
-rw-r--r--test/syscalls/linux/socket_test_util.cc14
-rw-r--r--test/syscalls/linux/socket_test_util.h4
-rw-r--r--test/syscalls/linux/socket_unix_stream.cc18
-rw-r--r--test/syscalls/linux/stat.cc18
-rw-r--r--test/syscalls/linux/tcp_socket.cc30
-rw-r--r--test/syscalls/linux/truncate.cc20
-rw-r--r--test/syscalls/linux/udp_socket.cc30
-rw-r--r--test/syscalls/linux/vdso_clock_gettime.cc69
24 files changed, 605 insertions, 234 deletions
diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD
index f949bc0e3..96a775456 100644
--- a/test/syscalls/BUILD
+++ b/test/syscalls/BUILD
@@ -238,7 +238,7 @@ syscall_test(
syscall_test(
size = "medium",
- add_overlay = False, # TODO(gvisor.dev/issue/317): enable when fixed.
+ add_overlay = True,
test = "//test/syscalls/linux:inotify_test",
)
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD
index e5d43cf2e..6a2ec9787 100644
--- a/test/syscalls/linux/BUILD
+++ b/test/syscalls/linux/BUILD
@@ -1079,6 +1079,7 @@ cc_binary(
gtest,
"//test/util:test_main",
"//test/util:test_util",
+ "//test/util:thread_util",
],
)
@@ -1667,12 +1668,14 @@ cc_binary(
"//test/util:cleanup",
"//test/util:file_descriptor",
"//test/util:fs_util",
+ "@com_google_absl//absl/container:node_hash_set",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
gtest,
"//test/util:memory_util",
"//test/util:posix_error",
+ "//test/util:proc_util",
"//test/util:temp_path",
"//test/util:test_util",
"//test/util:thread_util",
@@ -2412,6 +2415,7 @@ cc_library(
":socket_test_util",
"@com_google_absl//absl/memory",
gtest,
+ "//test/util:posix_error",
"//test/util:test_util",
],
alwayslink = 1,
diff --git a/test/syscalls/linux/fallocate.cc b/test/syscalls/linux/fallocate.cc
index cabc2b751..edd23e063 100644
--- a/test/syscalls/linux/fallocate.cc
+++ b/test/syscalls/linux/fallocate.cc
@@ -179,6 +179,12 @@ TEST_F(AllocateTest, FallocateOtherFDs) {
auto sock0 = FileDescriptor(socks[0]);
auto sock1 = FileDescriptor(socks[1]);
EXPECT_THAT(fallocate(sock0.get(), 0, 0, 10), SyscallFailsWithErrno(ENODEV));
+
+ int pipefds[2];
+ ASSERT_THAT(pipe(pipefds), SyscallSucceeds());
+ EXPECT_THAT(fallocate(pipefds[1], 0, 0, 10), SyscallFailsWithErrno(ESPIPE));
+ close(pipefds[0]);
+ close(pipefds[1]);
}
} // namespace
diff --git a/test/syscalls/linux/inotify.cc b/test/syscalls/linux/inotify.cc
index a5c421118..e4392a450 100644
--- a/test/syscalls/linux/inotify.cc
+++ b/test/syscalls/linux/inotify.cc
@@ -465,7 +465,9 @@ TEST(Inotify, ConcurrentFileDeletionAndWatchRemoval) {
for (int i = 0; i < 100; ++i) {
FileDescriptor file_fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_CREAT, S_IRUSR | S_IWUSR));
- file_fd.reset(); // Close before unlinking (although save is disabled).
+ // Close before unlinking (although S/R is disabled). Some filesystems
+ // cannot restore an open fd on an unlinked file.
+ file_fd.reset();
EXPECT_THAT(unlink(filename.c_str()), SyscallSucceeds());
}
};
@@ -1256,10 +1258,7 @@ TEST(Inotify, MknodGeneratesCreateEvent) {
InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS));
const TempPath file1(root.path() + "/file1");
- const int rc = mknod(file1.path().c_str(), S_IFREG, 0);
- // mknod(2) is only supported on tmpfs in the sandbox.
- SKIP_IF(IsRunningOnGvisor() && rc != 0);
- ASSERT_THAT(rc, SyscallSucceeds());
+ ASSERT_THAT(mknod(file1.path().c_str(), S_IFREG, 0), SyscallSucceeds());
const std::vector<Event> events =
ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get()));
@@ -1289,6 +1288,10 @@ TEST(Inotify, SymlinkGeneratesCreateEvent) {
}
TEST(Inotify, LinkGeneratesAttribAndCreateEvents) {
+ // Inotify does not work properly with hard links in gofer and overlay fs.
+ SKIP_IF(IsRunningOnGvisor() &&
+ !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir())));
+
const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
const TempPath file1 =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path()));
@@ -1301,11 +1304,8 @@ TEST(Inotify, LinkGeneratesAttribAndCreateEvents) {
const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE(
InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS));
- const int rc = link(file1.path().c_str(), link1.path().c_str());
- // NOTE(b/34861058): link(2) is only supported on tmpfs in the sandbox.
- SKIP_IF(IsRunningOnGvisor() && rc != 0 &&
- (errno == EPERM || errno == ENOENT));
- ASSERT_THAT(rc, SyscallSucceeds());
+ ASSERT_THAT(link(file1.path().c_str(), link1.path().c_str()),
+ SyscallSucceeds());
const std::vector<Event> events =
ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get()));
@@ -1334,68 +1334,70 @@ TEST(Inotify, UtimesGeneratesAttribEvent) {
}
TEST(Inotify, HardlinksReuseSameWatch) {
+ // Inotify does not work properly with hard links in gofer and overlay fs.
+ SKIP_IF(IsRunningOnGvisor() &&
+ !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir())));
+
const TempPath root = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
- TempPath file1 =
+ TempPath file =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(root.path()));
- TempPath link1(root.path() + "/link1");
- const int rc = link(file1.path().c_str(), link1.path().c_str());
- // link(2) is only supported on tmpfs in the sandbox.
- SKIP_IF(IsRunningOnGvisor() && rc != 0 &&
- (errno == EPERM || errno == ENOENT));
- ASSERT_THAT(rc, SyscallSucceeds());
+
+ TempPath file2(root.path() + "/file2");
+ ASSERT_THAT(link(file.path().c_str(), file2.path().c_str()),
+ SyscallSucceeds());
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK));
const int root_wd = ASSERT_NO_ERRNO_AND_VALUE(
InotifyAddWatch(fd.get(), root.path(), IN_ALL_EVENTS));
- const int file1_wd = ASSERT_NO_ERRNO_AND_VALUE(
- InotifyAddWatch(fd.get(), file1.path(), IN_ALL_EVENTS));
- const int link1_wd = ASSERT_NO_ERRNO_AND_VALUE(
- InotifyAddWatch(fd.get(), link1.path(), IN_ALL_EVENTS));
+ const int file_wd = ASSERT_NO_ERRNO_AND_VALUE(
+ InotifyAddWatch(fd.get(), file.path(), IN_ALL_EVENTS));
+ const int file2_wd = ASSERT_NO_ERRNO_AND_VALUE(
+ InotifyAddWatch(fd.get(), file2.path(), IN_ALL_EVENTS));
// The watch descriptors for watches on different links to the same file
// should be identical.
- EXPECT_NE(root_wd, file1_wd);
- EXPECT_EQ(file1_wd, link1_wd);
+ EXPECT_NE(root_wd, file_wd);
+ EXPECT_EQ(file_wd, file2_wd);
- FileDescriptor file1_fd =
- ASSERT_NO_ERRNO_AND_VALUE(Open(file1.path(), O_WRONLY));
+ FileDescriptor file_fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_WRONLY));
std::vector<Event> events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get()));
ASSERT_THAT(events,
- AreUnordered({Event(IN_OPEN, root_wd, Basename(file1.path())),
- Event(IN_OPEN, file1_wd)}));
+ AreUnordered({Event(IN_OPEN, root_wd, Basename(file.path())),
+ Event(IN_OPEN, file_wd)}));
// For the next step, we want to ensure all fds to the file are closed. Do
// that now and drain the resulting events.
- file1_fd.reset();
+ file_fd.reset();
events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get()));
ASSERT_THAT(
events,
- AreUnordered({Event(IN_CLOSE_WRITE, root_wd, Basename(file1.path())),
- Event(IN_CLOSE_WRITE, file1_wd)}));
+ AreUnordered({Event(IN_CLOSE_WRITE, root_wd, Basename(file.path())),
+ Event(IN_CLOSE_WRITE, file_wd)}));
// Try removing the link and let's see what events show up. Note that after
// this, we still have a link to the file so the watch shouldn't be
// automatically removed.
- const std::string link1_path = link1.reset();
+ const std::string file2_path = file2.reset();
events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get()));
ASSERT_THAT(events,
- AreUnordered({Event(IN_ATTRIB, link1_wd),
- Event(IN_DELETE, root_wd, Basename(link1_path))}));
+ AreUnordered({Event(IN_ATTRIB, file2_wd),
+ Event(IN_DELETE, root_wd, Basename(file2_path))}));
// Now remove the other link. Since this is the last link to the file, the
// watch should be automatically removed.
- const std::string file1_path = file1.reset();
+ const std::string file_path = file.reset();
events = ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(fd.get()));
ASSERT_THAT(
events,
- AreUnordered({Event(IN_ATTRIB, file1_wd), Event(IN_DELETE_SELF, file1_wd),
- Event(IN_IGNORED, file1_wd),
- Event(IN_DELETE, root_wd, Basename(file1_path))}));
+ AreUnordered({Event(IN_ATTRIB, file_wd), Event(IN_DELETE_SELF, file_wd),
+ Event(IN_IGNORED, file_wd),
+ Event(IN_DELETE, root_wd, Basename(file_path))}));
}
// Calling mkdir within "parent/child" should generate an event for child, but
@@ -1806,17 +1808,17 @@ TEST(Inotify, SpliceOnInotifyFD) {
// Watches on a parent should not be triggered by actions on a hard link to one
// of its children that has a different parent.
TEST(Inotify, LinkOnOtherParent) {
+ // Inotify does not work properly with hard links in gofer and overlay fs.
+ SKIP_IF(IsRunningOnGvisor() &&
+ !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir())));
+
const TempPath dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
const TempPath dir2 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
const TempPath file =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path()));
std::string link_path = NewTempAbsPathInDir(dir2.path());
- const int rc = link(file.path().c_str(), link_path.c_str());
- // NOTE(b/34861058): link(2) is only supported on tmpfs in the sandbox.
- SKIP_IF(IsRunningOnGvisor() && rc != 0 &&
- (errno == EPERM || errno == ENOENT));
- ASSERT_THAT(rc, SyscallSucceeds());
+ ASSERT_THAT(link(file.path().c_str(), link_path.c_str()), SyscallSucceeds());
const FileDescriptor inotify_fd =
ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK));
@@ -1825,13 +1827,18 @@ TEST(Inotify, LinkOnOtherParent) {
// Perform various actions on the link outside of dir1, which should trigger
// no inotify events.
- const FileDescriptor fd =
+ FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(link_path.c_str(), O_RDWR));
int val = 0;
ASSERT_THAT(write(fd.get(), &val, sizeof(val)), SyscallSucceeds());
ASSERT_THAT(read(fd.get(), &val, sizeof(val)), SyscallSucceeds());
ASSERT_THAT(ftruncate(fd.get(), 12345), SyscallSucceeds());
+
+ // Close before unlinking; some filesystems cannot restore an open fd on an
+ // unlinked file.
+ fd.reset();
ASSERT_THAT(unlink(link_path.c_str()), SyscallSucceeds());
+
const std::vector<Event> events =
ASSERT_NO_ERRNO_AND_VALUE(DrainEvents(inotify_fd.get()));
EXPECT_THAT(events, Are({}));
@@ -2055,21 +2062,21 @@ TEST(Inotify, ExcludeUnlinkDirectory_NoRandomSave) {
// We need to disable S/R because there are filesystems where we cannot re-open
// fds to an unlinked file across S/R, e.g. gofer-backed filesytems.
TEST(Inotify, ExcludeUnlinkMultipleChildren_NoRandomSave) {
- const DisableSave ds;
+ // Inotify does not work properly with hard links in gofer and overlay fs.
+ SKIP_IF(IsRunningOnGvisor() &&
+ !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir())));
// TODO(gvisor.dev/issue/1624): This test fails on VFS1.
SKIP_IF(IsRunningWithVFS1());
+ const DisableSave ds;
+
const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
const TempPath file =
ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path()));
std::string path1 = file.path();
std::string path2 = NewTempAbsPathInDir(dir.path());
+ ASSERT_THAT(link(path1.c_str(), path2.c_str()), SyscallSucceeds());
- const int rc = link(path1.c_str(), path2.c_str());
- // NOTE(b/34861058): link(2) is only supported on tmpfs in the sandbox.
- SKIP_IF(IsRunningOnGvisor() && rc != 0 &&
- (errno == EPERM || errno == ENOENT));
- ASSERT_THAT(rc, SyscallSucceeds());
const FileDescriptor fd1 =
ASSERT_NO_ERRNO_AND_VALUE(Open(path1.c_str(), O_RDWR));
const FileDescriptor fd2 =
@@ -2101,6 +2108,15 @@ TEST(Inotify, ExcludeUnlinkMultipleChildren_NoRandomSave) {
// We need to disable S/R because there are filesystems where we cannot re-open
// fds to an unlinked file across S/R, e.g. gofer-backed filesytems.
TEST(Inotify, ExcludeUnlinkInodeEvents_NoRandomSave) {
+ // TODO(gvisor.dev/issue/1624): Fails on VFS1.
+ SKIP_IF(IsRunningWithVFS1());
+
+ // NOTE(gvisor.dev/issue/3654): In the gofer filesystem, we do not allow
+ // setting attributes through an fd if the file at the open path has been
+ // deleted.
+ SKIP_IF(IsRunningOnGvisor() &&
+ !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(GetAbsoluteTestTmpdir())));
+
const DisableSave ds;
const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
@@ -2110,18 +2126,6 @@ TEST(Inotify, ExcludeUnlinkInodeEvents_NoRandomSave) {
const FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Open(file.path().c_str(), O_RDWR));
- // NOTE(b/157163751): Create another link before unlinking. This is needed for
- // the gofer filesystem in gVisor, where open fds will not work once the link
- // count hits zero. In VFS2, we end up skipping the gofer test anyway, because
- // hard links are not supported for gofer fs.
- if (IsRunningOnGvisor()) {
- std::string link_path = NewTempAbsPath();
- const int rc = link(file.path().c_str(), link_path.c_str());
- // NOTE(b/34861058): link(2) is only supported on tmpfs in the sandbox.
- SKIP_IF(rc != 0 && (errno == EPERM || errno == ENOENT));
- ASSERT_THAT(rc, SyscallSucceeds());
- }
-
const FileDescriptor inotify_fd =
ASSERT_NO_ERRNO_AND_VALUE(InotifyInit1(IN_NONBLOCK));
const int dir_wd = ASSERT_NO_ERRNO_AND_VALUE(InotifyAddWatch(
diff --git a/test/syscalls/linux/ip6tables.cc b/test/syscalls/linux/ip6tables.cc
index 78e1fa09d..de0a1c114 100644
--- a/test/syscalls/linux/ip6tables.cc
+++ b/test/syscalls/linux/ip6tables.cc
@@ -82,13 +82,37 @@ TEST(IP6TablesBasic, GetEntriesErrorPrecedence) {
SyscallFailsWithErrno(EINVAL));
}
+TEST(IP6TablesBasic, GetRevision) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ int sock;
+ ASSERT_THAT(sock = socket(AF_INET6, SOCK_RAW, IPPROTO_RAW),
+ SyscallSucceeds());
+
+ struct xt_get_revision rev = {
+ .name = "REDIRECT",
+ .revision = 0,
+ };
+ socklen_t rev_len = sizeof(rev);
+
+ // Revision 0 exists.
+ EXPECT_THAT(
+ getsockopt(sock, SOL_IPV6, IP6T_SO_GET_REVISION_TARGET, &rev, &rev_len),
+ SyscallSucceeds());
+ EXPECT_EQ(rev.revision, 0);
+
+ // Revisions > 0 don't exist.
+ rev.revision = 1;
+ EXPECT_THAT(
+ getsockopt(sock, SOL_IPV6, IP6T_SO_GET_REVISION_TARGET, &rev, &rev_len),
+ SyscallFailsWithErrno(EPROTONOSUPPORT));
+}
+
// This tests the initial state of a machine with empty ip6tables via
// getsockopt(IP6T_SO_GET_INFO). We don't have a guarantee that the iptables are
// empty when running in native, but we can test that gVisor has the same
// initial state that a newly-booted Linux machine would have.
TEST(IP6TablesTest, InitialInfo) {
- // TODO(gvisor.dev/issue/3549): Enable for ip6tables.
- SKIP_IF(true);
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
FileDescriptor sock =
@@ -132,8 +156,6 @@ TEST(IP6TablesTest, InitialInfo) {
// are empty when running in native, but we can test that gVisor has the same
// initial state that a newly-booted Linux machine would have.
TEST(IP6TablesTest, InitialEntries) {
- // TODO(gvisor.dev/issue/3549): Enable for ip6tables.
- SKIP_IF(true);
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
FileDescriptor sock =
diff --git a/test/syscalls/linux/iptables.cc b/test/syscalls/linux/iptables.cc
index 83b6a164a..7ee10bbde 100644
--- a/test/syscalls/linux/iptables.cc
+++ b/test/syscalls/linux/iptables.cc
@@ -117,6 +117,32 @@ TEST(IPTablesBasic, OriginalDstErrors) {
SyscallFailsWithErrno(ENOTCONN));
}
+TEST(IPTablesBasic, GetRevision) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ int sock;
+ ASSERT_THAT(sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP),
+ SyscallSucceeds());
+
+ struct xt_get_revision rev = {
+ .name = "REDIRECT",
+ .revision = 0,
+ };
+ socklen_t rev_len = sizeof(rev);
+
+ // Revision 0 exists.
+ EXPECT_THAT(
+ getsockopt(sock, SOL_IP, IPT_SO_GET_REVISION_TARGET, &rev, &rev_len),
+ SyscallSucceeds());
+ EXPECT_EQ(rev.revision, 0);
+
+ // Revisions > 0 don't exist.
+ rev.revision = 1;
+ EXPECT_THAT(
+ getsockopt(sock, SOL_IP, IPT_SO_GET_REVISION_TARGET, &rev, &rev_len),
+ SyscallFailsWithErrno(EPROTONOSUPPORT));
+}
+
// Fixture for iptables tests.
class IPTablesTest : public ::testing::Test {
protected:
diff --git a/test/syscalls/linux/kcov.cc b/test/syscalls/linux/kcov.cc
index f3c30444e..6816c1fd0 100644
--- a/test/syscalls/linux/kcov.cc
+++ b/test/syscalls/linux/kcov.cc
@@ -16,38 +16,47 @@
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <atomic>
+
#include "gtest/gtest.h"
#include "test/util/capability_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/test_util.h"
+#include "test/util/thread_util.h"
namespace gvisor {
namespace testing {
namespace {
-// For this test to work properly, it must be run with coverage enabled. On
+// For this set of tests to run, they must be run with coverage enabled. On
// native Linux, this involves compiling the kernel with kcov enabled. For
-// gVisor, we need to enable the Go coverage tool, e.g.
-// bazel test --collect_coverage_data --instrumentation_filter=//pkg/... <test>.
+// gVisor, we need to enable the Go coverage tool, e.g. bazel test --
+// collect_coverage_data --instrumentation_filter=//pkg/... <test>.
+
+constexpr char kcovPath[] = "/sys/kernel/debug/kcov";
+constexpr int kSize = 4096;
+constexpr int KCOV_INIT_TRACE = 0x80086301;
+constexpr int KCOV_ENABLE = 0x6364;
+constexpr int KCOV_DISABLE = 0x6365;
+
+uint64_t* KcovMmap(int fd) {
+ return (uint64_t*)mmap(nullptr, kSize * sizeof(uint64_t),
+ PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+}
+
TEST(KcovTest, Kcov) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE))));
- constexpr int kSize = 4096;
- constexpr int KCOV_INIT_TRACE = 0x80086301;
- constexpr int KCOV_ENABLE = 0x6364;
-
int fd;
- ASSERT_THAT(fd = open("/sys/kernel/debug/kcov", O_RDWR),
+ ASSERT_THAT(fd = open(kcovPath, O_RDWR),
AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(ENOENT)));
-
- // Kcov not enabled.
+ // Kcov not available.
SKIP_IF(errno == ENOENT);
+ auto fd_closer = Cleanup([fd]() { close(fd); });
ASSERT_THAT(ioctl(fd, KCOV_INIT_TRACE, kSize), SyscallSucceeds());
- uint64_t* area = (uint64_t*)mmap(nullptr, kSize * sizeof(uint64_t),
- PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
-
+ uint64_t* area = KcovMmap(fd);
ASSERT_TRUE(area != MAP_FAILED);
ASSERT_THAT(ioctl(fd, KCOV_ENABLE, 0), SyscallSucceeds());
@@ -62,6 +71,111 @@ TEST(KcovTest, Kcov) {
// Verify that PCs are in the standard kernel range.
EXPECT_GT(area[i], 0xffffffff7fffffffL);
}
+
+ ASSERT_THAT(ioctl(fd, KCOV_DISABLE, 0), SyscallSucceeds());
+}
+
+TEST(KcovTest, PrematureMmap) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE))));
+
+ int fd;
+ ASSERT_THAT(fd = open(kcovPath, O_RDWR),
+ AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(ENOENT)));
+ // Kcov not available.
+ SKIP_IF(errno == ENOENT);
+ auto fd_closer = Cleanup([fd]() { close(fd); });
+
+ // Cannot mmap before KCOV_INIT_TRACE.
+ uint64_t* area = KcovMmap(fd);
+ ASSERT_TRUE(area == MAP_FAILED);
+}
+
+// Tests that multiple kcov fds can be used simultaneously.
+TEST(KcovTest, MultipleFds) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE))));
+
+ int fd1;
+ ASSERT_THAT(fd1 = open(kcovPath, O_RDWR),
+ AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(ENOENT)));
+ // Kcov not available.
+ SKIP_IF(errno == ENOENT);
+
+ int fd2;
+ ASSERT_THAT(fd2 = open(kcovPath, O_RDWR), SyscallSucceeds());
+ auto fd_closer = Cleanup([fd1, fd2]() {
+ close(fd1);
+ close(fd2);
+ });
+
+ auto t1 = ScopedThread([&] {
+ ASSERT_THAT(ioctl(fd1, KCOV_INIT_TRACE, kSize), SyscallSucceeds());
+ uint64_t* area = KcovMmap(fd1);
+ ASSERT_TRUE(area != MAP_FAILED);
+ ASSERT_THAT(ioctl(fd1, KCOV_ENABLE, 0), SyscallSucceeds());
+ });
+
+ ASSERT_THAT(ioctl(fd2, KCOV_INIT_TRACE, kSize), SyscallSucceeds());
+ uint64_t* area = KcovMmap(fd2);
+ ASSERT_TRUE(area != MAP_FAILED);
+ ASSERT_THAT(ioctl(fd2, KCOV_ENABLE, 0), SyscallSucceeds());
+}
+
+// Tests behavior for two threads trying to use the same kcov fd.
+TEST(KcovTest, MultipleThreads) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE))));
+
+ int fd;
+ ASSERT_THAT(fd = open(kcovPath, O_RDWR),
+ AnyOf(SyscallSucceeds(), SyscallFailsWithErrno(ENOENT)));
+ // Kcov not available.
+ SKIP_IF(errno == ENOENT);
+ auto fd_closer = Cleanup([fd]() { close(fd); });
+
+ // Test the behavior of multiple threads trying to use the same kcov fd
+ // simultaneously.
+ std::atomic<bool> t1_enabled(false), t1_disabled(false), t2_failed(false),
+ t2_exited(false);
+ auto t1 = ScopedThread([&] {
+ ASSERT_THAT(ioctl(fd, KCOV_INIT_TRACE, kSize), SyscallSucceeds());
+ uint64_t* area = KcovMmap(fd);
+ ASSERT_TRUE(area != MAP_FAILED);
+ ASSERT_THAT(ioctl(fd, KCOV_ENABLE, 0), SyscallSucceeds());
+ t1_enabled = true;
+
+ // After t2 has made sure that enabling kcov again fails, disable it.
+ while (!t2_failed) {
+ sched_yield();
+ }
+ ASSERT_THAT(ioctl(fd, KCOV_DISABLE, 0), SyscallSucceeds());
+ t1_disabled = true;
+
+ // Wait for t2 to enable kcov and then exit, after which we should be able
+ // to enable kcov again, without needing to set up a new memory mapping.
+ while (!t2_exited) {
+ sched_yield();
+ }
+ ASSERT_THAT(ioctl(fd, KCOV_ENABLE, 0), SyscallSucceeds());
+ });
+
+ auto t2 = ScopedThread([&] {
+ // Wait for t1 to enable kcov, and make sure that enabling kcov again fails.
+ while (!t1_enabled) {
+ sched_yield();
+ }
+ ASSERT_THAT(ioctl(fd, KCOV_ENABLE, 0), SyscallFailsWithErrno(EINVAL));
+ t2_failed = true;
+
+ // Wait for t1 to disable kcov, after which using fd should now succeed.
+ while (!t1_disabled) {
+ sched_yield();
+ }
+ uint64_t* area = KcovMmap(fd);
+ ASSERT_TRUE(area != MAP_FAILED);
+ ASSERT_THAT(ioctl(fd, KCOV_ENABLE, 0), SyscallSucceeds());
+ });
+
+ t2.Join();
+ t2_exited = true;
}
} // namespace
diff --git a/test/syscalls/linux/mknod.cc b/test/syscalls/linux/mknod.cc
index 89e4564e8..ae65d366b 100644
--- a/test/syscalls/linux/mknod.cc
+++ b/test/syscalls/linux/mknod.cc
@@ -105,11 +105,13 @@ TEST(MknodTest, UnimplementedTypesReturnError) {
}
TEST(MknodTest, Socket) {
+ SKIP_IF(IsRunningOnGvisor() && IsRunningWithVFS1());
+
ASSERT_THAT(chdir(GetAbsoluteTestTmpdir().c_str()), SyscallSucceeds());
- SKIP_IF(IsRunningOnGvisor() && IsRunningWithVFS1());
+ auto filename = NewTempRelPath();
- ASSERT_THAT(mknod("./file0", S_IFSOCK | S_IRUSR | S_IWUSR, 0),
+ ASSERT_THAT(mknod(filename.c_str(), S_IFSOCK | S_IRUSR | S_IWUSR, 0),
SyscallSucceeds());
int sk;
@@ -117,9 +119,10 @@ TEST(MknodTest, Socket) {
FileDescriptor fd(sk);
struct sockaddr_un addr = {.sun_family = AF_UNIX};
- absl::SNPrintF(addr.sun_path, sizeof(addr.sun_path), "./file0");
+ absl::SNPrintF(addr.sun_path, sizeof(addr.sun_path), "%s", filename.c_str());
ASSERT_THAT(connect(sk, (struct sockaddr *)&addr, sizeof(addr)),
SyscallFailsWithErrno(ECONNREFUSED));
+ ASSERT_THAT(unlink(filename.c_str()), SyscallSucceeds());
}
TEST(MknodTest, Fifo) {
diff --git a/test/syscalls/linux/packet_socket_raw.cc b/test/syscalls/linux/packet_socket_raw.cc
index f3c1d6bc9..b558e3a01 100644
--- a/test/syscalls/linux/packet_socket_raw.cc
+++ b/test/syscalls/linux/packet_socket_raw.cc
@@ -643,6 +643,27 @@ TEST_P(RawPacketTest, GetSocketDetachFilter) {
SyscallFailsWithErrno(ENOPROTOOPT));
}
+TEST_P(RawPacketTest, SetAndGetSocketLinger) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ int level = SOL_SOCKET;
+ int type = SO_LINGER;
+
+ struct linger sl;
+ sl.l_onoff = 1;
+ sl.l_linger = 5;
+ ASSERT_THAT(setsockopt(s_, level, type, &sl, sizeof(sl)),
+ SyscallSucceedsWithValue(0));
+
+ struct linger got_linger = {};
+ socklen_t length = sizeof(sl);
+ ASSERT_THAT(getsockopt(s_, level, type, &got_linger, &length),
+ SyscallSucceedsWithValue(0));
+
+ ASSERT_EQ(length, sizeof(got_linger));
+ EXPECT_EQ(0, memcmp(&sl, &got_linger, length));
+}
+
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 1ba7bf4f5..e8fcc4439 100644
--- a/test/syscalls/linux/proc.cc
+++ b/test/syscalls/linux/proc.cc
@@ -47,6 +47,7 @@
#include "gmock/gmock.h"
#include "gtest/gtest.h"
+#include "absl/container/node_hash_set.h"
#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
#include "absl/strings/numbers.h"
@@ -63,6 +64,7 @@
#include "test/util/fs_util.h"
#include "test/util/memory_util.h"
#include "test/util/posix_error.h"
+#include "test/util/proc_util.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
@@ -672,6 +674,23 @@ TEST(ProcSelfMaps, Mprotect) {
3 * kPageSize, PROT_READ)));
}
+TEST(ProcSelfMaps, SharedAnon) {
+ const Mapping m = ASSERT_NO_ERRNO_AND_VALUE(
+ MmapAnon(kPageSize, PROT_READ, MAP_SHARED | MAP_ANONYMOUS));
+
+ const auto proc_self_maps =
+ ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/self/maps"));
+ for (const auto& line : absl::StrSplit(proc_self_maps, '\n')) {
+ const auto entry = ASSERT_NO_ERRNO_AND_VALUE(ParseProcMapsLine(line));
+ if (entry.start <= m.addr() && m.addr() < entry.end) {
+ // cf. proc(5), "/proc/[pid]/map_files/"
+ EXPECT_EQ(entry.filename, "/dev/zero (deleted)");
+ return;
+ }
+ }
+ FAIL() << "no maps entry containing mapping at " << m.ptr();
+}
+
TEST(ProcSelfFd, OpenFd) {
int pipe_fds[2];
ASSERT_THAT(pipe2(pipe_fds, O_CLOEXEC), SyscallSucceeds());
@@ -703,8 +722,8 @@ static void CheckFdDirGetdentsDuplicates(const std::string& path) {
EXPECT_GE(newfd, 1024);
auto fd_closer = Cleanup([newfd]() { close(newfd); });
auto fd_files = ASSERT_NO_ERRNO_AND_VALUE(ListDir(path.c_str(), false));
- std::unordered_set<std::string> fd_files_dedup(fd_files.begin(),
- fd_files.end());
+ absl::node_hash_set<std::string> fd_files_dedup(fd_files.begin(),
+ fd_files.end());
EXPECT_EQ(fd_files.size(), fd_files_dedup.size());
}
@@ -761,8 +780,12 @@ TEST(ProcSelfFdInfo, Flags) {
}
TEST(ProcSelfExe, Absolute) {
- auto exe = ASSERT_NO_ERRNO_AND_VALUE(
- ReadLink(absl::StrCat("/proc/", getpid(), "/exe")));
+ auto exe = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/exe"));
+ EXPECT_EQ(exe[0], '/');
+}
+
+TEST(ProcSelfCwd, Absolute) {
+ auto exe = ASSERT_NO_ERRNO_AND_VALUE(ReadLink("/proc/self/cwd"));
EXPECT_EQ(exe[0], '/');
}
@@ -1454,6 +1477,16 @@ TEST(ProcPidExe, Subprocess) {
EXPECT_EQ(actual, expected_absolute_path);
}
+// /proc/PID/cwd points to the correct directory.
+TEST(ProcPidCwd, Subprocess) {
+ auto want = ASSERT_NO_ERRNO_AND_VALUE(GetCWD());
+
+ char got[PATH_MAX + 1] = {};
+ ASSERT_THAT(ReadlinkWhileRunning("cwd", got, sizeof(got)),
+ SyscallSucceedsWithValue(Gt(0)));
+ EXPECT_EQ(got, want);
+}
+
// Test whether /proc/PID/ files can be read for a running process.
TEST(ProcPidFile, SubprocessRunning) {
char buf[1];
diff --git a/test/syscalls/linux/proc_net.cc b/test/syscalls/linux/proc_net.cc
index 4fab097f4..23677e296 100644
--- a/test/syscalls/linux/proc_net.cc
+++ b/test/syscalls/linux/proc_net.cc
@@ -39,6 +39,7 @@ namespace testing {
namespace {
constexpr const char kProcNet[] = "/proc/net";
+constexpr const char kIpForward[] = "/proc/sys/net/ipv4/ip_forward";
TEST(ProcNetSymlinkTarget, FileMode) {
struct stat s;
@@ -515,6 +516,46 @@ TEST(ProcSysNetIpv4Recovery, CanReadAndWrite) {
SyscallSucceedsWithValue(sizeof(kMessage)));
EXPECT_EQ(strcmp(buf, "100\n"), 0);
}
+
+TEST(ProcSysNetIpv4IpForward, Exists) {
+ auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kIpForward, O_RDONLY));
+}
+
+TEST(ProcSysNetIpv4IpForward, DefaultValueEqZero) {
+ // Test is only valid in sandbox. Not hermetic in native tests
+ // running on a arbitrary machine.
+ SKIP_IF(!IsRunningOnGvisor());
+ auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kIpForward, O_RDONLY));
+
+ char buf = 101;
+ EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
+ SyscallSucceedsWithValue(sizeof(buf)));
+
+ EXPECT_EQ(buf, '0') << "unexpected ip_forward: " << buf;
+}
+
+TEST(ProcSysNetIpv4IpForward, CanReadAndWrite) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability((CAP_DAC_OVERRIDE))));
+
+ auto const fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kIpForward, O_RDWR));
+
+ char buf;
+ EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
+ SyscallSucceedsWithValue(sizeof(buf)));
+
+ EXPECT_TRUE(buf == '0' || buf == '1') << "unexpected ip_forward: " << buf;
+
+ // constexpr char to_write = '1';
+ char to_write = (buf == '1') ? '0' : '1';
+ EXPECT_THAT(PwriteFd(fd.get(), &to_write, sizeof(to_write), 0),
+ SyscallSucceedsWithValue(sizeof(to_write)));
+
+ buf = 0;
+ EXPECT_THAT(PreadFd(fd.get(), &buf, sizeof(buf), 0),
+ SyscallSucceedsWithValue(sizeof(buf)));
+ EXPECT_EQ(buf, to_write);
+}
+
} // namespace
} // namespace testing
} // namespace gvisor
diff --git a/test/syscalls/linux/raw_socket_icmp.cc b/test/syscalls/linux/raw_socket_icmp.cc
index 3de898df7..1b9dbc584 100644
--- a/test/syscalls/linux/raw_socket_icmp.cc
+++ b/test/syscalls/linux/raw_socket_icmp.cc
@@ -416,6 +416,28 @@ TEST_F(RawSocketICMPTest, BindConnectSendAndReceive) {
ASSERT_NO_FATAL_FAILURE(ExpectICMPSuccess(icmp));
}
+// Set and get SO_LINGER.
+TEST_F(RawSocketICMPTest, SetAndGetSocketLinger) {
+ SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_RAW)));
+
+ int level = SOL_SOCKET;
+ int type = SO_LINGER;
+
+ struct linger sl;
+ sl.l_onoff = 1;
+ sl.l_linger = 5;
+ ASSERT_THAT(setsockopt(s_, level, type, &sl, sizeof(sl)),
+ SyscallSucceedsWithValue(0));
+
+ struct linger got_linger = {};
+ socklen_t length = sizeof(sl);
+ ASSERT_THAT(getsockopt(s_, level, type, &got_linger, &length),
+ SyscallSucceedsWithValue(0));
+
+ ASSERT_EQ(length, sizeof(got_linger));
+ EXPECT_EQ(0, memcmp(&sl, &got_linger, length));
+}
+
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/rename.cc b/test/syscalls/linux/rename.cc
index 833c0dc4f..5458f54ad 100644
--- a/test/syscalls/linux/rename.cc
+++ b/test/syscalls/linux/rename.cc
@@ -170,6 +170,9 @@ TEST(RenameTest, FileOverwritesFile) {
}
TEST(RenameTest, DirectoryOverwritesDirectoryLinkCount) {
+ // Directory link counts are synthetic on overlay filesystems.
+ SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(IsOverlayfs(GetAbsoluteTestTmpdir())));
+
auto parent1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
EXPECT_THAT(Links(parent1.path()), IsPosixErrorOkAndHolds(2));
diff --git a/test/syscalls/linux/socket_ip_udp_generic.cc b/test/syscalls/linux/socket_ip_udp_generic.cc
index 6e4ecd680..3f2c0fdf2 100644
--- a/test/syscalls/linux/socket_ip_udp_generic.cc
+++ b/test/syscalls/linux/socket_ip_udp_generic.cc
@@ -451,7 +451,7 @@ TEST_P(UDPSocketPairTest, TClassRecvMismatch) {
}
// Test the SO_LINGER option can be set/get on udp socket.
-TEST_P(UDPSocketPairTest, SoLingerFail) {
+TEST_P(UDPSocketPairTest, SetAndGetSocketLinger) {
auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
int level = SOL_SOCKET;
int type = SO_LINGER;
@@ -469,15 +469,7 @@ TEST_P(UDPSocketPairTest, SoLingerFail) {
SyscallSucceedsWithValue(0));
ASSERT_EQ(length, sizeof(got_linger));
- // Linux returns the values which are set in the SetSockOpt for SO_LINGER.
- // In gVisor, we do not store the linger values for UDP as SO_LINGER for UDP
- // is a no-op.
- if (IsRunningOnGvisor()) {
- struct linger want_linger = {};
- EXPECT_EQ(0, memcmp(&want_linger, &got_linger, length));
- } else {
- EXPECT_EQ(0, memcmp(&sl, &got_linger, length));
- }
+ EXPECT_EQ(0, memcmp(&sl, &got_linger, length));
}
} // namespace testing
diff --git a/test/syscalls/linux/socket_ipv4_udp_unbound.cc b/test/syscalls/linux/socket_ipv4_udp_unbound.cc
index 02ea05e22..a72c76c97 100644
--- a/test/syscalls/linux/socket_ipv4_udp_unbound.cc
+++ b/test/syscalls/linux/socket_ipv4_udp_unbound.cc
@@ -27,6 +27,7 @@
#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/test_util.h"
namespace gvisor {
@@ -73,9 +74,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackNoGroup) {
// Check that we did not receive the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- EXPECT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf),
- MSG_DONTWAIT),
- SyscallFailsWithErrno(EAGAIN));
+ EXPECT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ PosixErrorIs(EAGAIN, ::testing::_));
}
// Check that not setting a default send interface prevents multicast packets
@@ -207,8 +208,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackAddr) {
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -262,8 +264,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackNic) {
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -317,8 +320,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddr) {
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -372,8 +376,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNic) {
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -431,8 +436,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddrConnect) {
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -490,8 +496,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicConnect) {
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -545,8 +552,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddrSelf) {
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket1->get(), recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -600,8 +608,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelf) {
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket1->get(), recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -659,9 +668,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddrSelfConnect) {
// Check that we did not receive the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- EXPECT_THAT(RetryEINTR(recv)(socket1->get(), recv_buf, sizeof(recv_buf),
- MSG_DONTWAIT),
- SyscallFailsWithErrno(EAGAIN));
+ EXPECT_THAT(
+ RecvMsgTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ PosixErrorIs(EAGAIN, ::testing::_));
}
// Check that multicast works when the default send interface is configured by
@@ -717,9 +726,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfConnect) {
// Check that we did not receive the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- EXPECT_THAT(RetryEINTR(recv)(socket1->get(), recv_buf, sizeof(recv_buf),
- MSG_DONTWAIT),
- SyscallFailsWithErrno(EAGAIN));
+ EXPECT_THAT(
+ RecvMsgTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ PosixErrorIs(EAGAIN, ::testing::_));
}
// Check that multicast works when the default send interface is configured by
@@ -775,8 +784,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfAddrSelfNoLoop) {
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket1->get(), recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -834,8 +844,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastLoopbackIfNicSelfNoLoop) {
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket1->get(), recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket1->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -907,9 +918,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastDropAddr) {
// Check that we did not receive the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- EXPECT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf),
- MSG_DONTWAIT),
- SyscallFailsWithErrno(EAGAIN));
+ EXPECT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ PosixErrorIs(EAGAIN, ::testing::_));
}
// Check that dropping a group membership prevents multicast packets from being
@@ -965,9 +976,9 @@ TEST_P(IPv4UDPUnboundSocketTest, IpMulticastDropNic) {
// Check that we did not receive the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- EXPECT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf),
- MSG_DONTWAIT),
- SyscallFailsWithErrno(EAGAIN));
+ EXPECT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ PosixErrorIs(EAGAIN, ::testing::_));
}
TEST_P(IPv4UDPUnboundSocketTest, IpMulticastIfZero) {
@@ -1319,9 +1330,9 @@ 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(
- RetryEINTR(recv)(sockets->second_fd(), recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(RecvMsgTimeout(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)));
}
}
@@ -1398,9 +1409,9 @@ 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(
- RetryEINTR(recv)(sockets->second_fd(), recv_buf, sizeof(recv_buf), 0),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(RecvMsgTimeout(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)));
}
}
@@ -1421,9 +1432,9 @@ TEST_P(IPv4UDPUnboundSocketTest, TestMcastReceptionWhenDroppingMemberships) {
char recv_buf[sizeof(send_buf)] = {};
for (auto& sockets : socket_pairs) {
- ASSERT_THAT(RetryEINTR(recv)(sockets->second_fd(), recv_buf,
- sizeof(recv_buf), MSG_DONTWAIT),
- SyscallFailsWithErrno(EAGAIN));
+ ASSERT_THAT(RecvMsgTimeout(sockets->second_fd(), recv_buf,
+ sizeof(recv_buf), 1 /*timeout*/),
+ PosixErrorIs(EAGAIN, ::testing::_));
}
}
}
@@ -1474,9 +1485,9 @@ TEST_P(IPv4UDPUnboundSocketTest, TestBindToMcastThenJoinThenReceive) {
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf),
- MSG_DONTWAIT),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -1518,9 +1529,9 @@ TEST_P(IPv4UDPUnboundSocketTest, TestBindToMcastThenNoJoinThenNoReceive) {
// Check that we don't receive the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf),
- MSG_DONTWAIT),
- SyscallFailsWithErrno(EAGAIN));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ PosixErrorIs(EAGAIN, ::testing::_));
}
// Check that a socket can bind to a multicast address and still send out
@@ -1568,9 +1579,9 @@ TEST_P(IPv4UDPUnboundSocketTest, TestBindToMcastThenSend) {
// Check that we received the packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf),
- MSG_DONTWAIT),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -1615,9 +1626,9 @@ TEST_P(IPv4UDPUnboundSocketTest, TestBindToBcastThenReceive) {
// Check that we received the multicast packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf),
- MSG_DONTWAIT),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -1666,9 +1677,9 @@ TEST_P(IPv4UDPUnboundSocketTest, TestBindToBcastThenSend) {
// Check that we received the packet.
char recv_buf[sizeof(send_buf)] = {};
- ASSERT_THAT(RetryEINTR(recv)(socket2->get(), recv_buf, sizeof(recv_buf),
- MSG_DONTWAIT),
- SyscallSucceedsWithValue(sizeof(recv_buf)));
+ ASSERT_THAT(
+ RecvMsgTimeout(socket2->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(recv_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
@@ -1726,17 +1737,17 @@ 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(
- RetryEINTR(recv)(last->get(), recv_buf, sizeof(recv_buf), MSG_DONTWAIT),
- SyscallSucceedsWithValue(sizeof(send_buf)));
+ RecvMsgTimeout(last->get(), recv_buf, sizeof(recv_buf), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(send_buf)));
EXPECT_EQ(0, memcmp(send_buf, recv_buf, sizeof(send_buf)));
}
// Verify that no other messages were received.
for (auto& socket : sockets) {
char recv_buf[kMessageSize] = {};
- EXPECT_THAT(RetryEINTR(recv)(socket->get(), recv_buf, sizeof(recv_buf),
- MSG_DONTWAIT),
- SyscallFailsWithErrno(EAGAIN));
+ EXPECT_THAT(RecvMsgTimeout(socket->get(), recv_buf, sizeof(recv_buf),
+ 1 /*timeout*/),
+ PosixErrorIs(EAGAIN, ::testing::_));
}
}
@@ -2113,12 +2124,12 @@ TEST_P(IPv4UDPUnboundSocketTest, ReuseAddrReusePortDistribution) {
// balancing (REUSEPORT) instead of the most recently bound socket
// (REUSEADDR).
char recv_buf[kMessageSize] = {};
- EXPECT_THAT(RetryEINTR(recv)(receiver1->get(), recv_buf, sizeof(recv_buf),
- MSG_DONTWAIT),
- SyscallSucceedsWithValue(kMessageSize));
- EXPECT_THAT(RetryEINTR(recv)(receiver2->get(), recv_buf, sizeof(recv_buf),
- MSG_DONTWAIT),
- SyscallSucceedsWithValue(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));
}
// Test that socket will receive packet info control message.
diff --git a/test/syscalls/linux/socket_netlink_route_util.cc b/test/syscalls/linux/socket_netlink_route_util.cc
index a354f3f80..7a0bad4cb 100644
--- a/test/syscalls/linux/socket_netlink_route_util.cc
+++ b/test/syscalls/linux/socket_netlink_route_util.cc
@@ -42,7 +42,7 @@ PosixError PopulateNlmsghdr(LinkAddrModification modification,
return NoError();
case LinkAddrModification::kDelete:
hdr->nlmsg_type = RTM_DELADDR;
- hdr->nlmsg_flags = NLM_F_REQUEST;
+ hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
return NoError();
}
diff --git a/test/syscalls/linux/socket_test_util.cc b/test/syscalls/linux/socket_test_util.cc
index 53b678e94..e11792309 100644
--- a/test/syscalls/linux/socket_test_util.cc
+++ b/test/syscalls/linux/socket_test_util.cc
@@ -753,6 +753,20 @@ 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) {
+ 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(recv)(sock, buf, buf_size, 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 734b48b96..468bc96e0 100644
--- a/test/syscalls/linux/socket_test_util.h
+++ b/test/syscalls/linux/socket_test_util.h
@@ -467,6 +467,10 @@ 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);
+
// 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 99e77b89e..1edcb15a7 100644
--- a/test/syscalls/linux/socket_unix_stream.cc
+++ b/test/syscalls/linux/socket_unix_stream.cc
@@ -103,6 +103,24 @@ TEST_P(StreamUnixSocketPairTest, Sendto) {
SyscallFailsWithErrno(EISCONN));
}
+TEST_P(StreamUnixSocketPairTest, SetAndGetSocketLinger) {
+ auto sockets = ASSERT_NO_ERRNO_AND_VALUE(NewSocketPair());
+
+ struct linger sl = {1, 5};
+ EXPECT_THAT(
+ setsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)),
+ SyscallSucceedsWithValue(0));
+
+ struct linger got_linger = {};
+ socklen_t length = sizeof(sl);
+ EXPECT_THAT(getsockopt(sockets->first_fd(), SOL_SOCKET, SO_LINGER,
+ &got_linger, &length),
+ SyscallSucceedsWithValue(0));
+
+ ASSERT_EQ(length, sizeof(got_linger));
+ EXPECT_EQ(0, memcmp(&got_linger, &sl, length));
+}
+
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 1b2941c37..92260b1e1 100644
--- a/test/syscalls/linux/stat.cc
+++ b/test/syscalls/linux/stat.cc
@@ -333,20 +333,23 @@ TEST_F(StatTest, LeadingDoubleSlash) {
// Test that a rename doesn't change the underlying file.
TEST_F(StatTest, StatDoesntChangeAfterRename) {
- const TempPath old_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
+ const TempPath old_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
const TempPath new_path(NewTempAbsPath());
struct stat st_old = {};
struct stat st_new = {};
- ASSERT_THAT(stat(old_dir.path().c_str(), &st_old), SyscallSucceeds());
- ASSERT_THAT(rename(old_dir.path().c_str(), new_path.path().c_str()),
+ ASSERT_THAT(stat(old_file.path().c_str(), &st_old), SyscallSucceeds());
+ ASSERT_THAT(rename(old_file.path().c_str(), new_path.path().c_str()),
SyscallSucceeds());
ASSERT_THAT(stat(new_path.path().c_str(), &st_new), SyscallSucceeds());
EXPECT_EQ(st_old.st_nlink, st_new.st_nlink);
EXPECT_EQ(st_old.st_dev, st_new.st_dev);
- EXPECT_EQ(st_old.st_ino, st_new.st_ino);
+ // Overlay filesystems may synthesize directory inode numbers on the fly.
+ if (!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);
EXPECT_EQ(st_old.st_uid, st_new.st_uid);
EXPECT_EQ(st_old.st_gid, st_new.st_gid);
@@ -383,7 +386,9 @@ TEST_F(StatTest, LinkCountsWithRegularFileChild) {
// This test verifies that inodes remain around when there is an open fd
// after link count hits 0.
-TEST_F(StatTest, ZeroLinksOpenFdRegularFileChild_NoRandomSave) {
+//
+// It is marked NoSave because we don't support saving unlinked files.
+TEST_F(StatTest, ZeroLinksOpenFdRegularFileChild_NoSave) {
// Setting the enviornment variable GVISOR_GOFER_UNCACHED to any value
// will prevent this test from running, see the tmpfs lifecycle.
//
@@ -392,9 +397,6 @@ TEST_F(StatTest, ZeroLinksOpenFdRegularFileChild_NoRandomSave) {
const char* uncached_gofer = getenv("GVISOR_GOFER_UNCACHED");
SKIP_IF(uncached_gofer != nullptr);
- // We don't support saving unlinked files.
- const DisableSave ds;
-
const TempPath dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
const TempPath child = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith(
dir.path(), "hello", TempPath::kDefaultFileMode));
diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc
index ab731db1d..e0981e28a 100644
--- a/test/syscalls/linux/tcp_socket.cc
+++ b/test/syscalls/linux/tcp_socket.cc
@@ -1643,6 +1643,36 @@ TEST_P(SimpleTcpSocketTest, GetSocketDetachFilter) {
SyscallFailsWithErrno(ENOPROTOOPT));
}
+TEST_P(SimpleTcpSocketTest, CloseNonConnectedLingerOption) {
+ FileDescriptor s =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(GetParam(), SOCK_STREAM, IPPROTO_TCP));
+
+ constexpr int kLingerTimeout = 10; // Seconds.
+
+ // Set the SO_LINGER option.
+ struct linger sl = {
+ .l_onoff = 1,
+ .l_linger = kLingerTimeout,
+ };
+ ASSERT_THAT(setsockopt(s.get(), SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)),
+ SyscallSucceeds());
+
+ struct pollfd poll_fd = {
+ .fd = s.get(),
+ .events = POLLHUP,
+ };
+ constexpr int kPollTimeoutMs = 0;
+ ASSERT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
+ SyscallSucceedsWithValue(1));
+
+ auto const start_time = absl::Now();
+ EXPECT_THAT(close(s.release()), SyscallSucceeds());
+ auto const end_time = absl::Now();
+
+ // Close() should not linger and return immediately.
+ ASSERT_LT((end_time - start_time), absl::Seconds(kLingerTimeout));
+}
+
INSTANTIATE_TEST_SUITE_P(AllInetTests, SimpleTcpSocketTest,
::testing::Values(AF_INET, AF_INET6));
diff --git a/test/syscalls/linux/truncate.cc b/test/syscalls/linux/truncate.cc
index c988c6380..bfc95ed38 100644
--- a/test/syscalls/linux/truncate.cc
+++ b/test/syscalls/linux/truncate.cc
@@ -196,6 +196,26 @@ TEST(TruncateTest, FtruncateNonWriteable) {
EXPECT_THAT(ftruncate(fd.get(), 0), SyscallFailsWithErrno(EINVAL));
}
+// ftruncate(2) should succeed as long as the file descriptor is writeable,
+// regardless of whether the file permissions allow writing.
+TEST(TruncateTest, FtruncateWithoutWritePermission_NoRandomSave) {
+ // Drop capabilities that allow us to override file permissions.
+ ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false));
+
+ // The only time we can open a file with flags forbidden by its permissions
+ // is when we are creating the file. We cannot re-open with the same flags,
+ // so we cannot restore an fd obtained from such an operation.
+ const DisableSave ds;
+ auto path = NewTempAbsPath();
+ const FileDescriptor fd =
+ ASSERT_NO_ERRNO_AND_VALUE(Open(path, O_RDWR | O_CREAT, 0444));
+
+ // In goferfs, ftruncate may be converted to a remote truncate operation that
+ // unavoidably requires write permission.
+ SKIP_IF(IsRunningOnGvisor() && !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(path)));
+ ASSERT_THAT(ftruncate(fd.get(), 100), SyscallSucceeds());
+}
+
TEST(TruncateTest, TruncateNonExist) {
EXPECT_THAT(truncate("/foo/bar", 0), SyscallFailsWithErrno(ENOENT));
}
diff --git a/test/syscalls/linux/udp_socket.cc b/test/syscalls/linux/udp_socket.cc
index 97db2b321..1a7673317 100644
--- a/test/syscalls/linux/udp_socket.cc
+++ b/test/syscalls/linux/udp_socket.cc
@@ -14,6 +14,9 @@
#include <arpa/inet.h>
#include <fcntl.h>
+
+#include <ctime>
+
#ifdef __linux__
#include <linux/errqueue.h>
#include <linux/filter.h>
@@ -834,8 +837,9 @@ TEST_P(UdpSocketTest, ReceiveBeforeConnect) {
// Receive the data. It works because it was sent before the connect.
char received[sizeof(buf)];
- EXPECT_THAT(recv(bind_.get(), received, sizeof(received), 0),
- SyscallSucceedsWithValue(sizeof(received)));
+ EXPECT_THAT(
+ RecvMsgTimeout(bind_.get(), received, sizeof(received), 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(sizeof(received)));
EXPECT_EQ(memcmp(buf, received, sizeof(buf)), 0);
// Send again. This time it should not be received.
@@ -924,7 +928,9 @@ TEST_P(UdpSocketTest, ReadShutdownNonblockPendingData) {
SyscallSucceedsWithValue(1));
// We should get the data even though read has been shutdown.
- EXPECT_THAT(recv(bind_.get(), received, 2, 0), SyscallSucceedsWithValue(2));
+ EXPECT_THAT(
+ RecvMsgTimeout(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.
@@ -1692,9 +1698,9 @@ 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(
- recv(bind_.get(), received.data(), received.size(), MSG_DONTWAIT),
- SyscallSucceedsWithValue(received.size()));
+ EXPECT_THAT(RecvMsgTimeout(bind_.get(), received.data(), received.size(),
+ 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(received.size()));
}
{
@@ -1708,9 +1714,9 @@ TEST_P(UdpSocketTest, RecvBufLimitsEmptyRcvBuf) {
SyscallSucceedsWithValue(buf.size()));
std::vector<char> received(buf.size());
- EXPECT_THAT(
- recv(bind_.get(), received.data(), received.size(), MSG_DONTWAIT),
- SyscallSucceedsWithValue(received.size()));
+ ASSERT_THAT(RecvMsgTimeout(bind_.get(), received.data(), received.size(),
+ 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(received.size()));
}
}
@@ -1779,9 +1785,9 @@ TEST_P(UdpSocketTest, RecvBufLimits) {
for (int i = 0; i < sent - 1; i++) {
// Receive the data.
std::vector<char> received(buf.size());
- EXPECT_THAT(
- recv(bind_.get(), received.data(), received.size(), MSG_DONTWAIT),
- SyscallSucceedsWithValue(received.size()));
+ EXPECT_THAT(RecvMsgTimeout(bind_.get(), received.data(), received.size(),
+ 1 /*timeout*/),
+ IsPosixErrorOkAndHolds(received.size()));
EXPECT_EQ(memcmp(buf.data(), received.data(), buf.size()), 0);
}
diff --git a/test/syscalls/linux/vdso_clock_gettime.cc b/test/syscalls/linux/vdso_clock_gettime.cc
index ce1899f45..2a8699a7b 100644
--- a/test/syscalls/linux/vdso_clock_gettime.cc
+++ b/test/syscalls/linux/vdso_clock_gettime.cc
@@ -38,8 +38,6 @@ std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) {
switch (info.param) {
case CLOCK_MONOTONIC:
return "CLOCK_MONOTONIC";
- case CLOCK_REALTIME:
- return "CLOCK_REALTIME";
case CLOCK_BOOTTIME:
return "CLOCK_BOOTTIME";
default:
@@ -47,59 +45,36 @@ std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) {
}
}
-class CorrectVDSOClockTest : public ::testing::TestWithParam<clockid_t> {};
+class MonotonicVDSOClockTest : public ::testing::TestWithParam<clockid_t> {};
-TEST_P(CorrectVDSOClockTest, IsCorrect) {
+TEST_P(MonotonicVDSOClockTest, IsCorrect) {
+ // The VDSO implementation of clock_gettime() uses the TSC. On KVM, sentry and
+ // application TSCs can be very desynchronized; see
+ // sentry/platform/kvm/kvm.vCPU.setSystemTime().
+ SKIP_IF(GvisorPlatform() == Platform::kKVM);
+
+ // Check that when we alternate readings from the clock_gettime syscall and
+ // the VDSO's implementation, we observe the combined sequence as being
+ // monotonic.
struct timespec tvdso, tsys;
absl::Time vdso_time, sys_time;
- uint64_t total_calls = 0;
-
- // It is expected that 82.5% of clock_gettime calls will be less than 100us
- // skewed from the system time.
- // Unfortunately this is not only influenced by the VDSO clock skew, but also
- // by arbitrary scheduling delays and the like. The test is therefore
- // regularly disabled.
- std::map<absl::Duration, std::tuple<double, uint64_t, uint64_t>> confidence =
- {
- {absl::Microseconds(100), std::make_tuple(0.825, 0, 0)},
- {absl::Microseconds(250), std::make_tuple(0.94, 0, 0)},
- {absl::Milliseconds(1), std::make_tuple(0.999, 0, 0)},
- };
-
- absl::Time start = absl::Now();
- while (absl::Now() < start + absl::Seconds(30)) {
- EXPECT_THAT(clock_gettime(GetParam(), &tvdso), SyscallSucceeds());
- EXPECT_THAT(syscall(__NR_clock_gettime, GetParam(), &tsys),
- SyscallSucceeds());
-
+ ASSERT_THAT(syscall(__NR_clock_gettime, GetParam(), &tsys),
+ SyscallSucceeds());
+ sys_time = absl::TimeFromTimespec(tsys);
+ auto end = absl::Now() + absl::Seconds(10);
+ while (absl::Now() < end) {
+ ASSERT_THAT(clock_gettime(GetParam(), &tvdso), SyscallSucceeds());
vdso_time = absl::TimeFromTimespec(tvdso);
-
- for (auto const& conf : confidence) {
- std::get<1>(confidence[conf.first]) +=
- (sys_time - vdso_time) < conf.first;
- }
-
+ EXPECT_LE(sys_time, vdso_time);
+ ASSERT_THAT(syscall(__NR_clock_gettime, GetParam(), &tsys),
+ SyscallSucceeds());
sys_time = absl::TimeFromTimespec(tsys);
-
- for (auto const& conf : confidence) {
- std::get<2>(confidence[conf.first]) +=
- (vdso_time - sys_time) < conf.first;
- }
-
- ++total_calls;
- }
-
- for (auto const& conf : confidence) {
- EXPECT_GE(std::get<1>(conf.second) / static_cast<double>(total_calls),
- std::get<0>(conf.second));
- EXPECT_GE(std::get<2>(conf.second) / static_cast<double>(total_calls),
- std::get<0>(conf.second));
+ EXPECT_LE(vdso_time, sys_time);
}
}
-INSTANTIATE_TEST_SUITE_P(ClockGettime, CorrectVDSOClockTest,
- ::testing::Values(CLOCK_MONOTONIC, CLOCK_REALTIME,
- CLOCK_BOOTTIME),
+INSTANTIATE_TEST_SUITE_P(ClockGettime, MonotonicVDSOClockTest,
+ ::testing::Values(CLOCK_MONOTONIC, CLOCK_BOOTTIME),
PrintClockId);
} // namespace