summaryrefslogtreecommitdiffhomepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/syscalls/linux/BUILD2
-rw-r--r--test/syscalls/linux/cgroup.cc52
-rw-r--r--test/util/cgroup_util.cc14
-rw-r--r--test/util/cgroup_util.h2
4 files changed, 70 insertions, 0 deletions
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<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/util/cgroup_util.cc b/test/util/cgroup_util.cc
index 04d4f8de0..977993f41 100644
--- a/test/util/cgroup_util.cc
+++ b/test/util/cgroup_util.cc
@@ -142,6 +142,20 @@ PosixError Mounter::Unmount(const Cgroup& c) {
return NoError();
}
+void Mounter::release(const Cgroup& c) {
+ auto mp = mountpoints_.find(c.id());
+ if (mp != mountpoints_.end()) {
+ mp->second.release();
+ mountpoints_.erase(mp);
+ }
+
+ auto m = mounts_.find(c.id());
+ if (m != mounts_.end()) {
+ m->second.Release();
+ mounts_.erase(m);
+ }
+}
+
constexpr char kProcCgroupsHeader[] =
"#subsys_name\thierarchy\tnum_cgroups\tenabled";
diff --git a/test/util/cgroup_util.h b/test/util/cgroup_util.h
index b797a8b24..e3f696a89 100644
--- a/test/util/cgroup_util.h
+++ b/test/util/cgroup_util.h
@@ -83,6 +83,8 @@ class Mounter {
PosixError Unmount(const Cgroup& c);
+ void release(const Cgroup& c);
+
private:
// The destruction order of these members avoids errors during cleanup. We
// first unmount (by executing the mounts_ cleanups), then delete the