This is an automated email from the ASF dual-hosted git repository.

bmahler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git

commit 2458d2f9d367f4b4b6a7a79da24bf45ca6f7ebcb
Author: Jason Zhou <[email protected]>
AuthorDate: Fri Jul 26 17:17:31 2024 -0400

    [cgroups2] Helper to check device access.
    
    A device access is granted if it is encompassed by an allow entry and
    does not have access overlaps with any deny entry.
    
    The current process of manually checking if a device access would be
    granted given a state is tedious and leads to worse readability.
    
    A new helper function is added to check if an entry would be granted
    access in a CgroupDeviceAccess instance, and requires the state to be
    normalized.
    
    Review: https://reviews.apache.org/r/75113/
---
 .../device_manager/device_manager.cpp              | 29 +++++++++++++
 .../device_manager/device_manager.hpp              |  4 ++
 src/tests/device_manager_tests.cpp                 | 48 ++++++++++++++++++++++
 3 files changed, 81 insertions(+)

diff --git a/src/slave/containerizer/device_manager/device_manager.cpp 
b/src/slave/containerizer/device_manager/device_manager.cpp
index 4c9b86393..800e50243 100644
--- a/src/slave/containerizer/device_manager/device_manager.cpp
+++ b/src/slave/containerizer/device_manager/device_manager.cpp
@@ -340,6 +340,35 @@ DeviceManager::CgroupDeviceAccess 
DeviceManager::apply_diff(
   return new_state;
 }
 
+
+bool DeviceManager::CgroupDeviceAccess::is_access_granted(
+    const Entry& query) const
+{
+  CHECK(cgroups2::devices::normalized(allow_list));
+  CHECK(cgroups2::devices::normalized(deny_list));
+
+  auto allowed = [&]() {
+    foreach (const Entry& allow, allow_list) {
+      if (allow.encompasses(query)) {
+        return true;
+      }
+    }
+    return false;
+  };
+
+  auto denied = [&]() {
+    foreach (const Entry& deny, deny_list) {
+      if (deny.selector.encompasses(query.selector)
+          && deny.access.overlaps(query.access)) {
+        return true;
+      }
+    }
+    return false;
+  };
+
+  return allowed() && !denied();
+}
+
 } // namespace slave {
 } // namespace internal {
 } // namespace mesos {
diff --git a/src/slave/containerizer/device_manager/device_manager.hpp 
b/src/slave/containerizer/device_manager/device_manager.hpp
index 7c8523d8b..c0ffcc20f 100644
--- a/src/slave/containerizer/device_manager/device_manager.hpp
+++ b/src/slave/containerizer/device_manager/device_manager.hpp
@@ -68,6 +68,10 @@ public:
   {
     std::vector<cgroups::devices::Entry> allow_list;
     std::vector<cgroups::devices::Entry> deny_list;
+
+    // A device access is granted if it is encompassed by an allow entry
+    // and does not have access overlaps with any deny entry.
+    bool is_access_granted(const cgroups::devices::Entry& entry) const;
   };
 
   static Try<DeviceManager*> create(const Flags& flags);
diff --git a/src/tests/device_manager_tests.cpp 
b/src/tests/device_manager_tests.cpp
index 54d464e97..b30618c98 100644
--- a/src/tests/device_manager_tests.cpp
+++ b/src/tests/device_manager_tests.cpp
@@ -398,6 +398,54 @@ INSTANTIATE_TEST_CASE_P(
       vector<devices::Entry>{*devices::Entry::parse("c 3:* rm")},
       vector<devices::Entry>{*devices::Entry::parse("c 3:1 r")}}));
 
+
+TEST(DeviceManagerCgroupDeviceAccessTest, IsAccessGrantedTest)
+{
+  DeviceManager::CgroupDeviceAccess cgroup_device_access;
+
+  // Character devices with major minor numbers 1:3 can do write only:
+  cgroup_device_access.allow_list = {*devices::Entry::parse("c 1:3 w")};
+  EXPECT_TRUE(
+    cgroup_device_access.is_access_granted(*devices::Entry::parse("c 1:3 w")));
+  EXPECT_FALSE(
+    cgroup_device_access.is_access_granted(*devices::Entry::parse("c 1:3 
rw")));
+  EXPECT_FALSE(
+    cgroup_device_access.is_access_granted(*devices::Entry::parse("b 1:3 w")));
+
+  // Character devices with minor number 3 can do write only:
+  cgroup_device_access.allow_list = {*devices::Entry::parse("c *:3 w")};
+  EXPECT_TRUE(
+    cgroup_device_access.is_access_granted(*devices::Entry::parse("c 4:3 w")));
+  EXPECT_TRUE(
+    cgroup_device_access.is_access_granted(*devices::Entry::parse("c *:3 w")));
+
+  // Character devices with major number 5 can do write only:
+  cgroup_device_access.allow_list = {(*devices::Entry::parse("c 5:* w"))};
+  EXPECT_TRUE(
+    cgroup_device_access.is_access_granted(*devices::Entry::parse("c 5:* w")));
+  EXPECT_TRUE(
+    cgroup_device_access.is_access_granted(*devices::Entry::parse("c 5:2 w")));
+
+  // All devices will match the catch-all and can perform all operations:
+  cgroup_device_access.allow_list = {*devices::Entry::parse("a *:* rwm")};
+  EXPECT_TRUE(
+    cgroup_device_access.is_access_granted(*devices::Entry::parse("c 6:2 w")));
+
+  // Deny all accesses to character device with major and numbers 1:3.
+  cgroup_device_access.deny_list = {*devices::Entry::parse("c 1:3 rwm")};
+  EXPECT_FALSE(
+    cgroup_device_access.is_access_granted(*devices::Entry::parse("c 1:3 w")));
+  EXPECT_FALSE(
+    cgroup_device_access.is_access_granted(*devices::Entry::parse("c 1:3 
rw")));
+
+  // Entry should be denied if encompassed by an entry in allow_list and
+  // overlaps with entry in deny_list.
+  cgroup_device_access.allow_list = {*devices::Entry::parse("c 1:3 rw")};
+  cgroup_device_access.deny_list = {*devices::Entry::parse("c 1:3 w")};
+  EXPECT_FALSE(
+    cgroup_device_access.is_access_granted(*devices::Entry::parse("c 1:3 
rw")));
+}
+
 } // namespace tests {
 } // namespace internal {
 } // namespace mesos {

Reply via email to