summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls
diff options
context:
space:
mode:
Diffstat (limited to 'test/syscalls')
-rw-r--r--test/syscalls/linux/BUILD1
-rw-r--r--test/syscalls/linux/eventfd.cc16
-rw-r--r--test/syscalls/linux/pipe.cc29
-rw-r--r--test/syscalls/linux/proc.cc89
-rw-r--r--test/syscalls/linux/socket.cc61
-rw-r--r--test/syscalls/linux/socket_inet_loopback.cc51
6 files changed, 179 insertions, 68 deletions
diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD
index d9095c95f..837e56042 100644
--- a/test/syscalls/linux/BUILD
+++ b/test/syscalls/linux/BUILD
@@ -365,6 +365,7 @@ cc_binary(
":socket_test_util",
"//test/util:file_descriptor",
gtest,
+ "//test/util:temp_umask",
"//test/util:test_main",
"//test/util:test_util",
],
diff --git a/test/syscalls/linux/eventfd.cc b/test/syscalls/linux/eventfd.cc
index 927001eee..548b05a64 100644
--- a/test/syscalls/linux/eventfd.cc
+++ b/test/syscalls/linux/eventfd.cc
@@ -100,6 +100,22 @@ TEST(EventfdTest, SmallRead) {
ASSERT_THAT(read(efd.get(), &l, 4), SyscallFailsWithErrno(EINVAL));
}
+TEST(EventfdTest, PreadIllegalSeek) {
+ FileDescriptor efd =
+ ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE));
+
+ uint64_t l = 0;
+ ASSERT_THAT(pread(efd.get(), &l, 4, 0), SyscallFailsWithErrno(ESPIPE));
+}
+
+TEST(EventfdTest, PwriteIllegalSeek) {
+ FileDescriptor efd =
+ ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE));
+
+ uint64_t l = 0;
+ ASSERT_THAT(pwrite(efd.get(), &l, 4, 0), SyscallFailsWithErrno(ESPIPE));
+}
+
TEST(EventfdTest, BigWrite) {
FileDescriptor efd =
ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_NONBLOCK | EFD_SEMAPHORE));
diff --git a/test/syscalls/linux/pipe.cc b/test/syscalls/linux/pipe.cc
index 67228b66b..34291850d 100644
--- a/test/syscalls/linux/pipe.cc
+++ b/test/syscalls/linux/pipe.cc
@@ -631,11 +631,14 @@ INSTANTIATE_TEST_SUITE_P(
"namednonblocking",
[](int fds[2], bool* is_blocking, bool* is_namedpipe) {
// Create a new file-based pipe (non-blocking).
- auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- ASSERT_THAT(unlink(file.path().c_str()), SyscallSucceeds());
- SKIP_IF(mkfifo(file.path().c_str(), 0644) != 0);
- fds[0] = open(file.path().c_str(), O_NONBLOCK | O_RDONLY);
- fds[1] = open(file.path().c_str(), O_NONBLOCK | O_WRONLY);
+ std::string path;
+ {
+ auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ path = file.path();
+ }
+ SKIP_IF(mkfifo(path.c_str(), 0644) != 0);
+ fds[0] = open(path.c_str(), O_NONBLOCK | O_RDONLY);
+ fds[1] = open(path.c_str(), O_NONBLOCK | O_WRONLY);
MaybeSave();
*is_blocking = false;
*is_namedpipe = true;
@@ -645,13 +648,15 @@ INSTANTIATE_TEST_SUITE_P(
"namedblocking",
[](int fds[2], bool* is_blocking, bool* is_namedpipe) {
// Create a new file-based pipe (blocking).
- auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
- ASSERT_THAT(unlink(file.path().c_str()), SyscallSucceeds());
- SKIP_IF(mkfifo(file.path().c_str(), 0644) != 0);
- ScopedThread t([&file, &fds]() {
- fds[1] = open(file.path().c_str(), O_WRONLY);
- });
- fds[0] = open(file.path().c_str(), O_RDONLY);
+ std::string path;
+ {
+ auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
+ path = file.path();
+ }
+ SKIP_IF(mkfifo(path.c_str(), 0644) != 0);
+ ScopedThread t(
+ [&path, &fds]() { fds[1] = open(path.c_str(), O_WRONLY); });
+ fds[0] = open(path.c_str(), O_RDONLY);
t.Join();
MaybeSave();
*is_blocking = true;
diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc
index 79a625ebc..63642880a 100644
--- a/test/syscalls/linux/proc.cc
+++ b/test/syscalls/linux/proc.cc
@@ -1939,43 +1939,66 @@ TEST(ProcSelfMounts, RequiredFieldsArePresent) {
}
void CheckDuplicatesRecursively(std::string path) {
- errno = 0;
- DIR* dir = opendir(path.c_str());
- if (dir == nullptr) {
- // Ignore any directories we can't read or missing directories as the
- // directory could have been deleted/mutated from the time the parent
- // directory contents were read.
- return;
- }
- auto dir_closer = Cleanup([&dir]() { closedir(dir); });
- std::unordered_set<std::string> children;
- while (true) {
- // Readdir(3): If the end of the directory stream is reached, NULL is
- // returned and errno is not changed. If an error occurs, NULL is returned
- // and errno is set appropriately. To distinguish end of stream and from an
- // error, set errno to zero before calling readdir() and then check the
- // value of errno if NULL is returned.
+ std::vector<std::string> child_dirs;
+
+ // There is the known issue of the linux procfs, that two consequent calls of
+ // readdir can return the same entry twice if between these calls one or more
+ // entries have been removed from this directory.
+ int max_attempts = 5;
+ for (int i = 0; i < max_attempts; i++) {
+ child_dirs.clear();
errno = 0;
- struct dirent* dp = readdir(dir);
- if (dp == nullptr) {
- ASSERT_EQ(errno, 0) << path;
- break; // We're done.
+ bool success = true;
+ DIR* dir = opendir(path.c_str());
+ if (dir == nullptr) {
+ // Ignore any directories we can't read or missing directories as the
+ // directory could have been deleted/mutated from the time the parent
+ // directory contents were read.
+ return;
}
-
- if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
- continue;
+ auto dir_closer = Cleanup([&dir]() { closedir(dir); });
+ std::unordered_set<std::string> children;
+ while (true) {
+ // Readdir(3): If the end of the directory stream is reached, NULL is
+ // returned and errno is not changed. If an error occurs, NULL is
+ // returned and errno is set appropriately. To distinguish end of stream
+ // and from an error, set errno to zero before calling readdir() and then
+ // check the value of errno if NULL is returned.
+ errno = 0;
+ struct dirent* dp = readdir(dir);
+ if (dp == nullptr) {
+ ASSERT_EQ(errno, 0) << path;
+ break; // We're done.
+ }
+
+ if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
+ continue;
+ }
+
+ // Ignore a duplicate entry if it isn't the last attempt.
+ if (i == max_attempts - 1) {
+ ASSERT_EQ(children.find(std::string(dp->d_name)), children.end())
+ << absl::StrCat(path, "/", dp->d_name);
+ } else if (children.find(std::string(dp->d_name)) != children.end()) {
+ std::cerr << "Duplicate entry: " << i << ":"
+ << absl::StrCat(path, "/", dp->d_name) << std::endl;
+ success = false;
+ break;
+ }
+ children.insert(std::string(dp->d_name));
+
+ ASSERT_NE(dp->d_type, DT_UNKNOWN);
+
+ if (dp->d_type == DT_DIR) {
+ child_dirs.push_back(std::string(dp->d_name));
+ }
}
-
- ASSERT_EQ(children.find(std::string(dp->d_name)), children.end())
- << dp->d_name;
- children.insert(std::string(dp->d_name));
-
- ASSERT_NE(dp->d_type, DT_UNKNOWN);
-
- if (dp->d_type != DT_DIR) {
- continue;
+ if (success) {
+ break;
}
- CheckDuplicatesRecursively(absl::StrCat(path, "/", dp->d_name));
+ }
+ for (auto dname = child_dirs.begin(); dname != child_dirs.end(); dname++) {
+ CheckDuplicatesRecursively(absl::StrCat(path, "/", *dname));
}
}
diff --git a/test/syscalls/linux/socket.cc b/test/syscalls/linux/socket.cc
index 3a07ac8d2..703d594a2 100644
--- a/test/syscalls/linux/socket.cc
+++ b/test/syscalls/linux/socket.cc
@@ -13,11 +13,14 @@
// limitations under the License.
#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
#include "gtest/gtest.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
+#include "test/util/temp_umask.h"
#include "test/util/test_util.h"
namespace gvisor {
@@ -58,11 +61,69 @@ TEST(SocketTest, ProtocolInet) {
}
}
+TEST(SocketTest, UnixSocketFileMode) {
+ // TODO(gvisor.dev/issue/1624): Re-enable this test once VFS1 is deleted. It
+ // should pass in VFS2.
+ SKIP_IF(IsRunningOnGvisor());
+
+ FileDescriptor bound =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, SOCK_STREAM, PF_UNIX));
+
+ // The permissions of the file created with bind(2) should be defined by the
+ // permissions of the bound socket and the umask.
+ mode_t sock_perm = 0765, mask = 0123;
+ ASSERT_THAT(fchmod(bound.get(), sock_perm), SyscallSucceeds());
+ TempUmask m(mask);
+
+ struct sockaddr_un addr =
+ ASSERT_NO_ERRNO_AND_VALUE(UniqueUnixAddr(/*abstract=*/false, AF_UNIX));
+ ASSERT_THAT(bind(bound.get(), reinterpret_cast<struct sockaddr*>(&addr),
+ sizeof(addr)),
+ SyscallSucceeds());
+
+ struct stat statbuf = {};
+ ASSERT_THAT(stat(addr.sun_path, &statbuf), SyscallSucceeds());
+ EXPECT_EQ(statbuf.st_mode, S_IFSOCK | sock_perm & ~mask);
+}
+
+TEST(SocketTest, UnixConnectNeedsWritePerm) {
+ // TODO(gvisor.dev/issue/1624): Re-enable this test once VFS1 is deleted. It
+ // should succeed in VFS2.
+ SKIP_IF(IsRunningOnGvisor());
+
+ FileDescriptor bound =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, SOCK_STREAM, PF_UNIX));
+
+ struct sockaddr_un addr =
+ ASSERT_NO_ERRNO_AND_VALUE(UniqueUnixAddr(/*abstract=*/false, AF_UNIX));
+ ASSERT_THAT(bind(bound.get(), reinterpret_cast<struct sockaddr*>(&addr),
+ sizeof(addr)),
+ SyscallSucceeds());
+ ASSERT_THAT(listen(bound.get(), 1), SyscallSucceeds());
+
+ // Connect should fail without write perms.
+ ASSERT_THAT(chmod(addr.sun_path, 0500), SyscallSucceeds());
+ FileDescriptor client =
+ ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_UNIX, SOCK_STREAM, PF_UNIX));
+ EXPECT_THAT(connect(client.get(), reinterpret_cast<struct sockaddr*>(&addr),
+ sizeof(addr)),
+ SyscallFailsWithErrno(EACCES));
+
+ // Connect should succeed with write perms.
+ ASSERT_THAT(chmod(addr.sun_path, 0200), SyscallSucceeds());
+ EXPECT_THAT(connect(client.get(), reinterpret_cast<struct sockaddr*>(&addr),
+ sizeof(addr)),
+ SyscallSucceeds());
+}
+
using SocketOpenTest = ::testing::TestWithParam<int>;
// UDS cannot be opened.
TEST_P(SocketOpenTest, Unix) {
// FIXME(b/142001530): Open incorrectly succeeds on gVisor.
+ //
+ // TODO(gvisor.dev/issue/1624): Re-enable this test once VFS1 is deleted. It
+ // should succeed in VFS2.
SKIP_IF(IsRunningOnGvisor());
FileDescriptor bound =
diff --git a/test/syscalls/linux/socket_inet_loopback.cc b/test/syscalls/linux/socket_inet_loopback.cc
index 9400ffaeb..fa890ec98 100644
--- a/test/syscalls/linux/socket_inet_loopback.cc
+++ b/test/syscalls/linux/socket_inet_loopback.cc
@@ -162,7 +162,7 @@ TEST_P(DualStackSocketTest, AddressOperations) {
ASSERT_NO_ERRNO(SetAddrPort(
addr.family(), const_cast<sockaddr_storage*>(&addr.addr), 1337));
- EXPECT_THAT(connect(fd.get(), addr_in, addr.addr_len),
+ EXPECT_THAT(RetryEINTR(connect)(fd.get(), addr_in, addr.addr_len),
SyscallSucceeds())
<< GetAddrStr(addr_in);
bound = true;
@@ -353,8 +353,9 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdownListen) {
for (int i = 0; i < kBacklog; i++) {
auto client = ASSERT_NO_ERRNO_AND_VALUE(
Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(connect(client.get(), reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
+ ASSERT_THAT(RetryEINTR(connect)(client.get(),
+ reinterpret_cast<sockaddr*>(&conn_addr),
+ connector.addr_len),
SyscallSucceeds());
}
for (int i = 0; i < kBacklog; i++) {
@@ -397,8 +398,9 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdown) {
for (int i = 0; i < kFDs; i++) {
auto client = ASSERT_NO_ERRNO_AND_VALUE(
Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(connect(client.get(), reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
+ ASSERT_THAT(RetryEINTR(connect)(client.get(),
+ reinterpret_cast<sockaddr*>(&conn_addr),
+ connector.addr_len),
SyscallSucceeds());
ASSERT_THAT(accept(listen_fd.get(), nullptr, nullptr), SyscallSucceeds());
}
@@ -425,8 +427,9 @@ TEST_P(SocketInetLoopbackTest, TCPListenShutdown) {
for (int i = 0; i < kFDs; i++) {
auto client = ASSERT_NO_ERRNO_AND_VALUE(
Socket(connector.family(), SOCK_STREAM, IPPROTO_TCP));
- ASSERT_THAT(connect(client.get(), reinterpret_cast<sockaddr*>(&conn_addr),
- connector.addr_len),
+ ASSERT_THAT(RetryEINTR(connect)(client.get(),
+ reinterpret_cast<sockaddr*>(&conn_addr),
+ connector.addr_len),
SyscallFailsWithErrno(ECONNREFUSED));
}
}
@@ -1824,10 +1827,10 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReserved) {
// Connect to bind an ephemeral port.
const FileDescriptor connected_fd =
ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(
- connect(connected_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- bound_addr_len),
- SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(),
+ reinterpret_cast<sockaddr*>(&bound_addr),
+ bound_addr_len),
+ SyscallSucceeds());
// Get the ephemeral port.
sockaddr_storage connected_addr = {};
@@ -1930,8 +1933,9 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V6EphemeralPortReservedReuseAddr) {
ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
&kSockOptOn, sizeof(kSockOptOn)),
SyscallSucceeds());
- ASSERT_THAT(connect(connected_fd.get(),
- reinterpret_cast<sockaddr*>(&bound_addr), bound_addr_len),
+ ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(),
+ reinterpret_cast<sockaddr*>(&bound_addr),
+ bound_addr_len),
SyscallSucceeds());
// Get the ephemeral port.
@@ -1991,10 +1995,10 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V4MappedEphemeralPortReserved) {
// Connect to bind an ephemeral port.
const FileDescriptor connected_fd =
ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(
- connect(connected_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- bound_addr_len),
- SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(),
+ reinterpret_cast<sockaddr*>(&bound_addr),
+ bound_addr_len),
+ SyscallSucceeds());
// Get the ephemeral port.
sockaddr_storage connected_addr = {};
@@ -2121,8 +2125,9 @@ TEST_P(SocketMultiProtocolInetLoopbackTest,
ASSERT_THAT(setsockopt(connected_fd.get(), SOL_SOCKET, SO_REUSEADDR,
&kSockOptOn, sizeof(kSockOptOn)),
SyscallSucceeds());
- ASSERT_THAT(connect(connected_fd.get(),
- reinterpret_cast<sockaddr*>(&bound_addr), bound_addr_len),
+ ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(),
+ reinterpret_cast<sockaddr*>(&bound_addr),
+ bound_addr_len),
SyscallSucceeds());
// Get the ephemeral port.
@@ -2182,10 +2187,10 @@ TEST_P(SocketMultiProtocolInetLoopbackTest, V4EphemeralPortReserved) {
// Connect to bind an ephemeral port.
const FileDescriptor connected_fd =
ASSERT_NO_ERRNO_AND_VALUE(Socket(test_addr.family(), param.type, 0));
- ASSERT_THAT(
- connect(connected_fd.get(), reinterpret_cast<sockaddr*>(&bound_addr),
- bound_addr_len),
- SyscallSucceeds());
+ ASSERT_THAT(RetryEINTR(connect)(connected_fd.get(),
+ reinterpret_cast<sockaddr*>(&bound_addr),
+ bound_addr_len),
+ SyscallSucceeds());
// Get the ephemeral port.
sockaddr_storage connected_addr = {};