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


The following commit(s) were added to refs/heads/master by this push:
     new c4f2890af [cgroups2] Helper to check device entry normalization.
c4f2890af is described below

commit c4f2890afafc747462189d76c5ff2bd7bb5e5018
Author: Jason Zhou <[email protected]>
AuthorDate: Fri Jul 26 16:41:50 2024 -0400

    [cgroups2] Helper to check device entry normalization.
    
    Currently we assume that a device state is normalized before using it
    for generating ebpf files. However, we have not been enforcing these
    constraints.
    
    We add a helper to check if a device state is normalized so that we can
    enforce these constraints.
    
    An allow or deny list is 'normalized' iff everything below are true:
    
    1. No Entry has empty accesses specified.
    2. No two entries on the same list can have the same selector (type,
       major & minor numbers).
    3. No two entries on the same list can be encompassed by the other
       entry (see Entry::encompasses).
    
    Review: https://reviews.apache.org/r/75099/
---
 src/linux/cgroups2.cpp                     | 47 ++++++++++++++++++++++++++++++
 src/linux/cgroups2.hpp                     | 11 +++++++
 src/tests/containerizer/cgroups2_tests.cpp | 45 ++++++++++++++++++++++++++++
 3 files changed, 103 insertions(+)

diff --git a/src/linux/cgroups2.cpp b/src/linux/cgroups2.cpp
index ac4678261..bd6018050 100644
--- a/src/linux/cgroups2.cpp
+++ b/src/linux/cgroups2.cpp
@@ -1483,6 +1483,53 @@ Try<Nothing> configure(
   return Nothing();
 }
 
+
+bool normalized(const vector<Entry>& query)
+{
+  auto has_empties = [](const vector<Entry>& entries) {
+    foreach (const Entry& entry, entries) {
+      if (entry.access.none()) {
+        return true;
+      }
+    }
+    return false;
+  };
+
+  if (has_empties(query)) {
+    return false;
+  }
+
+  auto has_duplicate_selectors = [](const vector<Entry>& entries) {
+    hashset<string> selectors;
+    foreach (const Entry& entry, entries) {
+      selectors.insert(stringify(entry.selector));
+    }
+    return selectors.size() != entries.size();
+  };
+
+  if (has_duplicate_selectors(query)) {
+    return false;
+  }
+
+  auto has_encompassed_entries = [](const vector<Entry>& entries) {
+    foreach (const Entry& entry, entries) {
+      foreach (const Entry& other, entries) {
+        if ((!cgroups::devices::operator==(entry, other))
+            && entry.encompasses(other)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  };
+
+  if (has_encompassed_entries(query)) {
+    return false;
+  }
+
+  return true;
+}
+
 } // namespace devices {
 
 } // namespace cgroups2 {
diff --git a/src/linux/cgroups2.hpp b/src/linux/cgroups2.hpp
index accaebdad..ef4e67932 100644
--- a/src/linux/cgroups2.hpp
+++ b/src/linux/cgroups2.hpp
@@ -440,6 +440,17 @@ Try<Nothing> configure(
     const std::vector<Entry>& allow,
     const std::vector<Entry>& deny);
 
+
+// Checks if the list of entries passed in is normalized.
+// A list of entries is normalized if:
+//
+// 1. No Entry has empty accesses specified.
+// 2. No two entries on the same list can have the same selector (type,
+//    major & minor numbers).
+// 3. No two entries on the same list can be encompassed by the other
+//   entry (see Entry::encompasses).
+bool normalized(const std::vector<Entry>& entries);
+
 } // namespace devices {
 
 } // namespace cgroups2 {
diff --git a/src/tests/containerizer/cgroups2_tests.cpp 
b/src/tests/containerizer/cgroups2_tests.cpp
index 39f17460c..388639bed 100644
--- a/src/tests/containerizer/cgroups2_tests.cpp
+++ b/src/tests/containerizer/cgroups2_tests.cpp
@@ -862,6 +862,51 @@ TEST_F(Cgroups2Test, ROOT_CGROUPS2_GetBpfFdById)
   ASSERT_SOME(result);
 }
 
+
+TEST(Cgroups2DevicesTest, NormalizedTest)
+{
+  // Not normalized if there is an entry with no accesses specified.
+  devices::Entry empty_entry;
+  empty_entry.selector.type = devices::Entry::Selector::Type::CHARACTER;
+  empty_entry.selector.major = 1;
+  empty_entry.selector.minor = 3;
+  empty_entry.access.read = false;
+  empty_entry.access.write = false;
+  empty_entry.access.mknod = false;
+  EXPECT_FALSE(cgroups2::devices::normalized({empty_entry}));
+
+  // Not normalized if there is any entry that shares the same selector
+  // with another entry in the same list.
+  EXPECT_FALSE(cgroups2::devices::normalized(
+    {CHECK_NOTERROR(devices::Entry::parse("b 3:1 rw")),
+     CHECK_NOTERROR(devices::Entry::parse("b 3:1 m"))}));
+
+  // Not normalized if there are entries which are encompassed by
+  // another on the same list.
+  EXPECT_FALSE(cgroups2::devices::normalized({
+    CHECK_NOTERROR(devices::Entry::parse("c *:* rw")),
+    CHECK_NOTERROR(devices::Entry::parse("c 3:1 w"))
+  }));
+
+  // Normalized if all of the above are false.
+  EXPECT_TRUE(cgroups2::devices::normalized({
+    CHECK_NOTERROR(devices::Entry::parse("c *:* m")),
+    CHECK_NOTERROR(devices::Entry::parse("b *:* m")),
+    CHECK_NOTERROR(devices::Entry::parse("c 5:1 rwm")),
+    CHECK_NOTERROR(devices::Entry::parse("c 4:0 rwm")),
+    CHECK_NOTERROR(devices::Entry::parse("c 4:1 rwm")),
+    CHECK_NOTERROR(devices::Entry::parse("c 136:* rwm")),
+    CHECK_NOTERROR(devices::Entry::parse("c 5:2 rwm")),
+    CHECK_NOTERROR(devices::Entry::parse("c 10:200 rwm")),
+    CHECK_NOTERROR(devices::Entry::parse("c 1:3 rwm")),
+    CHECK_NOTERROR(devices::Entry::parse("c 1:5 rwm")),
+    CHECK_NOTERROR(devices::Entry::parse("c 1:7 rwm")),
+    CHECK_NOTERROR(devices::Entry::parse("c 5:0 rwm")),
+    CHECK_NOTERROR(devices::Entry::parse("c 1:9 rwm")),
+    CHECK_NOTERROR(devices::Entry::parse("c 1:8 rwm"))
+  }));
+}
+
 } // namespace tests {
 
 } // namespace internal {

Reply via email to