From 78ae3db1a39c0cd925c6b75807fa1dc76ba99986 Mon Sep 17 00:00:00 2001 From: Rahat Mahmood Date: Fri, 14 May 2021 11:06:07 -0700 Subject: Fix cgroup hierarchy registration. Previously, registration was racy because we were publishing hierarchies in the registry without fully initializing the underlying filesystem. This led to concurrent mount(2)s discovering the partially intialized filesystems and dropping the final refs on them which cause them to be freed prematurely. Reported-by: syzbot+13f54e77bdf59f0171f0@syzkaller.appspotmail.com Reported-by: syzbot+2c7f0a9127ac6a84f17e@syzkaller.appspotmail.com PiperOrigin-RevId: 373824552 --- test/syscalls/linux/BUILD | 2 ++ test/syscalls/linux/cgroup.cc | 52 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) (limited to 'test/syscalls/linux') diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 94a582256..efed4aeb0 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -4247,10 +4247,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 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()); -- cgit v1.2.3