diff options
Diffstat (limited to 'test/syscalls/linux/fcntl.cc')
-rw-r--r-- | test/syscalls/linux/fcntl.cc | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/test/syscalls/linux/fcntl.cc b/test/syscalls/linux/fcntl.cc index 4b581045b..4fa6751ff 100644 --- a/test/syscalls/linux/fcntl.cc +++ b/test/syscalls/linux/fcntl.cc @@ -15,6 +15,7 @@ #include <fcntl.h> #include <signal.h> #include <sys/epoll.h> +#include <sys/mman.h> #include <sys/types.h> #include <syscall.h> #include <unistd.h> @@ -35,10 +36,12 @@ #include "absl/time/clock.h" #include "absl/time/time.h" #include "test/syscalls/linux/socket_test_util.h" +#include "test/util/capability_util.h" #include "test/util/cleanup.h" #include "test/util/eventfd_util.h" #include "test/util/file_descriptor.h" #include "test/util/fs_util.h" +#include "test/util/memory_util.h" #include "test/util/multiprocess_util.h" #include "test/util/posix_error.h" #include "test/util/save_util.h" @@ -204,6 +207,41 @@ PosixErrorOr<Cleanup> SubprocessLock(std::string const& path, bool for_write, return std::move(cleanup); } +TEST(FcntlTest, FcntlDupWithOpath) { + SKIP_IF(IsRunningWithVFS1()); + auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_PATH)); + + int new_fd; + // Dup the descriptor and make sure it's the same file. + EXPECT_THAT(new_fd = fcntl(fd.get(), F_DUPFD, 0), SyscallSucceeds()); + + FileDescriptor nfd = FileDescriptor(new_fd); + ASSERT_NE(fd.get(), nfd.get()); + ASSERT_NO_ERRNO(CheckSameFile(fd, nfd)); + EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(O_PATH)); +} + +TEST(FcntlTest, SetFileStatusFlagWithOpath) { + SKIP_IF(IsRunningWithVFS1()); + TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); + + EXPECT_THAT(fcntl(fd.get(), F_SETFL, 0), SyscallFailsWithErrno(EBADF)); +} + +TEST(FcntlTest, BadFcntlsWithOpath) { + SKIP_IF(IsRunningWithVFS1()); + TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); + + EXPECT_THAT(fcntl(fd.get(), F_SETOWN, 0), SyscallFailsWithErrno(EBADF)); + EXPECT_THAT(fcntl(fd.get(), F_GETOWN, 0), SyscallFailsWithErrno(EBADF)); + + EXPECT_THAT(fcntl(fd.get(), F_SETOWN_EX, 0), SyscallFailsWithErrno(EBADF)); + EXPECT_THAT(fcntl(fd.get(), F_GETOWN_EX, 0), SyscallFailsWithErrno(EBADF)); +} + TEST(FcntlTest, SetCloExecBadFD) { // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag not set. FileDescriptor f = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, 0)); @@ -223,6 +261,32 @@ TEST(FcntlTest, SetCloExec) { ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); } +TEST(FcntlTest, SetCloExecWithOpath) { + SKIP_IF(IsRunningWithVFS1()); + // Open a file descriptor with FD_CLOEXEC descriptor flag not set. + TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); + ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0)); + + // Set the FD_CLOEXEC flag. + ASSERT_THAT(fcntl(fd.get(), F_SETFD, FD_CLOEXEC), SyscallSucceeds()); + ASSERT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC)); +} + +TEST(FcntlTest, DupFDCloExecWithOpath) { + SKIP_IF(IsRunningWithVFS1()); + // Open a file descriptor with FD_CLOEXEC descriptor flag not set. + TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_PATH)); + int nfd; + ASSERT_THAT(nfd = fcntl(fd.get(), F_DUPFD_CLOEXEC, 0), SyscallSucceeds()); + FileDescriptor dup_fd(nfd); + + // Check for the FD_CLOEXEC flag. + ASSERT_THAT(fcntl(dup_fd.get(), F_GETFD), + SyscallSucceedsWithValue(FD_CLOEXEC)); +} + TEST(FcntlTest, ClearCloExec) { // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag set. FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_CLOEXEC)); @@ -264,6 +328,22 @@ TEST(FcntlTest, GetAllFlags) { EXPECT_EQ(rflags, expected); } +// When O_PATH is specified in flags, flag bits other than O_CLOEXEC, +// O_DIRECTORY, and O_NOFOLLOW are ignored. +TEST(FcntlTest, GetOpathFlag) { + SKIP_IF(IsRunningWithVFS1()); + TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + int flags = O_RDWR | O_DIRECT | O_SYNC | O_NONBLOCK | O_APPEND | O_PATH | + O_NOFOLLOW | O_DIRECTORY; + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), flags)); + + int expected = O_PATH | O_NOFOLLOW | O_DIRECTORY; + + int rflags; + EXPECT_THAT(rflags = fcntl(fd.get(), F_GETFL), SyscallSucceeds()); + EXPECT_EQ(rflags, expected); +} + TEST(FcntlTest, SetFlags) { TempPath path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), 0)); @@ -392,6 +472,22 @@ TEST_F(FcntlLockTest, SetLockBadOpenFlagsRead) { EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl1), SyscallFailsWithErrno(EBADF)); } +TEST_F(FcntlLockTest, SetLockWithOpath) { + SKIP_IF(IsRunningWithVFS1()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); + + struct flock fl0; + fl0.l_type = F_WRLCK; + fl0.l_whence = SEEK_SET; + fl0.l_start = 0; + fl0.l_len = 0; // Lock all file + + // Expect that setting a write lock using a Opath file descriptor + // won't work. + EXPECT_THAT(fcntl(fd.get(), F_SETLK, &fl0), SyscallFailsWithErrno(EBADF)); +} + TEST_F(FcntlLockTest, SetLockUnlockOnNothing) { auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); FileDescriptor fd = @@ -1642,6 +1738,202 @@ TEST(FcntlTest, SetFlSetOwnSetSigDoNotRace) { } } +TEST_F(FcntlLockTest, GetLockOnNothing) { + SKIP_IF(IsRunningWithVFS1()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds()); + ASSERT_TRUE(fl.l_type == F_UNLCK); +} + +TEST_F(FcntlLockTest, GetLockOnLockSameProcess) { + SKIP_IF(IsRunningWithVFS1()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds()); + ASSERT_TRUE(fl.l_type == F_UNLCK); + + fl.l_type = F_WRLCK; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + ASSERT_THAT(fcntl(fd.get(), F_GETLK, &fl), SyscallSucceeds()); + ASSERT_TRUE(fl.l_type == F_UNLCK); +} + +TEST_F(FcntlLockTest, GetReadLockOnReadLock) { + SKIP_IF(IsRunningWithVFS1()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + pid_t child_pid = fork(); + if (child_pid == 0) { + TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); + TEST_CHECK(fl.l_type == F_UNLCK); + _exit(0); + } + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); +} + +TEST_F(FcntlLockTest, GetReadLockOnWriteLock) { + SKIP_IF(IsRunningWithVFS1()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + fl.l_type = F_RDLCK; + pid_t child_pid = fork(); + if (child_pid == 0) { + TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); + TEST_CHECK(fl.l_type == F_WRLCK); + TEST_CHECK(fl.l_pid == getppid()); + _exit(0); + } + + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); +} + +TEST_F(FcntlLockTest, GetWriteLockOnReadLock) { + SKIP_IF(IsRunningWithVFS1()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_RDLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + fl.l_type = F_WRLCK; + pid_t child_pid = fork(); + if (child_pid == 0) { + TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); + TEST_CHECK(fl.l_type == F_RDLCK); + TEST_CHECK(fl.l_pid == getppid()); + _exit(0); + } + + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); +} + +TEST_F(FcntlLockTest, GetWriteLockOnWriteLock) { + SKIP_IF(IsRunningWithVFS1()); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_RDWR, 0666)); + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + pid_t child_pid = fork(); + if (child_pid == 0) { + TEST_CHECK(fcntl(fd.get(), F_GETLK, &fl) >= 0); + TEST_CHECK(fl.l_type == F_WRLCK); + TEST_CHECK(fl.l_pid == getppid()); + _exit(0); + } + + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); +} + +// Tests that the pid returned from F_GETLK is relative to the caller's PID +// namespace. +TEST_F(FcntlLockTest, GetLockRespectsPIDNamespace) { + SKIP_IF(IsRunningWithVFS1()); + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); + std::string filename = file.path(); + const FileDescriptor fd = + ASSERT_NO_ERRNO_AND_VALUE(Open(filename, O_RDWR, 0666)); + + // Lock in the parent process. + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + ASSERT_THAT(fcntl(fd.get(), F_SETLK, &fl), SyscallSucceeds()); + + auto child_getlk = [](void* filename) { + int fd = open((char*)filename, O_RDWR, 0666); + TEST_CHECK(fd >= 0); + + struct flock fl; + fl.l_type = F_WRLCK; + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 40; + TEST_CHECK(fcntl(fd, F_GETLK, &fl) >= 0); + TEST_CHECK(fl.l_type == F_WRLCK); + // Parent PID should be 0 in the child PID namespace. + TEST_CHECK(fl.l_pid == 0); + close(fd); + return 0; + }; + + // Set up child process in a new PID namespace. + constexpr int kStackSize = 4096; + Mapping stack = ASSERT_NO_ERRNO_AND_VALUE( + Mmap(nullptr, kStackSize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0)); + pid_t child_pid; + ASSERT_THAT( + child_pid = clone(child_getlk, (char*)stack.ptr() + stack.len(), + CLONE_NEWPID | SIGCHLD, (void*)filename.c_str()), + SyscallSucceeds()); + + int status; + ASSERT_THAT(waitpid(child_pid, &status, 0), + SyscallSucceedsWithValue(child_pid)); + ASSERT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0); +} + } // namespace } // namespace testing |