diff options
Diffstat (limited to 'test/syscalls')
38 files changed, 439 insertions, 267 deletions
diff --git a/test/syscalls/BUILD b/test/syscalls/BUILD index 0435f61a2..85412f54b 100644 --- a/test/syscalls/BUILD +++ b/test/syscalls/BUILD @@ -313,6 +313,10 @@ syscall_test( ) syscall_test( + test = "//test/syscalls/linux:verity_mmap_test", +) + +syscall_test( add_overlay = True, test = "//test/syscalls/linux:mount_test", ) diff --git a/test/syscalls/linux/32bit.cc b/test/syscalls/linux/32bit.cc index 6080a59b7..cbf1b4f05 100644 --- a/test/syscalls/linux/32bit.cc +++ b/test/syscalls/linux/32bit.cc @@ -240,9 +240,9 @@ TEST(Call32Bit, Disallowed) { } } -} // namespace - #endif +} // namespace + } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 1ff001426..c7d0f9036 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -1024,6 +1024,7 @@ cc_binary( "//test/util:temp_path", "//test/util:test_main", "//test/util:test_util", + "//test/util:verity_util", ], ) @@ -1294,6 +1295,23 @@ cc_binary( ) cc_binary( + name = "verity_mmap_test", + testonly = 1, + srcs = ["verity_mmap.cc"], + linkstatic = 1, + deps = [ + "//test/util:capability_util", + gtest, + "//test/util:fs_util", + "//test/util:memory_util", + "//test/util:temp_path", + "//test/util:test_main", + "//test/util:test_util", + "//test/util:verity_util", + ], +) + +cc_binary( name = "mount_test", testonly = 1, srcs = ["mount.cc"], @@ -4248,10 +4266,12 @@ cc_binary( "//test/util:mount_util", "@com_google_absl//absl/strings", gtest, + "//test/util:cleanup", "//test/util:posix_error", "//test/util:temp_path", "//test/util:test_main", "//test/util:test_util", + "//test/util:thread_util", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/container:flat_hash_set", ], diff --git a/test/syscalls/linux/cgroup.cc b/test/syscalls/linux/cgroup.cc index 70ad5868f..a009ade7e 100644 --- a/test/syscalls/linux/cgroup.cc +++ b/test/syscalls/linux/cgroup.cc @@ -25,9 +25,11 @@ #include "absl/strings/str_split.h" #include "test/util/capability_util.h" #include "test/util/cgroup_util.h" +#include "test/util/cleanup.h" #include "test/util/mount_util.h" #include "test/util/temp_path.h" #include "test/util/test_util.h" +#include "test/util/thread_util.h" namespace gvisor { namespace testing { @@ -192,6 +194,56 @@ TEST(Cgroup, MoptAllMustBeExclusive) { SyscallFailsWithErrno(EINVAL)); } +TEST(Cgroup, MountRace) { + SKIP_IF(!CgroupsAvailable()); + + TempPath mountpoint = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + + const DisableSave ds; // Too many syscalls. + + auto mount_thread = [&mountpoint]() { + for (int i = 0; i < 100; ++i) { + mount("none", mountpoint.path().c_str(), "cgroup", 0, 0); + } + }; + std::list<ScopedThread> threads; + for (int i = 0; i < 10; ++i) { + threads.emplace_back(mount_thread); + } + for (auto& t : threads) { + t.Join(); + } + + auto cleanup = Cleanup([&mountpoint] { + // We need 1 umount call per successful mount. If some of the mount calls + // were unsuccessful, their corresponding umount will silently fail. + for (int i = 0; i < (10 * 100) + 1; ++i) { + umount(mountpoint.path().c_str()); + } + }); + + Cgroup c = Cgroup(mountpoint.path()); + // c should be a valid cgroup. + EXPECT_NO_ERRNO(c.ContainsCallingProcess()); +} + +TEST(Cgroup, UnmountRepeated) { + SKIP_IF(!CgroupsAvailable()); + + const DisableSave ds; // Too many syscalls. + + Mounter m(ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir())); + Cgroup c = ASSERT_NO_ERRNO_AND_VALUE(m.MountCgroupfs("")); + + // First unmount should succeed. + EXPECT_THAT(umount(c.Path().c_str()), SyscallSucceeds()); + + // We just manually unmounted, so release managed resources. + m.release(c); + + EXPECT_THAT(umount(c.Path().c_str()), SyscallFailsWithErrno(EINVAL)); +} + TEST(MemoryCgroup, MemoryUsageInBytes) { SKIP_IF(!CgroupsAvailable()); diff --git a/test/syscalls/linux/chdir.cc b/test/syscalls/linux/chdir.cc index 3182c228b..3c64b9eab 100644 --- a/test/syscalls/linux/chdir.cc +++ b/test/syscalls/linux/chdir.cc @@ -41,8 +41,8 @@ TEST(ChdirTest, Success) { TEST(ChdirTest, PermissionDenied) { // Drop capabilities that allow us to override directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE( TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0666 /* mode */)); diff --git a/test/syscalls/linux/chmod.cc b/test/syscalls/linux/chmod.cc index 4a5ea84d4..dd82c5fb1 100644 --- a/test/syscalls/linux/chmod.cc +++ b/test/syscalls/linux/chmod.cc @@ -33,7 +33,7 @@ namespace { TEST(ChmodTest, ChmodFileSucceeds) { // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + AutoCapability cap(CAP_DAC_OVERRIDE, false); auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); @@ -43,8 +43,8 @@ TEST(ChmodTest, ChmodFileSucceeds) { TEST(ChmodTest, ChmodDirSucceeds) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); const std::string fileInDir = NewTempAbsPathInDir(dir.path()); @@ -55,7 +55,7 @@ TEST(ChmodTest, ChmodDirSucceeds) { TEST(ChmodTest, FchmodFileSucceeds) { // Drop capabilities that allow us to file directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + AutoCapability cap(CAP_DAC_OVERRIDE, false); auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666)); int fd; @@ -72,8 +72,8 @@ TEST(ChmodTest, FchmodFileSucceeds) { TEST(ChmodTest, FchmodDirSucceeds) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); int fd; @@ -118,7 +118,7 @@ TEST(ChmodTest, FchmodDirWithOpath) { TEST(ChmodTest, FchmodatWithOpath) { SKIP_IF(IsRunningWithVFS1()); // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + AutoCapability cap(CAP_DAC_OVERRIDE, false); auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); @@ -140,7 +140,7 @@ TEST(ChmodTest, FchmodatNotDir) { TEST(ChmodTest, FchmodatFileAbsolutePath) { // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + AutoCapability cap(CAP_DAC_OVERRIDE, false); auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); @@ -150,8 +150,8 @@ TEST(ChmodTest, FchmodatFileAbsolutePath) { TEST(ChmodTest, FchmodatDirAbsolutePath) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); @@ -167,7 +167,7 @@ TEST(ChmodTest, FchmodatDirAbsolutePath) { TEST(ChmodTest, FchmodatFile) { // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + AutoCapability cap(CAP_DAC_OVERRIDE, false); auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); @@ -188,8 +188,8 @@ TEST(ChmodTest, FchmodatFile) { TEST(ChmodTest, FchmodatDir) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); @@ -227,8 +227,8 @@ TEST(ChmodTest, ChmodDowngradeWritability) { TEST(ChmodTest, ChmodFileToNoPermissionsSucceeds) { // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666)); @@ -254,8 +254,8 @@ TEST(ChmodTest, FchmodDowngradeWritability) { TEST(ChmodTest, FchmodFileToNoPermissionsSucceeds) { // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0666)); diff --git a/test/syscalls/linux/chown.cc b/test/syscalls/linux/chown.cc index ff0d39343..b0c1b6f4a 100644 --- a/test/syscalls/linux/chown.cc +++ b/test/syscalls/linux/chown.cc @@ -91,9 +91,7 @@ using Chown = class ChownParamTest : public ::testing::TestWithParam<Chown> {}; TEST_P(ChownParamTest, ChownFileSucceeds) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_CHOWN))) { - ASSERT_NO_ERRNO(SetCapability(CAP_CHOWN, false)); - } + AutoCapability cap(CAP_CHOWN, false); const auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); @@ -135,9 +133,7 @@ TEST_P(ChownParamTest, ChownFilePermissionDenied) { // thread won't be able to open some log files after the test ends. ScopedThread([&] { // Drop privileges. - if (HaveCapability(CAP_CHOWN).ValueOrDie()) { - EXPECT_NO_ERRNO(SetCapability(CAP_CHOWN, false)); - } + AutoCapability cap(CAP_CHOWN, false); // Change EUID and EGID. // diff --git a/test/syscalls/linux/epoll.cc b/test/syscalls/linux/epoll.cc index b180f633c..af3d27894 100644 --- a/test/syscalls/linux/epoll.cc +++ b/test/syscalls/linux/epoll.cc @@ -39,6 +39,15 @@ namespace { constexpr int kFDsPerEpoll = 3; constexpr uint64_t kMagicConstant = 0x0102030405060708; +#ifndef SYS_epoll_pwait2 +#define SYS_epoll_pwait2 441 +#endif + +int epoll_pwait2(int fd, struct epoll_event* events, int maxevents, + const struct timespec* timeout, const sigset_t* sigset) { + return syscall(SYS_epoll_pwait2, fd, events, maxevents, timeout, sigset); +} + TEST(EpollTest, AllWritable) { auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); std::vector<FileDescriptor> eventfds; @@ -144,6 +153,50 @@ TEST(EpollTest, Timeout) { EXPECT_GT(ms_elapsed(begin, end), kTimeoutMs - 1); } +TEST(EpollTest, EpollPwait2Timeout) { + auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); + // 200 milliseconds. + constexpr int kTimeoutNs = 200000000; + struct timespec timeout; + timeout.tv_sec = 0; + timeout.tv_nsec = 0; + struct timespec begin; + struct timespec end; + struct epoll_event result[kFDsPerEpoll]; + + std::vector<FileDescriptor> eventfds; + for (int i = 0; i < kFDsPerEpoll; i++) { + eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); + ASSERT_NO_ERRNO(RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN, + kMagicConstant + i)); + } + + // Pass valid arguments so that the syscall won't be blocked indefinitely + // nor return errno EINVAL. + // + // The syscall returns immediately when timeout is zero, + // even if no events are available. + SKIP_IF(!IsRunningOnGvisor() && + epoll_pwait2(epollfd.get(), result, kFDsPerEpoll, &timeout, nullptr) < + 0 && + errno == ENOSYS); + + { + const DisableSave ds; // Timing-related. + EXPECT_THAT(clock_gettime(CLOCK_MONOTONIC, &begin), SyscallSucceeds()); + + timeout.tv_nsec = kTimeoutNs; + ASSERT_THAT(RetryEINTR(epoll_pwait2)(epollfd.get(), result, kFDsPerEpoll, + &timeout, nullptr), + SyscallSucceedsWithValue(0)); + EXPECT_THAT(clock_gettime(CLOCK_MONOTONIC, &end), SyscallSucceeds()); + } + + // Check the lower bound on the timeout. Checking for an upper bound is + // fragile because Linux can overrun the timeout due to scheduling delays. + EXPECT_GT(ns_elapsed(begin, end), kTimeoutNs - 1); +} + void* writer(void* arg) { int fd = *reinterpret_cast<int*>(arg); uint64_t tmp = 1; diff --git a/test/syscalls/linux/fchdir.cc b/test/syscalls/linux/fchdir.cc index c6675802d..0383f3f85 100644 --- a/test/syscalls/linux/fchdir.cc +++ b/test/syscalls/linux/fchdir.cc @@ -46,8 +46,8 @@ TEST(FchdirTest, InvalidFD) { TEST(FchdirTest, PermissionDenied) { // Drop capabilities that allow us to override directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto temp_dir = ASSERT_NO_ERRNO_AND_VALUE( TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0666 /* mode */)); diff --git a/test/syscalls/linux/fcntl.cc b/test/syscalls/linux/fcntl.cc index 4fa6751ff..91526572b 100644 --- a/test/syscalls/linux/fcntl.cc +++ b/test/syscalls/linux/fcntl.cc @@ -390,9 +390,7 @@ TEST_F(FcntlLockTest, SetLockDir) { } TEST_F(FcntlLockTest, SetLockSymlink) { - // TODO(gvisor.dev/issue/2782): Replace with IsRunningWithVFS1() when O_PATH - // is supported. - SKIP_IF(IsRunningOnGvisor()); + SKIP_IF(IsRunningWithVFS1()); auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); auto symlink = ASSERT_NO_ERRNO_AND_VALUE( diff --git a/test/syscalls/linux/flock.cc b/test/syscalls/linux/flock.cc index fd387aa45..10dad042f 100644 --- a/test/syscalls/linux/flock.cc +++ b/test/syscalls/linux/flock.cc @@ -662,9 +662,7 @@ TEST(FlockTestNoFixture, FlockDir) { } TEST(FlockTestNoFixture, FlockSymlink) { - // TODO(gvisor.dev/issue/2782): Replace with IsRunningWithVFS1() when O_PATH - // is supported. - SKIP_IF(IsRunningOnGvisor()); + SKIP_IF(IsRunningWithVFS1()); auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); auto symlink = ASSERT_NO_ERRNO_AND_VALUE( diff --git a/test/syscalls/linux/memory_accounting.cc b/test/syscalls/linux/memory_accounting.cc index 94aea4077..867a4513b 100644 --- a/test/syscalls/linux/memory_accounting.cc +++ b/test/syscalls/linux/memory_accounting.cc @@ -83,7 +83,7 @@ TEST(MemoryAccounting, AnonAccountingPreservedOnSaveRestore) { uint64_t anon_after_alloc = ASSERT_NO_ERRNO_AND_VALUE(AnonUsageFromMeminfo()); EXPECT_THAT(anon_after_alloc, - EquivalentWithin(anon_initial + map_bytes, 0.03)); + EquivalentWithin(anon_initial + map_bytes, 0.04)); // We have many implicit S/R cycles from scraping /proc/meminfo throughout the // test, but throw an explicit S/R in here as well. @@ -91,7 +91,7 @@ TEST(MemoryAccounting, AnonAccountingPreservedOnSaveRestore) { // Usage should remain the same across S/R. uint64_t anon_after_sr = ASSERT_NO_ERRNO_AND_VALUE(AnonUsageFromMeminfo()); - EXPECT_THAT(anon_after_sr, EquivalentWithin(anon_after_alloc, 0.03)); + EXPECT_THAT(anon_after_sr, EquivalentWithin(anon_after_alloc, 0.04)); } } // namespace diff --git a/test/syscalls/linux/mkdir.cc b/test/syscalls/linux/mkdir.cc index 11fbfa5c5..36504fe6d 100644 --- a/test/syscalls/linux/mkdir.cc +++ b/test/syscalls/linux/mkdir.cc @@ -72,8 +72,8 @@ TEST_F(MkdirTest, HonorsUmask2) { TEST_F(MkdirTest, FailsOnDirWithoutWritePerms) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); ASSERT_THAT(mkdir(dirname_.c_str(), 0555), SyscallSucceeds()); auto dir = JoinPath(dirname_.c_str(), "foo"); @@ -84,8 +84,8 @@ TEST_F(MkdirTest, FailsOnDirWithoutWritePerms) { TEST_F(MkdirTest, DirAlreadyExists) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); ASSERT_THAT(mkdir(dirname_.c_str(), 0777), SyscallSucceeds()); auto dir = JoinPath(dirname_.c_str(), "foo"); diff --git a/test/syscalls/linux/mlock.cc b/test/syscalls/linux/mlock.cc index 78ac96bed..dfa5b7133 100644 --- a/test/syscalls/linux/mlock.cc +++ b/test/syscalls/linux/mlock.cc @@ -114,9 +114,7 @@ TEST(MlockTest, Fork) { } TEST(MlockTest, RlimitMemlockZero) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_IPC_LOCK))) { - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_LOCK, false)); - } + AutoCapability cap(CAP_IPC_LOCK, false); Cleanup reset_rlimit = ASSERT_NO_ERRNO_AND_VALUE(ScopedSetSoftRlimit(RLIMIT_MEMLOCK, 0)); auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( @@ -127,9 +125,7 @@ TEST(MlockTest, RlimitMemlockZero) { } TEST(MlockTest, RlimitMemlockInsufficient) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_IPC_LOCK))) { - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_LOCK, false)); - } + AutoCapability cap(CAP_IPC_LOCK, false); Cleanup reset_rlimit = ASSERT_NO_ERRNO_AND_VALUE(ScopedSetSoftRlimit(RLIMIT_MEMLOCK, kPageSize)); auto const mapping = ASSERT_NO_ERRNO_AND_VALUE( @@ -255,9 +251,7 @@ TEST(MapLockedTest, Basic) { } TEST(MapLockedTest, RlimitMemlockZero) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_IPC_LOCK))) { - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_LOCK, false)); - } + AutoCapability cap(CAP_IPC_LOCK, false); Cleanup reset_rlimit = ASSERT_NO_ERRNO_AND_VALUE(ScopedSetSoftRlimit(RLIMIT_MEMLOCK, 0)); EXPECT_THAT( @@ -266,9 +260,7 @@ TEST(MapLockedTest, RlimitMemlockZero) { } TEST(MapLockedTest, RlimitMemlockInsufficient) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_IPC_LOCK))) { - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_LOCK, false)); - } + AutoCapability cap(CAP_IPC_LOCK, false); Cleanup reset_rlimit = ASSERT_NO_ERRNO_AND_VALUE(ScopedSetSoftRlimit(RLIMIT_MEMLOCK, kPageSize)); EXPECT_THAT( @@ -298,9 +290,7 @@ TEST(MremapLockedTest, RlimitMemlockZero) { MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_LOCKED)); EXPECT_TRUE(IsPageMlocked(mapping.addr())); - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_IPC_LOCK))) { - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_LOCK, false)); - } + AutoCapability cap(CAP_IPC_LOCK, false); Cleanup reset_rlimit = ASSERT_NO_ERRNO_AND_VALUE(ScopedSetSoftRlimit(RLIMIT_MEMLOCK, 0)); void* addr = mremap(mapping.ptr(), mapping.len(), 2 * mapping.len(), @@ -315,9 +305,7 @@ TEST(MremapLockedTest, RlimitMemlockInsufficient) { MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_LOCKED)); EXPECT_TRUE(IsPageMlocked(mapping.addr())); - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_IPC_LOCK))) { - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_LOCK, false)); - } + AutoCapability cap(CAP_IPC_LOCK, false); Cleanup reset_rlimit = ASSERT_NO_ERRNO_AND_VALUE( ScopedSetSoftRlimit(RLIMIT_MEMLOCK, mapping.len())); void* addr = mremap(mapping.ptr(), mapping.len(), 2 * mapping.len(), diff --git a/test/syscalls/linux/mount.cc b/test/syscalls/linux/mount.cc index 92d0fb05c..3c7311782 100644 --- a/test/syscalls/linux/mount.cc +++ b/test/syscalls/linux/mount.cc @@ -45,6 +45,7 @@ namespace testing { namespace { +using ::testing::AnyOf; using ::testing::Contains; using ::testing::Pair; @@ -360,7 +361,8 @@ TEST(MountTest, MountInfo) { if (e.mount_point == dir.path()) { EXPECT_EQ(e.fstype, "tmpfs"); auto mopts = ParseMountOptions(e.mount_opts); - EXPECT_THAT(mopts, Contains(Pair("mode", "0123"))); + EXPECT_THAT(mopts, AnyOf(Contains(Pair("mode", "0123")), + Contains(Pair("mode", "123")))); } } @@ -371,7 +373,8 @@ TEST(MountTest, MountInfo) { if (e.mount_point == dir.path()) { EXPECT_EQ(e.fstype, "tmpfs"); auto mopts = ParseMountOptions(e.super_opts); - EXPECT_THAT(mopts, Contains(Pair("mode", "0123"))); + EXPECT_THAT(mopts, AnyOf(Contains(Pair("mode", "0123")), + Contains(Pair("mode", "123")))); } } } diff --git a/test/syscalls/linux/open.cc b/test/syscalls/linux/open.cc index 4697c404c..ab9d19fef 100644 --- a/test/syscalls/linux/open.cc +++ b/test/syscalls/linux/open.cc @@ -433,7 +433,7 @@ TEST_F(OpenTest, CanTruncateReadOnly) { // O_TRUNC should fail. TEST_F(OpenTest, CanTruncateReadOnlyNoWritePermission) { // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + AutoCapability cap(CAP_DAC_OVERRIDE, false); const DisableSave ds; // Permissions are dropped. ASSERT_THAT(chmod(test_file_name_.c_str(), S_IRUSR | S_IRGRP), @@ -473,8 +473,8 @@ TEST_F(OpenTest, CanTruncateWriteOnlyNoReadPermission) { } TEST_F(OpenTest, CanTruncateWithStrangePermissions) { - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); const DisableSave ds; // Permissions are dropped. std::string path = NewTempAbsPath(); // Create a file without user permissions. @@ -510,8 +510,8 @@ TEST_F(OpenTest, OpenWithStrangeFlags) { TEST_F(OpenTest, OpenWithOpath) { SKIP_IF(IsRunningWithVFS1()); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); const DisableSave ds; // Permissions are dropped. std::string path = NewTempAbsPath(); diff --git a/test/syscalls/linux/open_create.cc b/test/syscalls/linux/open_create.cc index 43d446926..177bda54d 100644 --- a/test/syscalls/linux/open_create.cc +++ b/test/syscalls/linux/open_create.cc @@ -93,7 +93,8 @@ TEST(CreateTest, CreatFileWithOTruncAndReadOnly) { TEST(CreateTest, CreateFailsOnDirWithoutWritePerms) { // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to // always override directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + AutoCapability cap(CAP_DAC_OVERRIDE, false); + auto parent = ASSERT_NO_ERRNO_AND_VALUE( TempPath::CreateDirWith(GetAbsoluteTestTmpdir(), 0555)); auto file = JoinPath(parent.path(), "foo"); @@ -123,8 +124,8 @@ TEST(CreateTest, ChmodReadToWriteBetweenOpens) { // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to // override file read/write permissions. CAP_DAC_READ_SEARCH needs to be // cleared for the same reason. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0400)); @@ -152,7 +153,7 @@ TEST(CreateTest, ChmodReadToWriteBetweenOpens) { TEST(CreateTest, ChmodWriteToReadBetweenOpens) { // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to // override file read/write permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + AutoCapability cap(CAP_DAC_OVERRIDE, false); const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileMode(0200)); @@ -186,8 +187,8 @@ TEST(CreateTest, CreateWithReadFlagNotAllowedByMode) { // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to // override file read/write permissions. CAP_DAC_READ_SEARCH needs to be // cleared for the same reason. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); // Create and open a file with read flag but without read permissions. const std::string path = NewTempAbsPath(); @@ -212,7 +213,7 @@ TEST(CreateTest, CreateWithWriteFlagNotAllowedByMode) { // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to // override file read/write permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + AutoCapability cap(CAP_DAC_OVERRIDE, false); // Create and open a file with write flag but without write permissions. const std::string path = NewTempAbsPath(); diff --git a/test/syscalls/linux/prctl.cc b/test/syscalls/linux/prctl.cc index f675dc430..19a57d353 100644 --- a/test/syscalls/linux/prctl.cc +++ b/test/syscalls/linux/prctl.cc @@ -184,10 +184,8 @@ TEST(PrctlTest, PDeathSig) { // This test is to validate that calling prctl with PR_SET_MM without the // CAP_SYS_RESOURCE returns EPERM. TEST(PrctlTest, InvalidPrSetMM) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_RESOURCE))) { - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_RESOURCE, - false)); // Drop capability to test below. - } + // Drop capability to test below. + AutoCapability cap(CAP_SYS_RESOURCE, false); ASSERT_THAT(prctl(PR_SET_MM, 0, 0, 0, 0), SyscallFailsWithErrno(EPERM)); } diff --git a/test/syscalls/linux/proc.cc b/test/syscalls/linux/proc.cc index 9e48fbca5..24928d876 100644 --- a/test/syscalls/linux/proc.cc +++ b/test/syscalls/linux/proc.cc @@ -1201,6 +1201,15 @@ TEST(ProcSelfCwd, Absolute) { EXPECT_EQ(exe[0], '/'); } +// Sanity check that /proc/cmdline is present. +TEST(ProcCmdline, IsPresent) { + SKIP_IF(IsRunningWithVFS1()); + + std::string proc_cmdline = + ASSERT_NO_ERRNO_AND_VALUE(GetContents("/proc/cmdline")); + ASSERT_FALSE(proc_cmdline.empty()); +} + // Sanity check for /proc/cpuinfo fields that must be present. TEST(ProcCpuinfo, RequiredFieldsArePresent) { std::string proc_cpuinfo = @@ -1849,8 +1858,8 @@ TEST(ProcPidSymlink, SubprocessRunning) { } TEST(ProcPidSymlink, SubprocessZombied) { - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); char buf[1]; @@ -2252,7 +2261,7 @@ TEST(ProcTask, VerifyTaskDir) { TEST(ProcTask, TaskDirCannotBeDeleted) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + AutoCapability cap(CAP_DAC_OVERRIDE, false); EXPECT_THAT(rmdir("/proc/self/task"), SyscallFails()); EXPECT_THAT(rmdir(absl::StrCat("/proc/self/task/", getpid()).c_str()), @@ -2698,6 +2707,14 @@ TEST(Proc, Statfs) { EXPECT_EQ(st.f_namelen, NAME_MAX); } +// Tests that /proc/[pid]/fd/[num] can resolve to a path inside /proc. +TEST(Proc, ResolveSymlinkToProc) { + const auto proc = ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/cmdline", 0)); + const auto path = JoinPath("/proc/self/fd/", absl::StrCat(proc.get())); + const auto target = ASSERT_NO_ERRNO_AND_VALUE(ReadLink(path)); + EXPECT_EQ(target, JoinPath("/proc/", absl::StrCat(getpid()), "/cmdline")); +} + } // namespace } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/ptrace.cc b/test/syscalls/linux/ptrace.cc index 2d9fec371..d519b65e6 100644 --- a/test/syscalls/linux/ptrace.cc +++ b/test/syscalls/linux/ptrace.cc @@ -175,7 +175,7 @@ TEST(PtraceTest, AttachSameThreadGroup) { TEST(PtraceTest, TraceParentNotAllowed) { SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) < 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); + AutoCapability cap(CAP_SYS_PTRACE, false); pid_t const child_pid = fork(); if (child_pid == 0) { @@ -193,7 +193,7 @@ TEST(PtraceTest, TraceParentNotAllowed) { TEST(PtraceTest, TraceNonDescendantNotAllowed) { SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) < 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); + AutoCapability cap(CAP_SYS_PTRACE, false); pid_t const tracee_pid = fork(); if (tracee_pid == 0) { @@ -259,7 +259,7 @@ TEST(PtraceTest, TraceNonDescendantWithCapabilityAllowed) { TEST(PtraceTest, TraceDescendantsAllowed) { SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) > 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); + AutoCapability cap(CAP_SYS_PTRACE, false); // Use socket pair to communicate tids to this process from its grandchild. int sockets[2]; @@ -346,7 +346,7 @@ TEST(PtraceTest, PrctlSetPtracerInvalidPID) { TEST(PtraceTest, PrctlSetPtracerPID) { SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); + AutoCapability cap(CAP_SYS_PTRACE, false); // Use sockets to synchronize between tracer and tracee. int sockets[2]; @@ -410,7 +410,7 @@ TEST(PtraceTest, PrctlSetPtracerPID) { TEST(PtraceTest, PrctlSetPtracerAny) { SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); + AutoCapability cap(CAP_SYS_PTRACE, false); // Use sockets to synchronize between tracer and tracee. int sockets[2]; @@ -475,7 +475,7 @@ TEST(PtraceTest, PrctlSetPtracerAny) { TEST(PtraceTest, PrctlClearPtracer) { SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); + AutoCapability cap(CAP_SYS_PTRACE, false); // Use sockets to synchronize between tracer and tracee. int sockets[2]; @@ -543,7 +543,7 @@ TEST(PtraceTest, PrctlClearPtracer) { TEST(PtraceTest, PrctlReplacePtracer) { SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); + AutoCapability cap(CAP_SYS_PTRACE, false); pid_t const unused_pid = fork(); if (unused_pid == 0) { @@ -633,7 +633,7 @@ TEST(PtraceTest, PrctlReplacePtracer) { // thread group leader is still around. TEST(PtraceTest, PrctlSetPtracerPersistsPastTraceeThreadExit) { SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); + AutoCapability cap(CAP_SYS_PTRACE, false); // Use sockets to synchronize between tracer and tracee. int sockets[2]; @@ -703,7 +703,7 @@ TEST(PtraceTest, PrctlSetPtracerPersistsPastTraceeThreadExit) { // even if the tracee thread is terminated. TEST(PtraceTest, PrctlSetPtracerPersistsPastLeaderExec) { SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); + AutoCapability cap(CAP_SYS_PTRACE, false); // Use sockets to synchronize between tracer and tracee. int sockets[2]; @@ -770,7 +770,7 @@ TEST(PtraceTest, PrctlSetPtracerPersistsPastLeaderExec) { // exec. TEST(PtraceTest, PrctlSetPtracerDoesNotPersistPastNonLeaderExec) { SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); + AutoCapability cap(CAP_SYS_PTRACE, false); // Use sockets to synchronize between tracer and tracee. int sockets[2]; @@ -904,7 +904,7 @@ TEST(PtraceTest, PrctlSetPtracerDoesNotPersistPastTracerThreadExit) { [[noreturn]] void RunPrctlSetPtracerDoesNotPersistPastTracerThreadExit( int tracee_tid, int fd) { - TEST_PCHECK(SetCapability(CAP_SYS_PTRACE, false).ok()); + AutoCapability cap(CAP_SYS_PTRACE, false); ScopedThread t([fd] { pid_t const tracer_tid = gettid(); @@ -1033,7 +1033,7 @@ TEST(PtraceTest, PrctlSetPtracerRespectsTracerThreadID) { // attached. TEST(PtraceTest, PrctlClearPtracerDoesNotAffectCurrentTracer) { SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); + AutoCapability cap(CAP_SYS_PTRACE, false); // Use sockets to synchronize between tracer and tracee. int sockets[2]; @@ -1118,7 +1118,7 @@ TEST(PtraceTest, PrctlClearPtracerDoesNotAffectCurrentTracer) { TEST(PtraceTest, PrctlNotInherited) { SKIP_IF(ASSERT_NO_ERRNO_AND_VALUE(YamaPtraceScope()) != 1); - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); + AutoCapability cap(CAP_SYS_PTRACE, false); // Allow any ptracer. This should not affect the child processes. ASSERT_THAT(prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY), SyscallSucceeds()); @@ -2302,7 +2302,7 @@ TEST(PtraceTest, SetYAMAPtraceScope) { EXPECT_STREQ(buf.data(), "0\n"); // Test that a child can attach to its parent when ptrace_scope is 0. - ASSERT_NO_ERRNO(SetCapability(CAP_SYS_PTRACE, false)); + AutoCapability cap(CAP_SYS_PTRACE, false); pid_t const child_pid = fork(); if (child_pid == 0) { TEST_PCHECK(CheckPtraceAttach(getppid()) == 0); diff --git a/test/syscalls/linux/pty.cc b/test/syscalls/linux/pty.cc index 541b2744c..1c83e2656 100644 --- a/test/syscalls/linux/pty.cc +++ b/test/syscalls/linux/pty.cc @@ -396,12 +396,13 @@ TEST(PtyTrunc, Truncate) { setsid(); // Make sure we're ignoring SIGHUP, which will be sent to this process once we - // disconnect they TTY. + // disconnect the TTY. struct sigaction sa = {}; sa.sa_handler = SIG_IGN; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); - auto const cleanup = ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGHUP, sa)); + const Cleanup cleanup = + ASSERT_NO_ERRNO_AND_VALUE(ScopedSigaction(SIGHUP, sa)); // Opening PTYs with O_TRUNC shouldn't cause an error, but calls to // (f)truncate should. @@ -484,7 +485,7 @@ TEST(BasicPtyTest, OpenSetsControllingTTY) { setsid(); // Make sure we're ignoring SIGHUP, which will be sent to this process once we - // disconnect they TTY. + // disconnect the TTY. struct sigaction sa = {}; sa.sa_handler = SIG_IGN; sa.sa_flags = 0; @@ -1422,7 +1423,7 @@ TEST_F(JobControlTest, ReleaseTTY) { ASSERT_THAT(ioctl(replica_.get(), TIOCSCTTY, 0), SyscallSucceeds()); // Make sure we're ignoring SIGHUP, which will be sent to this process once we - // disconnect they TTY. + // disconnect the TTY. struct sigaction sa = {}; sa.sa_handler = SIG_IGN; sa.sa_flags = 0; @@ -1543,7 +1544,7 @@ TEST_F(JobControlTest, ReleaseTTYSignals) { EXPECT_THAT(setpgid(diff_pgrp_child, diff_pgrp_child), SyscallSucceeds()); // Make sure we're ignoring SIGHUP, which will be sent to this process once we - // disconnect they TTY. + // disconnect the TTY. struct sigaction sighup_sa = {}; sighup_sa.sa_handler = SIG_IGN; sighup_sa.sa_flags = 0; diff --git a/test/syscalls/linux/raw_socket_hdrincl.cc b/test/syscalls/linux/raw_socket_hdrincl.cc index 2f25aceb2..8b3d02d97 100644 --- a/test/syscalls/linux/raw_socket_hdrincl.cc +++ b/test/syscalls/linux/raw_socket_hdrincl.cc @@ -177,10 +177,8 @@ TEST_F(RawHDRINCL, ConnectToLoopback) { SyscallSucceeds()); } -TEST_F(RawHDRINCL, SendWithoutConnectSucceeds) { - // FIXME(gvisor.dev/issue/3159): Test currently flaky. - SKIP_IF(true); - +// FIXME(gvisor.dev/issue/3159): Test currently flaky. +TEST_F(RawHDRINCL, DISABLED_SendWithoutConnectSucceeds) { struct iphdr hdr = LoopbackHeader(); ASSERT_THAT(send(socket_, &hdr, sizeof(hdr), 0), SyscallSucceedsWithValue(sizeof(hdr))); diff --git a/test/syscalls/linux/rename.cc b/test/syscalls/linux/rename.cc index b1a813de0..76a8da65f 100644 --- a/test/syscalls/linux/rename.cc +++ b/test/syscalls/linux/rename.cc @@ -259,8 +259,8 @@ TEST(RenameTest, DirectoryDoesNotOverwriteNonemptyDirectory) { TEST(RenameTest, FailsWhenOldParentNotWritable) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); @@ -275,8 +275,8 @@ TEST(RenameTest, FailsWhenOldParentNotWritable) { TEST(RenameTest, FailsWhenNewParentNotWritable) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); @@ -293,8 +293,8 @@ TEST(RenameTest, FailsWhenNewParentNotWritable) { // to overwrite. TEST(RenameTest, OverwriteFailsWhenNewParentNotWritable) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto dir1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); auto f1 = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir1.path())); @@ -312,8 +312,8 @@ TEST(RenameTest, OverwriteFailsWhenNewParentNotWritable) { // because the user cannot determine if source exists. TEST(RenameTest, FileDoesNotExistWhenNewParentNotExecutable) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); // No execute permission. auto dir = ASSERT_NO_ERRNO_AND_VALUE( diff --git a/test/syscalls/linux/rlimits.cc b/test/syscalls/linux/rlimits.cc index 860f0f688..d31a2a880 100644 --- a/test/syscalls/linux/rlimits.cc +++ b/test/syscalls/linux/rlimits.cc @@ -41,9 +41,7 @@ TEST(RlimitTest, SetRlimitHigher) { TEST(RlimitTest, UnprivilegedSetRlimit) { // Drop privileges if necessary. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_RESOURCE))) { - EXPECT_NO_ERRNO(SetCapability(CAP_SYS_RESOURCE, false)); - } + AutoCapability cap(CAP_SYS_RESOURCE, false); struct rlimit rl = {}; rl.rlim_cur = 1000; diff --git a/test/syscalls/linux/semaphore.cc b/test/syscalls/linux/semaphore.cc index 207377efb..2ce8f836c 100644 --- a/test/syscalls/linux/semaphore.cc +++ b/test/syscalls/linux/semaphore.cc @@ -535,7 +535,7 @@ TEST(SemaphoreTest, SemCtlGetPidFork) { TEST(SemaphoreTest, SemIpcSet) { // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); + AutoCapability cap(CAP_IPC_OWNER, false); AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); ASSERT_THAT(sem.get(), SyscallSucceeds()); @@ -560,7 +560,7 @@ TEST(SemaphoreTest, SemIpcSet) { TEST(SemaphoreTest, SemCtlIpcStat) { // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); + AutoCapability cap(CAP_IPC_OWNER, false); const uid_t kUid = getuid(); const gid_t kGid = getgid(); time_t start_time = time(nullptr); @@ -635,7 +635,7 @@ PosixErrorOr<int> WaitSemctl(int semid, int target, int cmd) { TEST(SemaphoreTest, SemopGetzcnt) { // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); + AutoCapability cap(CAP_IPC_OWNER, false); // Create a write only semaphore set. AutoSem sem(semget(IPC_PRIVATE, 1, 0200 | IPC_CREAT)); ASSERT_THAT(sem.get(), SyscallSucceeds()); @@ -743,7 +743,7 @@ TEST(SemaphoreTest, SemopGetzcntOnSignal) { TEST(SemaphoreTest, SemopGetncnt) { // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); + AutoCapability cap(CAP_IPC_OWNER, false); // Create a write only semaphore set. AutoSem sem(semget(IPC_PRIVATE, 1, 0200 | IPC_CREAT)); ASSERT_THAT(sem.get(), SyscallSucceeds()); @@ -853,7 +853,7 @@ TEST(SemaphoreTest, IpcInfo) { std::set<int> sem_ids; struct seminfo info; // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); + AutoCapability cap(CAP_IPC_OWNER, false); for (int i = 0; i < kLoops; i++) { AutoSem sem(semget(IPC_PRIVATE, 1, 0600 | IPC_CREAT)); ASSERT_THAT(sem.get(), SyscallSucceeds()); @@ -923,7 +923,7 @@ TEST(SemaphoreTest, SemInfo) { std::set<int> sem_ids; struct seminfo info; // Drop CAP_IPC_OWNER which allows us to bypass semaphore permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_IPC_OWNER, false)); + AutoCapability cap(CAP_IPC_OWNER, false); for (int i = 0; i < kLoops; i++) { AutoSem sem(semget(IPC_PRIVATE, kSemSetSize, 0600 | IPC_CREAT)); ASSERT_THAT(sem.get(), SyscallSucceeds()); diff --git a/test/syscalls/linux/socket_capability.cc b/test/syscalls/linux/socket_capability.cc index 84b5b2b21..f75482aba 100644 --- a/test/syscalls/linux/socket_capability.cc +++ b/test/syscalls/linux/socket_capability.cc @@ -40,7 +40,7 @@ TEST(SocketTest, UnixConnectNeedsWritePerm) { // Drop capabilites that allow us to override permision checks. Otherwise if // the test is run as root, the connect below will bypass permission checks // and succeed unexpectedly. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + AutoCapability cap(CAP_DAC_OVERRIDE, false); // Connect should fail without write perms. ASSERT_THAT(chmod(addr.sun_path, 0500), SyscallSucceeds()); diff --git a/test/syscalls/linux/socket_ip_tcp_generic.cc b/test/syscalls/linux/socket_ip_tcp_generic.cc index 59b56dc1a..2f5743cda 100644 --- a/test/syscalls/linux/socket_ip_tcp_generic.cc +++ b/test/syscalls/linux/socket_ip_tcp_generic.cc @@ -1155,7 +1155,7 @@ TEST_P(TCPSocketPairTest, IpMulticastLoopDefault) { TEST_P(TCPSocketPairTest, TCPResetDuringClose) { DisableSave ds; // Too many syscalls. - constexpr int kThreadCount = 1000; + constexpr int kThreadCount = 100; std::unique_ptr<ScopedThread> instances[kThreadCount]; for (int i = 0; i < kThreadCount; i++) { instances[i] = absl::make_unique<ScopedThread>([&]() { diff --git a/test/syscalls/linux/sticky.cc b/test/syscalls/linux/sticky.cc index 4afed6d08..5a2841899 100644 --- a/test/syscalls/linux/sticky.cc +++ b/test/syscalls/linux/sticky.cc @@ -56,9 +56,7 @@ TEST(StickyTest, StickyBitPermDenied) { // thread won't be able to open some log files after the test ends. ScopedThread([&] { // Drop privileges. - if (HaveCapability(CAP_FOWNER).ValueOrDie()) { - EXPECT_NO_ERRNO(SetCapability(CAP_FOWNER, false)); - } + AutoCapability cap(CAP_FOWNER, false); // Change EUID and EGID. EXPECT_THAT( @@ -98,9 +96,7 @@ TEST(StickyTest, StickyBitSameUID) { // thread won't be able to open some log files after the test ends. ScopedThread([&] { // Drop privileges. - if (HaveCapability(CAP_FOWNER).ValueOrDie()) { - EXPECT_NO_ERRNO(SetCapability(CAP_FOWNER, false)); - } + AutoCapability cap(CAP_FOWNER, false); // Change EGID. EXPECT_THAT( diff --git a/test/syscalls/linux/symlink.cc b/test/syscalls/linux/symlink.cc index 9f6c59446..fa6849f11 100644 --- a/test/syscalls/linux/symlink.cc +++ b/test/syscalls/linux/symlink.cc @@ -100,8 +100,8 @@ TEST(SymlinkTest, CanCreateSymlinkDir) { TEST(SymlinkTest, CannotCreateSymlinkInReadOnlyDir) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); const std::string olddir = NewTempAbsPath(); ASSERT_THAT(mkdir(olddir.c_str(), 0444), SyscallSucceeds()); @@ -250,8 +250,8 @@ TEST(SymlinkTest, PwriteToSymlink) { TEST(SymlinkTest, SymlinkAtDegradedPermissions) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); @@ -301,8 +301,8 @@ TEST(SymlinkTest, ReadlinkAtDirWithOpath) { TEST(SymlinkTest, ReadlinkAtDegradedPermissions) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); const std::string oldpath = NewTempAbsPathInDir(dir.path()); diff --git a/test/syscalls/linux/tcp_socket.cc b/test/syscalls/linux/tcp_socket.cc index 011b60f0e..5bfdecc79 100644 --- a/test/syscalls/linux/tcp_socket.cc +++ b/test/syscalls/linux/tcp_socket.cc @@ -1164,7 +1164,13 @@ TEST_P(SimpleTcpSocketTest, SelfConnectSend) { ASSERT_THAT(RetryEINTR(connect)(s.get(), AsSockAddr(&addr), addrlen), SyscallSucceeds()); - std::vector<char> writebuf(512 << 10); // 512 KiB. + // Ensure the write buffer is large enough not to block on a single write. + size_t write_size = 512 << 10; // 512 KiB. + EXPECT_THAT(setsockopt(s.get(), SOL_SOCKET, SO_SNDBUF, &write_size, + sizeof(write_size)), + SyscallSucceedsWithValue(0)); + + std::vector<char> writebuf(write_size); // Try to send the whole thing. int n; diff --git a/test/syscalls/linux/truncate.cc b/test/syscalls/linux/truncate.cc index 5db0b8276..0f08d9996 100644 --- a/test/syscalls/linux/truncate.cc +++ b/test/syscalls/linux/truncate.cc @@ -181,7 +181,7 @@ TEST(TruncateTest, FtruncateDir) { TEST(TruncateTest, TruncateNonWriteable) { // Make sure we don't have CAP_DAC_OVERRIDE, since that allows the user to // always override write permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + AutoCapability cap(CAP_DAC_OVERRIDE, false); auto temp_file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( GetAbsoluteTestTmpdir(), absl::string_view(), 0555 /* mode */)); EXPECT_THAT(truncate(temp_file.path().c_str(), 0), @@ -210,7 +210,7 @@ TEST(TruncateTest, FtruncateWithOpath) { // regardless of whether the file permissions allow writing. TEST(TruncateTest, FtruncateWithoutWritePermission) { // Drop capabilities that allow us to override file permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); + AutoCapability cap(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, diff --git a/test/syscalls/linux/tuntap.cc b/test/syscalls/linux/tuntap.cc index 6e3a00d2c..279fe342c 100644 --- a/test/syscalls/linux/tuntap.cc +++ b/test/syscalls/linux/tuntap.cc @@ -170,10 +170,10 @@ TEST(TuntapStaticTest, NetTunExists) { class TuntapTest : public ::testing::Test { protected: void SetUp() override { - have_net_admin_cap_ = + const bool have_net_admin_cap = ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)); - if (have_net_admin_cap_ && !IsRunningOnGvisor()) { + if (have_net_admin_cap && !IsRunningOnGvisor()) { // gVisor always creates enabled/up'd interfaces, while Linux does not (as // observed in b/110961832). Some of the tests require the Linux stack to // notify the socket of any link-address-resolution failures. Those @@ -183,21 +183,12 @@ class TuntapTest : public ::testing::Test { ASSERT_NO_ERRNO(LinkChangeFlags(link.index, IFF_UP, IFF_UP)); } } - - void TearDown() override { - if (have_net_admin_cap_) { - // Bring back capability if we had dropped it in test case. - ASSERT_NO_ERRNO(SetCapability(CAP_NET_ADMIN, true)); - } - } - - bool have_net_admin_cap_; }; TEST_F(TuntapTest, CreateInterfaceNoCap) { SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); - ASSERT_NO_ERRNO(SetCapability(CAP_NET_ADMIN, false)); + AutoCapability cap(CAP_NET_ADMIN, false); FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR)); diff --git a/test/syscalls/linux/uname.cc b/test/syscalls/linux/uname.cc index d8824b171..759ea4f53 100644 --- a/test/syscalls/linux/uname.cc +++ b/test/syscalls/linux/uname.cc @@ -76,9 +76,7 @@ TEST(UnameTest, SetNames) { } TEST(UnameTest, UnprivilegedSetNames) { - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))) { - EXPECT_NO_ERRNO(SetCapability(CAP_SYS_ADMIN, false)); - } + AutoCapability cap(CAP_SYS_ADMIN, false); EXPECT_THAT(sethostname("", 0), SyscallFailsWithErrno(EPERM)); EXPECT_THAT(setdomainname("", 0), SyscallFailsWithErrno(EPERM)); diff --git a/test/syscalls/linux/unlink.cc b/test/syscalls/linux/unlink.cc index 7c301c305..75dcf4465 100644 --- a/test/syscalls/linux/unlink.cc +++ b/test/syscalls/linux/unlink.cc @@ -66,8 +66,8 @@ TEST(UnlinkTest, AtDir) { TEST(UnlinkTest, AtDirDegradedPermissions) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); @@ -86,8 +86,8 @@ TEST(UnlinkTest, AtDirDegradedPermissions) { // Files cannot be unlinked if the parent is not writable and executable. TEST(UnlinkTest, ParentDegradedPermissions) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); diff --git a/test/syscalls/linux/utimes.cc b/test/syscalls/linux/utimes.cc index e647d2896..e711d6657 100644 --- a/test/syscalls/linux/utimes.cc +++ b/test/syscalls/linux/utimes.cc @@ -225,7 +225,8 @@ void TestUtimensat(int dirFd, std::string const& path) { EXPECT_GE(mtime3, before); EXPECT_LE(mtime3, after); - EXPECT_EQ(atime3, mtime3); + // TODO(b/187074006): atime/mtime may differ with local_gofer_uncached. + // EXPECT_EQ(atime3, mtime3); } TEST(UtimensatTest, OnAbsPath) { diff --git a/test/syscalls/linux/verity_ioctl.cc b/test/syscalls/linux/verity_ioctl.cc index 822e16f3c..be91b23d0 100644 --- a/test/syscalls/linux/verity_ioctl.cc +++ b/test/syscalls/linux/verity_ioctl.cc @@ -28,40 +28,13 @@ #include "test/util/mount_util.h" #include "test/util/temp_path.h" #include "test/util/test_util.h" +#include "test/util/verity_util.h" namespace gvisor { namespace testing { namespace { -#ifndef FS_IOC_ENABLE_VERITY -#define FS_IOC_ENABLE_VERITY 1082156677 -#endif - -#ifndef FS_IOC_MEASURE_VERITY -#define FS_IOC_MEASURE_VERITY 3221513862 -#endif - -#ifndef FS_VERITY_FL -#define FS_VERITY_FL 1048576 -#endif - -#ifndef FS_IOC_GETFLAGS -#define FS_IOC_GETFLAGS 2148034049 -#endif - -struct fsverity_digest { - __u16 digest_algorithm; - __u16 digest_size; /* input/output */ - __u8 digest[]; -}; - -constexpr int kMaxDigestSize = 64; -constexpr int kDefaultDigestSize = 32; -constexpr char kContents[] = "foobarbaz"; -constexpr char kMerklePrefix[] = ".merkle.verity."; -constexpr char kMerkleRootPrefix[] = ".merkleroot.verity."; - class IoctlTest : public ::testing::Test { protected: void SetUp() override { @@ -85,80 +58,6 @@ class IoctlTest : public ::testing::Test { std::string filename_; }; -// Provide a function to convert bytes to hex string, since -// absl::BytesToHexString does not seem to be compatible with golang -// hex.DecodeString used in verity due to zero-padding. -std::string BytesToHexString(uint8_t bytes[], int size) { - std::stringstream ss; - ss << std::hex; - for (int i = 0; i < size; ++i) { - ss << std::setw(2) << std::setfill('0') << static_cast<int>(bytes[i]); - } - return ss.str(); -} - -std::string MerklePath(absl::string_view path) { - return JoinPath(Dirname(path), - std::string(kMerklePrefix) + std::string(Basename(path))); -} - -std::string MerkleRootPath(absl::string_view path) { - return JoinPath(Dirname(path), - std::string(kMerkleRootPrefix) + std::string(Basename(path))); -} - -// Flip a random bit in the file represented by fd. -PosixError FlipRandomBit(int fd, int size) { - // Generate a random offset in the file. - srand(time(nullptr)); - unsigned int seed = 0; - int random_offset = rand_r(&seed) % size; - - // Read a random byte and flip a bit in it. - char buf[1]; - RETURN_ERROR_IF_SYSCALL_FAIL(PreadFd(fd, buf, 1, random_offset)); - buf[0] ^= 1; - RETURN_ERROR_IF_SYSCALL_FAIL(PwriteFd(fd, buf, 1, random_offset)); - return NoError(); -} - -// Mount a verity on the tmpfs and enable both the file and the direcotry. Then -// mount a new verity with measured root hash. -PosixErrorOr<std::string> MountVerity(std::string tmpfs_dir, - std::string filename) { - // Mount a verity fs on the existing tmpfs mount. - std::string mount_opts = "lower_path=" + tmpfs_dir; - ASSIGN_OR_RETURN_ERRNO(TempPath verity_dir, TempPath::CreateDir()); - RETURN_ERROR_IF_SYSCALL_FAIL( - mount("", verity_dir.path().c_str(), "verity", 0, mount_opts.c_str())); - - // Enable both the file and the directory. - ASSIGN_OR_RETURN_ERRNO( - auto fd, Open(JoinPath(verity_dir.path(), filename), O_RDONLY, 0777)); - RETURN_ERROR_IF_SYSCALL_FAIL(ioctl(fd.get(), FS_IOC_ENABLE_VERITY)); - ASSIGN_OR_RETURN_ERRNO(auto dir_fd, Open(verity_dir.path(), O_RDONLY, 0777)); - RETURN_ERROR_IF_SYSCALL_FAIL(ioctl(dir_fd.get(), FS_IOC_ENABLE_VERITY)); - - // Measure the root hash. - uint8_t digest_array[sizeof(struct fsverity_digest) + kMaxDigestSize] = {0}; - struct fsverity_digest* digest = - reinterpret_cast<struct fsverity_digest*>(digest_array); - digest->digest_size = kMaxDigestSize; - RETURN_ERROR_IF_SYSCALL_FAIL( - ioctl(dir_fd.get(), FS_IOC_MEASURE_VERITY, digest)); - - // Mount a verity fs with specified root hash. - mount_opts += - ",root_hash=" + BytesToHexString(digest->digest, digest->digest_size); - ASSIGN_OR_RETURN_ERRNO(TempPath verity_with_hash_dir, TempPath::CreateDir()); - RETURN_ERROR_IF_SYSCALL_FAIL(mount("", verity_with_hash_dir.path().c_str(), - "verity", 0, mount_opts.c_str())); - // Verity directories should not be deleted. Release the TempPath objects to - // prevent those directories from being deleted by the destructor. - verity_dir.release(); - return verity_with_hash_dir.release(); -} - TEST_F(IoctlTest, Enable) { // Mount a verity fs on the existing tmpfs mount. std::string mount_opts = "lower_path=" + tmpfs_dir_.path(); diff --git a/test/syscalls/linux/verity_mmap.cc b/test/syscalls/linux/verity_mmap.cc new file mode 100644 index 000000000..dde74cc91 --- /dev/null +++ b/test/syscalls/linux/verity_mmap.cc @@ -0,0 +1,158 @@ +// Copyright 2021 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <stdint.h> +#include <stdlib.h> +#include <sys/mman.h> +#include <sys/mount.h> + +#include <string> + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/util/capability_util.h" +#include "test/util/fs_util.h" +#include "test/util/memory_util.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" +#include "test/util/verity_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +class MmapTest : public ::testing::Test { + protected: + void SetUp() override { + // Verity is implemented in VFS2. + SKIP_IF(IsRunningWithVFS1()); + + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))); + // Mount a tmpfs file system, to be wrapped by a verity fs. + tmpfs_dir_ = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); + ASSERT_THAT(mount("", tmpfs_dir_.path().c_str(), "tmpfs", 0, ""), + SyscallSucceeds()); + + // Create a new file in the tmpfs mount. + file_ = ASSERT_NO_ERRNO_AND_VALUE( + TempPath::CreateFileWith(tmpfs_dir_.path(), kContents, 0777)); + filename_ = Basename(file_.path()); + } + + TempPath tmpfs_dir_; + TempPath file_; + std::string filename_; +}; + +TEST_F(MmapTest, MmapRead) { + std::string verity_dir = + ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); + + // Make sure the file can be open and mmapped in the mounted verity fs. + auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(JoinPath(verity_dir, filename_), O_RDONLY, 0777)); + + Mapping const m = + ASSERT_NO_ERRNO_AND_VALUE(Mmap(nullptr, sizeof(kContents) - 1, PROT_READ, + MAP_SHARED, verity_fd.get(), 0)); + EXPECT_THAT(std::string(m.view()), ::testing::StrEq(kContents)); +} + +TEST_F(MmapTest, ModifiedBeforeMmap) { + std::string verity_dir = + ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); + + // Modify the file and check verification failure upon mmapping. + auto const fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(JoinPath(tmpfs_dir_.path(), filename_), O_RDWR, 0777)); + ASSERT_NO_ERRNO(FlipRandomBit(fd.get(), sizeof(kContents) - 1)); + + auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(JoinPath(verity_dir, filename_), O_RDONLY, 0777)); + Mapping const m = + ASSERT_NO_ERRNO_AND_VALUE(Mmap(nullptr, sizeof(kContents) - 1, PROT_READ, + MAP_SHARED, verity_fd.get(), 0)); + + // Memory fault is expected when Translate fails. + EXPECT_EXIT(std::string(m.view()), ::testing::KilledBySignal(SIGSEGV), ""); +} + +TEST_F(MmapTest, ModifiedAfterMmap) { + std::string verity_dir = + ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); + + auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(JoinPath(verity_dir, filename_), O_RDONLY, 0777)); + Mapping const m = + ASSERT_NO_ERRNO_AND_VALUE(Mmap(nullptr, sizeof(kContents) - 1, PROT_READ, + MAP_SHARED, verity_fd.get(), 0)); + + // Modify the file after mapping and check verification failure upon mmapping. + auto const fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(JoinPath(tmpfs_dir_.path(), filename_), O_RDWR, 0777)); + ASSERT_NO_ERRNO(FlipRandomBit(fd.get(), sizeof(kContents) - 1)); + + // Memory fault is expected when Translate fails. + EXPECT_EXIT(std::string(m.view()), ::testing::KilledBySignal(SIGSEGV), ""); +} + +class MmapParamTest + : public MmapTest, + public ::testing::WithParamInterface<std::tuple<int, int>> { + protected: + int prot() const { return std::get<0>(GetParam()); } + int flags() const { return std::get<1>(GetParam()); } +}; + +INSTANTIATE_TEST_SUITE_P( + WriteExecNoneSharedPrivate, MmapParamTest, + ::testing::Combine(::testing::ValuesIn({ + PROT_WRITE, + PROT_EXEC, + PROT_NONE, + }), + ::testing::ValuesIn({MAP_SHARED, MAP_PRIVATE}))); + +TEST_P(MmapParamTest, Mmap) { + std::string verity_dir = + ASSERT_NO_ERRNO_AND_VALUE(MountVerity(tmpfs_dir_.path(), filename_)); + + // Make sure the file can be open and mmapped in the mounted verity fs. + auto const verity_fd = ASSERT_NO_ERRNO_AND_VALUE( + Open(JoinPath(verity_dir, filename_), O_RDONLY, 0777)); + + if (prot() == PROT_WRITE && flags() == MAP_SHARED) { + // Verity file system is read-only. + EXPECT_THAT( + reinterpret_cast<intptr_t>(mmap(nullptr, sizeof(kContents) - 1, prot(), + flags(), verity_fd.get(), 0)), + SyscallFailsWithErrno(EACCES)); + } else { + Mapping const m = ASSERT_NO_ERRNO_AND_VALUE(Mmap( + nullptr, sizeof(kContents) - 1, prot(), flags(), verity_fd.get(), 0)); + if (prot() == PROT_NONE) { + // Memory mapped by MAP_NONE cannot be accessed. + EXPECT_EXIT(std::string(m.view()), ::testing::KilledBySignal(SIGSEGV), + ""); + } else { + EXPECT_THAT(std::string(m.view()), ::testing::StrEq(kContents)); + } + } +} + +} // namespace + +} // namespace testing +} // namespace gvisor diff --git a/test/syscalls/linux/xattr.cc b/test/syscalls/linux/xattr.cc index dd8067807..c8a97df6b 100644 --- a/test/syscalls/linux/xattr.cc +++ b/test/syscalls/linux/xattr.cc @@ -109,8 +109,8 @@ TEST_F(XattrTest, XattrInvalidPrefix) { // the restore will fail to open it with r/w permissions. TEST_F(XattrTest, XattrReadOnly) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); const char* path = test_file_name_.c_str(); const char name[] = "user.test"; @@ -140,8 +140,8 @@ TEST_F(XattrTest, XattrReadOnly) { // the restore will fail to open it with r/w permissions. TEST_F(XattrTest, XattrWriteOnly) { // Drop capabilities that allow us to override file and directory permissions. - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_OVERRIDE, false)); - ASSERT_NO_ERRNO(SetCapability(CAP_DAC_READ_SEARCH, false)); + AutoCapability cap1(CAP_DAC_OVERRIDE, false); + AutoCapability cap2(CAP_DAC_READ_SEARCH, false); DisableSave ds; ASSERT_NO_ERRNO(testing::Chmod(test_file_name_, S_IWUSR)); @@ -632,7 +632,7 @@ TEST_F(XattrTest, TrustedNamespaceWithCapSysAdmin) { // Trusted namespace not supported in VFS1. SKIP_IF(IsRunningWithVFS1()); - // TODO(b/66162845): Only gVisor tmpfs currently supports trusted namespace. + // TODO(b/166162845): Only gVisor tmpfs currently supports trusted namespace. SKIP_IF(IsRunningOnGvisor() && !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(test_file_name_))); @@ -680,9 +680,7 @@ TEST_F(XattrTest, TrustedNamespaceWithoutCapSysAdmin) { !ASSERT_NO_ERRNO_AND_VALUE(IsTmpfs(test_file_name_))); // Drop CAP_SYS_ADMIN if we have it. - if (ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_ADMIN))) { - EXPECT_NO_ERRNO(SetCapability(CAP_SYS_ADMIN, false)); - } + AutoCapability cap(CAP_SYS_ADMIN, false); const char* path = test_file_name_.c_str(); const char name[] = "trusted.test"; |