You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by gi...@apache.org on 2019/01/28 07:52:40 UTC
[mesos] 09/11: Added Seccomp parser tests.
This is an automated email from the ASF dual-hosted git repository.
gilbert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/mesos.git
commit 90ddd116fabde038a5726c69d3dda8da3c884855
Author: Andrei Budnik <ab...@mesosphere.com>
AuthorDate: Sun Jan 27 23:52:05 2019 -0800
Added Seccomp parser tests.
See summary.
Review: https://reviews.apache.org/r/69409/
---
src/Makefile.am | 5 +
src/tests/CMakeLists.txt | 5 +
.../containerizer/linux_seccomp_parser_tests.cpp | 543 +++++++++++++++++++++
src/tests/environment.cpp | 35 ++
4 files changed, 588 insertions(+)
diff --git a/src/Makefile.am b/src/Makefile.am
index 46e943e..78f3e85 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -2651,6 +2651,11 @@ mesos_tests_SOURCES += \
tests/containerizer/xfs_quota_tests.cpp
endif
+if ENABLE_SECCOMP_ISOLATOR
+mesos_tests_SOURCES += \
+ tests/containerizer/linux_seccomp_parser_tests.cpp
+endif
+
mesos_tests_CPPFLAGS = $(MESOS_CPPFLAGS)
mesos_tests_CPPFLAGS += -DSOURCE_DIR=\"$(abs_top_srcdir)\"
mesos_tests_CPPFLAGS += -DBUILD_DIR=\"$(abs_top_builddir)\"
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 7a1265e..ceaa119 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -244,6 +244,11 @@ if (LINUX)
containerizer/xfs_quota_tests.cpp)
endif()
+ if (ENABLE_SECCOMP_ISOLATOR)
+ list(APPEND MESOS_TESTS_SRC
+ containerizer/linux_seccomp_parser_tests.cpp)
+ endif()
+
if (ENABLE_LINUX_ROUTING)
list(APPEND MESOS_TESTS_SRC
containerizer/routing_tests.cpp)
diff --git a/src/tests/containerizer/linux_seccomp_parser_tests.cpp b/src/tests/containerizer/linux_seccomp_parser_tests.cpp
new file mode 100644
index 0000000..0949332
--- /dev/null
+++ b/src/tests/containerizer/linux_seccomp_parser_tests.cpp
@@ -0,0 +1,543 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <gtest/gtest.h>
+
+#include <stout/gtest.hpp>
+#include <stout/try.hpp>
+
+#include "linux/capabilities.hpp"
+#include "linux/seccomp/seccomp_parser.hpp"
+
+using mesos::seccomp::ContainerSeccompProfile;
+
+using std::multiset;
+using std::string;
+
+namespace mesos {
+namespace internal {
+namespace tests {
+
+class SeccompParserTest : public ::testing::Test {};
+
+
+// This test verifies that the Seccomp parser can parse a valid Seccomp profile.
+TEST_F(SeccompParserTest, SECCOMP_ParseSeccompProfile)
+{
+ const string config =
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [
+ {
+ "architecture": "SCMP_ARCH_X86_64",
+ "subArchitectures": [
+ "SCMP_ARCH_X86",
+ "SCMP_ARCH_X32"
+ ]
+ },
+ {
+ "architecture": "SCMP_ARCH_AARCH64",
+ "subArchitectures": [
+ "SCMP_ARCH_ARM"
+ ]
+ }
+ ],
+ "syscalls": [
+ {
+ "names": [
+ "accept",
+ "writev"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "comment": "",
+ "includes": {},
+ "excludes": {}
+ },
+ {
+ "names": [
+ "clone"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [
+ {
+ "index": 0,
+ "value": 2080505856,
+ "valueTwo": 0,
+ "op": "SCMP_CMP_MASKED_EQ"
+ }
+ ],
+ "comment": "",
+ "includes": {},
+ "excludes": {
+ "caps": [
+ "CAP_SYS_ADMIN"
+ ],
+ "arches": [
+ "s390",
+ "s390x"
+ ]
+ }
+ }
+ ]
+ })~";
+
+ Try<ContainerSeccompProfile> profile =
+ mesos::internal::seccomp::parseProfileData(config);
+
+ ASSERT_SOME(profile);
+
+ EXPECT_EQ(profile->default_action(),
+ ContainerSeccompProfile::Syscall::ACT_ERRNO);
+
+ // Note that the Seccomp parser doesn't add an architecture with all its
+ // subarchitectures when the architecture is not native.
+ const auto& architectures = profile->architectures();
+ EXPECT_EQ(multiset<int>(architectures.begin(), architectures.end()),
+ multiset<int>({ContainerSeccompProfile::ARCH_X86_64,
+ ContainerSeccompProfile::ARCH_X86,
+ ContainerSeccompProfile::ARCH_X32}));
+
+ EXPECT_EQ(profile->syscalls_size(), 2);
+
+ {
+ const auto& rule = profile->syscalls().Get(0);
+
+ const auto& names = rule.names();
+ EXPECT_EQ(multiset<string>(names.begin(), names.end()),
+ multiset<string>({"accept", "writev"}));
+
+ EXPECT_EQ(rule.action(), ContainerSeccompProfile::Syscall::ACT_ALLOW);
+ EXPECT_EQ(rule.args_size(), 0);
+ EXPECT_FALSE(rule.has_includes());
+ EXPECT_FALSE(rule.has_excludes());
+ }
+
+ {
+ const auto& rule = profile->syscalls().Get(1);
+
+ const auto& names = rule.names();
+ EXPECT_EQ(multiset<string>(names.begin(), names.end()),
+ multiset<string>({"clone"}));
+
+ EXPECT_EQ(rule.action(), ContainerSeccompProfile::Syscall::ACT_ALLOW);
+
+ EXPECT_EQ(rule.args_size(), 1);
+
+ const auto& arg = rule.args().Get(0);
+ EXPECT_EQ(arg.index(), 0u);
+ EXPECT_EQ(arg.value(), 2080505856u);
+ EXPECT_EQ(arg.value_two(), 0u);
+ EXPECT_EQ(arg.op(), ContainerSeccompProfile::Syscall::Arg::CMP_MASKED_EQ);
+
+ EXPECT_FALSE(rule.has_includes());
+
+ EXPECT_TRUE(rule.has_excludes());
+ EXPECT_EQ(rule.excludes().capabilities_size(), 1);
+ EXPECT_EQ(rule.excludes().capabilities().Get(0), CapabilityInfo::SYS_ADMIN);
+ }
+}
+
+
+// This test verifies handling of errors caused by an invalid Seccomp profile.
+TEST_F(SeccompParserTest, SECCOMP_ParseInvalidSeccompProfile)
+{
+ auto check = [](const string& config, const string& failureMessage) {
+ CHECK(!failureMessage.empty());
+
+ Try<ContainerSeccompProfile> profile =
+ mesos::internal::seccomp::parseProfileData(config);
+
+ ASSERT_ERROR(profile);
+ EXPECT_TRUE(strings::contains(profile.error(), failureMessage));
+ };
+
+ // Check for the existence of the top-most objects.
+ check(
+ R"~(
+ {
+ "defaultAction!!BUG": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": []
+ })~",
+ "Cannot determine 'defaultAction'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap!!BUG": [],
+ "syscalls": []
+ })~",
+ "Cannot determine 'archMap'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls!!BUG": []
+ })~",
+ "Cannot determine 'syscalls'");
+
+ // Check `defaultAction` object.
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_!!BUG",
+ "archMap": [],
+ "syscalls": []
+ })~",
+ "Unknown syscall action: 'SCMP_ACT_!!BUG'");
+
+ // Check `archMap` object.
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [
+ {
+ "architecture": "SCMP_ARCH_!!BUG",
+ "subArchitectures": []
+ }
+ ],
+ "syscalls": []
+ })~",
+ "Unknown architecture: 'SCMP_ARCH_!!BUG'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [
+ {
+ "architecture": "SCMP_ARCH_X86_64",
+ "subArchitectures": [
+ "SCMP_ARCH_X86",
+ "SCMP_ARCH_!!BUG"
+ ]
+ }
+ ],
+ "syscalls": []
+ })~",
+ "Unknown architecture: 'SCMP_ARCH_!!BUG'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [
+ {
+ "architecture": "SCMP_ARCH_X86_64",
+ "subArchitectures!!BUG": []
+ }
+ ],
+ "syscalls": []
+ })~",
+ "Cannot determine 'subArchitectures'");
+
+ // Check `syscalls` section.
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names!!BUG": ["accept"],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "includes": {},
+ "excludes": {}
+ }
+ ]
+ })~",
+ "Cannot determine 'names'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names": ["accept"],
+ "action!!BUG": "SCMP_ACT_ALLOW",
+ "args": [],
+ "includes": {},
+ "excludes": {}
+ }
+ ]
+ })~",
+ "Cannot determine 'action'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names": ["accept"],
+ "action": "SCMP_ACT_ALLOW",
+ "args!!BUG": [],
+ "includes": {},
+ "excludes": {}
+ }
+ ]
+ })~",
+ "Cannot determine 'args'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names": ["accept"],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "includes!!BUG": {},
+ "excludes": {}
+ }
+ ]
+ })~",
+ "Cannot determine 'includes'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names": ["accept"],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "includes": {},
+ "excludes!!BUG": {}
+ }
+ ]
+ })~",
+ "Cannot determine 'excludes'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names": [
+ "accept",
+ "writev!!BUG"
+ ],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "includes": {},
+ "excludes": {}
+ }
+ ]
+ })~",
+ "Unrecognized syscall 'writev!!BUG'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names": ["accept"],
+ "action": "SCMP_ACT_!!BUG",
+ "args": [],
+ "includes": {},
+ "excludes": {}
+ }
+ ]
+ })~",
+ "Unknown syscall action: 'SCMP_ACT_!!BUG'");
+
+ // Check `args` object which belongs to the `syscalls` object.
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names": ["accept"],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [
+ {
+ "index!!BUG": 0,
+ "value": 2080505856,
+ "valueTwo": 0,
+ "op": "SCMP_CMP_MASKED_EQ"
+ }
+ ],
+ "includes": {},
+ "excludes": {}
+ }
+ ]
+ })~",
+ "Cannot determine 'index'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names": ["accept"],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [
+ {
+ "index": 0,
+ "value!!BUG": 2080505856,
+ "valueTwo": 0,
+ "op": "SCMP_CMP_MASKED_EQ"
+ }
+ ],
+ "includes": {},
+ "excludes": {}
+ }
+ ]
+ })~",
+ "Cannot determine 'value'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names": ["accept"],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [
+ {
+ "index": 0,
+ "value": 2080505856,
+ "valueTwo!!BUG": 0,
+ "op": "SCMP_CMP_MASKED_EQ"
+ }
+ ],
+ "includes": {},
+ "excludes": {}
+ }
+ ]
+ })~",
+ "Cannot determine 'valueTwo'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names": ["accept"],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [
+ {
+ "index": 0,
+ "value": 2080505856,
+ "valueTwo": 0,
+ "op!!BUG": "SCMP_CMP_MASKED_EQ"
+ }
+ ],
+ "includes": {},
+ "excludes": {}
+ }
+ ]
+ })~",
+ "Cannot determine 'op'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names": ["accept"],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [
+ {
+ "index": 0,
+ "value": 2080505856,
+ "valueTwo": 0,
+ "op": "SCMP_CMP_MASKED_EQ!!BUG"
+ }
+ ],
+ "includes": {},
+ "excludes": {}
+ }
+ ]
+ })~",
+ "Unknown operator: 'SCMP_CMP_MASKED_EQ!!BUG'");
+
+ // Check `excludes` object which belongs to the `syscalls` object.
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names": ["accept"],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "includes": {},
+ "excludes": {
+ "arches": [
+ "amd64!!SPECTRE"
+ ]
+ }
+ }
+ ]
+ })~",
+ "Unknown architecture: 'amd64!!SPECTRE'");
+
+ check(
+ R"~(
+ {
+ "defaultAction": "SCMP_ACT_ERRNO",
+ "archMap": [],
+ "syscalls": [
+ {
+ "names": ["accept"],
+ "action": "SCMP_ACT_ALLOW",
+ "args": [],
+ "includes": {},
+ "excludes": {
+ "caps": [
+ "CAP_SYS_ADMIN!!BUG"
+ ]
+ }
+ }
+ ]
+ })~",
+ "Unknown capability: 'CAP_SYS_ADMIN!!BUG'");
+}
+
+} // namespace tests {
+} // namespace internal {
+} // namespace mesos {
diff --git a/src/tests/environment.cpp b/src/tests/environment.cpp
index 03f8f59..c3596fb 100644
--- a/src/tests/environment.cpp
+++ b/src/tests/environment.cpp
@@ -914,6 +914,40 @@ public:
};
+class SeccompFilter : public TestFilter
+{
+public:
+ SeccompFilter()
+ {
+ // Since the Seccomp parser depends on CPU architecture, we can run Seccomp
+ // tests deterministically only on `x86_64`.
+ Try<string> arch = os::shell("arch");
+ if (arch.isError()) {
+ seccompError = arch.error();
+ } else if (strings::trim(arch.get()) != "x86_64") {
+ seccompError = Error("Seccomp tests cannot be run on non-x86_64 systems");
+ }
+
+ if (seccompError.isSome()) {
+ std::cerr
+ << "-------------------------------------------------------------\n"
+ << "We can't run any SECCOMP tests:\n"
+ << seccompError.get() << "\n"
+ << "-------------------------------------------------------------"
+ << std::endl;
+ }
+ }
+
+ bool disable(const ::testing::TestInfo* test) const override
+ {
+ return matches(test, "SECCOMP_") && seccompError.isSome();
+ }
+
+private:
+ Option<Error> seccompError;
+};
+
+
class UnprivilegedUserFilter : public TestFilter
{
public:
@@ -1070,6 +1104,7 @@ Environment::Environment(const Flags& _flags)
std::make_shared<PerfCPUCyclesFilter>(),
std::make_shared<PerfFilter>(),
std::make_shared<RootFilter>(),
+ std::make_shared<SeccompFilter>(),
std::make_shared<UnprivilegedUserFilter>(),
std::make_shared<UnzipFilter>(),
std::make_shared<VEthFilter>(),