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 {