From 34ec00c5e775479b15ae8ad69456cef02f0a545a Mon Sep 17 00:00:00 2001 From: Ghanan Gowripalan Date: Mon, 9 Aug 2021 10:16:59 -0700 Subject: Run raw IP socket syscall tests on Fuchsia + Do not check for CAP_NET_RAW on Fuchsia Fuchsia does not support capabilities the same way Linux does. Instead emulate the check for CAP_NET_RAW by checking if a raw IP sockets may be created. PiperOrigin-RevId: 389663218 --- test/syscalls/linux/BUILD | 3 + test/syscalls/linux/raw_socket.cc | 2 - test/syscalls/linux/raw_socket_hdrincl.cc | 1 - test/syscalls/linux/raw_socket_icmp.cc | 1 - test/util/BUILD | 12 ++- test/util/capability_util.cc | 85 -------------------- test/util/capability_util.h | 112 ++------------------------ test/util/fuchsia_capability_util.cc | 45 +++++++++++ test/util/fuchsia_capability_util.h | 41 ++++++++++ test/util/linux_capability_util.cc | 85 ++++++++++++++++++++ test/util/linux_capability_util.h | 128 ++++++++++++++++++++++++++++++ 11 files changed, 318 insertions(+), 197 deletions(-) delete mode 100644 test/util/capability_util.cc create mode 100644 test/util/fuchsia_capability_util.cc create mode 100644 test/util/fuchsia_capability_util.h create mode 100644 test/util/linux_capability_util.cc create mode 100644 test/util/linux_capability_util.h (limited to 'test') diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index 71cc9b51d..960421466 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -7,6 +7,9 @@ package( exports_files( [ + "raw_socket.cc", + "raw_socket_hdrincl.cc", + "raw_socket_icmp.cc", "socket.cc", "socket_inet_loopback.cc", "socket_inet_loopback_isolated.cc", diff --git a/test/syscalls/linux/raw_socket.cc b/test/syscalls/linux/raw_socket.cc index e19fe8f6b..4e69e389b 100644 --- a/test/syscalls/linux/raw_socket.cc +++ b/test/syscalls/linux/raw_socket.cc @@ -13,8 +13,6 @@ // limitations under the License. #include -#include -#include #include #include #include diff --git a/test/syscalls/linux/raw_socket_hdrincl.cc b/test/syscalls/linux/raw_socket_hdrincl.cc index f1d8fd295..e54d781c9 100644 --- a/test/syscalls/linux/raw_socket_hdrincl.cc +++ b/test/syscalls/linux/raw_socket_hdrincl.cc @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include #include #include diff --git a/test/syscalls/linux/raw_socket_icmp.cc b/test/syscalls/linux/raw_socket_icmp.cc index 27d3fffee..80a524273 100644 --- a/test/syscalls/linux/raw_socket_icmp.cc +++ b/test/syscalls/linux/raw_socket_icmp.cc @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include #include #include #include diff --git a/test/util/BUILD b/test/util/BUILD index 4a4401ba8..3211546f5 100644 --- a/test/util/BUILD +++ b/test/util/BUILD @@ -8,13 +8,21 @@ package( cc_library( name = "capability_util", testonly = 1, - srcs = ["capability_util.cc"], - hdrs = ["capability_util.h"], + srcs = [ + "fuchsia_capability_util.cc", + "linux_capability_util.cc", + ], + hdrs = [ + "capability_util.h", + "fuchsia_capability_util.h", + "linux_capability_util.h", + ], deps = [ ":cleanup", ":memory_util", ":posix_error", ":save_util", + ":socket_util", ":test_util", "@com_google_absl//absl/strings", ], diff --git a/test/util/capability_util.cc b/test/util/capability_util.cc deleted file mode 100644 index 3bf218128..000000000 --- a/test/util/capability_util.cc +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2018 The gVisor Authors. -// -// Licensed 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. - -#ifdef __linux__ - -#include "test/util/capability_util.h" - -#include -#include -#include -#include - -#include - -#include "absl/strings/str_cat.h" -#include "test/util/memory_util.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -PosixErrorOr CanCreateUserNamespace() { - // The most reliable way to determine if userns creation is possible is by - // trying to create one; see below. - ASSIGN_OR_RETURN_ERRNO( - auto child_stack, - MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); - int const child_pid = clone( - +[](void*) { return 0; }, - reinterpret_cast(child_stack.addr() + kPageSize), - CLONE_NEWUSER | SIGCHLD, /* arg = */ nullptr); - if (child_pid > 0) { - int status; - int const ret = waitpid(child_pid, &status, /* options = */ 0); - MaybeSave(); - if (ret < 0) { - return PosixError(errno, "waitpid"); - } - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - return PosixError( - ESRCH, absl::StrCat("child process exited with status ", status)); - } - return true; - } else if (errno == EPERM) { - // Per clone(2), EPERM can be returned if: - // - // - "CLONE_NEWUSER was specified in flags, but either the effective user ID - // or the effective group ID of the caller does not have a mapping in the - // parent namespace (see user_namespaces(7))." - // - // - "(since Linux 3.9) CLONE_NEWUSER was specified in flags and the caller - // is in a chroot environment (i.e., the caller's root directory does - // not match the root directory of the mount namespace in which it - // resides)." - std::cerr << "clone(CLONE_NEWUSER) failed with EPERM" << std::endl; - return false; - } else if (errno == EUSERS) { - // "(since Linux 3.11) CLONE_NEWUSER was specified in flags, and the call - // would cause the limit on the number of nested user namespaces to be - // exceeded. See user_namespaces(7)." - std::cerr << "clone(CLONE_NEWUSER) failed with EUSERS" << std::endl; - return false; - } else { - // Unexpected error code; indicate an actual error. - return PosixError(errno, "clone(CLONE_NEWUSER)"); - } -} - -} // namespace testing -} // namespace gvisor - -#endif // __linux__ diff --git a/test/util/capability_util.h b/test/util/capability_util.h index c4b0feade..f5d622e2d 100644 --- a/test/util/capability_util.h +++ b/test/util/capability_util.h @@ -17,112 +17,12 @@ #ifndef GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ #define GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ -#ifdef __linux__ - -#include -#include -#include -#include - -#include "test/util/cleanup.h" -#include "test/util/posix_error.h" -#include "test/util/save_util.h" -#include "test/util/test_util.h" - -#ifndef _LINUX_CAPABILITY_VERSION_3 -#error Expecting _LINUX_CAPABILITY_VERSION_3 support +#if defined(__Fuchsia__) +#include "test/util/fuchsia_capability_util.h" +#elif defined(__linux__) +#include "test/util/linux_capability_util.h" +#else +#error "Unhandled platform" #endif -namespace gvisor { -namespace testing { - -// HaveCapability returns true if the process has the specified EFFECTIVE -// capability. -inline PosixErrorOr HaveCapability(int cap) { - if (!cap_valid(cap)) { - return PosixError(EINVAL, "Invalid capability"); - } - - struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; - struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); - MaybeSave(); - - return (caps[CAP_TO_INDEX(cap)].effective & CAP_TO_MASK(cap)) != 0; -} - -// SetCapability sets the specified EFFECTIVE capability. -inline PosixError SetCapability(int cap, bool set) { - if (!cap_valid(cap)) { - return PosixError(EINVAL, "Invalid capability"); - } - - struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; - struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); - MaybeSave(); - - if (set) { - caps[CAP_TO_INDEX(cap)].effective |= CAP_TO_MASK(cap); - } else { - caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap); - } - header = {_LINUX_CAPABILITY_VERSION_3, 0}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps)); - MaybeSave(); - - return NoError(); -} - -// DropPermittedCapability drops the specified PERMITTED. The EFFECTIVE -// capabilities must be a subset of PERMITTED, so those are dropped as well. -inline PosixError DropPermittedCapability(int cap) { - if (!cap_valid(cap)) { - return PosixError(EINVAL, "Invalid capability"); - } - - struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; - struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); - MaybeSave(); - - caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap); - caps[CAP_TO_INDEX(cap)].permitted &= ~CAP_TO_MASK(cap); - - header = {_LINUX_CAPABILITY_VERSION_3, 0}; - RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps)); - MaybeSave(); - - return NoError(); -} - -PosixErrorOr CanCreateUserNamespace(); - -class AutoCapability { - public: - AutoCapability(int cap, bool set) : cap_(cap), set_(set) { - const bool has = EXPECT_NO_ERRNO_AND_VALUE(HaveCapability(cap)); - if (set != has) { - EXPECT_NO_ERRNO(SetCapability(cap_, set_)); - applied_ = true; - } - } - - ~AutoCapability() { - if (applied_) { - EXPECT_NO_ERRNO(SetCapability(cap_, !set_)); - } - } - - private: - int cap_; - bool set_; - bool applied_ = false; -}; - -} // namespace testing -} // namespace gvisor - -#endif // __linux__ - #endif // GVISOR_TEST_UTIL_CAPABILITY_UTIL_H_ diff --git a/test/util/fuchsia_capability_util.cc b/test/util/fuchsia_capability_util.cc new file mode 100644 index 000000000..43f60f20f --- /dev/null +++ b/test/util/fuchsia_capability_util.cc @@ -0,0 +1,45 @@ +// Copyright 2021 The gVisor Authors. +// +// Licensed 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. + +#ifdef __Fuchsia__ + +#include "test/util/fuchsia_capability_util.h" + +#include +#include + +#include "test/util/socket_util.h" + +namespace gvisor { +namespace testing { + +PosixErrorOr HaveCapability(int cap) { + if (cap == CAP_NET_RAW) { + auto s = Socket(AF_INET, SOCK_RAW, IPPROTO_UDP); + if (s.ok()) { + return true; + } + if (s.error().errno_value() == EPERM) { + return false; + } + return s.error(); + } + + return false; +} + +} // namespace testing +} // namespace gvisor + +#endif // __Fuchsia__ diff --git a/test/util/fuchsia_capability_util.h b/test/util/fuchsia_capability_util.h new file mode 100644 index 000000000..87657d7e8 --- /dev/null +++ b/test/util/fuchsia_capability_util.h @@ -0,0 +1,41 @@ +// Copyright 2021 The gVisor Authors. +// +// Licensed 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. + +// Utilities for testing capabilities on Fuchsia. + +#ifndef GVISOR_TEST_UTIL_FUCHSIA_CAPABILITY_UTIL_H_ +#define GVISOR_TEST_UTIL_FUCHSIA_CAPABILITY_UTIL_H_ + +#ifdef __Fuchsia__ + +#include "test/util/posix_error.h" + +#ifdef CAP_NET_RAW +#error "Fuchsia should not define CAP_NET_RAW" +#endif // CAP_NET_RAW +#define CAP_NET_RAW 0 + +namespace gvisor { +namespace testing { + +// HaveCapability returns true if the process has the specified EFFECTIVE +// capability. +PosixErrorOr HaveCapability(int cap); + +} // namespace testing +} // namespace gvisor + +#endif // __Fuchsia__ + +#endif // GVISOR_TEST_UTIL_FUCHSIA_CAPABILITY_UTIL_H_ diff --git a/test/util/linux_capability_util.cc b/test/util/linux_capability_util.cc new file mode 100644 index 000000000..958eb96f9 --- /dev/null +++ b/test/util/linux_capability_util.cc @@ -0,0 +1,85 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed 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. + +#ifdef __linux__ + +#include "test/util/linux_capability_util.h" + +#include +#include +#include +#include + +#include + +#include "absl/strings/str_cat.h" +#include "test/util/memory_util.h" +#include "test/util/posix_error.h" +#include "test/util/save_util.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +PosixErrorOr CanCreateUserNamespace() { + // The most reliable way to determine if userns creation is possible is by + // trying to create one; see below. + ASSIGN_OR_RETURN_ERRNO( + auto child_stack, + MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); + int const child_pid = clone( + +[](void*) { return 0; }, + reinterpret_cast(child_stack.addr() + kPageSize), + CLONE_NEWUSER | SIGCHLD, /* arg = */ nullptr); + if (child_pid > 0) { + int status; + int const ret = waitpid(child_pid, &status, /* options = */ 0); + MaybeSave(); + if (ret < 0) { + return PosixError(errno, "waitpid"); + } + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + return PosixError( + ESRCH, absl::StrCat("child process exited with status ", status)); + } + return true; + } else if (errno == EPERM) { + // Per clone(2), EPERM can be returned if: + // + // - "CLONE_NEWUSER was specified in flags, but either the effective user ID + // or the effective group ID of the caller does not have a mapping in the + // parent namespace (see user_namespaces(7))." + // + // - "(since Linux 3.9) CLONE_NEWUSER was specified in flags and the caller + // is in a chroot environment (i.e., the caller's root directory does + // not match the root directory of the mount namespace in which it + // resides)." + std::cerr << "clone(CLONE_NEWUSER) failed with EPERM" << std::endl; + return false; + } else if (errno == EUSERS) { + // "(since Linux 3.11) CLONE_NEWUSER was specified in flags, and the call + // would cause the limit on the number of nested user namespaces to be + // exceeded. See user_namespaces(7)." + std::cerr << "clone(CLONE_NEWUSER) failed with EUSERS" << std::endl; + return false; + } else { + // Unexpected error code; indicate an actual error. + return PosixError(errno, "clone(CLONE_NEWUSER)"); + } +} + +} // namespace testing +} // namespace gvisor + +#endif // __linux__ diff --git a/test/util/linux_capability_util.h b/test/util/linux_capability_util.h new file mode 100644 index 000000000..be94ebd19 --- /dev/null +++ b/test/util/linux_capability_util.h @@ -0,0 +1,128 @@ +// Copyright 2018 The gVisor Authors. +// +// Licensed 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. + +// Utilities for testing capabilities on Linux. + +#ifndef GVISOR_TEST_UTIL_LINUX_CAPABILITY_UTIL_H_ +#define GVISOR_TEST_UTIL_LINUX_CAPABILITY_UTIL_H_ + +#ifdef __linux__ + +#include +#include +#include +#include + +#include "test/util/cleanup.h" +#include "test/util/posix_error.h" +#include "test/util/save_util.h" +#include "test/util/test_util.h" + +#ifndef _LINUX_CAPABILITY_VERSION_3 +#error Expecting _LINUX_CAPABILITY_VERSION_3 support +#endif + +namespace gvisor { +namespace testing { + +// HaveCapability returns true if the process has the specified EFFECTIVE +// capability. +inline PosixErrorOr HaveCapability(int cap) { + if (!cap_valid(cap)) { + return PosixError(EINVAL, "Invalid capability"); + } + + struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; + struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; + RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); + MaybeSave(); + + return (caps[CAP_TO_INDEX(cap)].effective & CAP_TO_MASK(cap)) != 0; +} + +// SetCapability sets the specified EFFECTIVE capability. +inline PosixError SetCapability(int cap, bool set) { + if (!cap_valid(cap)) { + return PosixError(EINVAL, "Invalid capability"); + } + + struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; + struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; + RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); + MaybeSave(); + + if (set) { + caps[CAP_TO_INDEX(cap)].effective |= CAP_TO_MASK(cap); + } else { + caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap); + } + header = {_LINUX_CAPABILITY_VERSION_3, 0}; + RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps)); + MaybeSave(); + + return NoError(); +} + +// DropPermittedCapability drops the specified PERMITTED. The EFFECTIVE +// capabilities must be a subset of PERMITTED, so those are dropped as well. +inline PosixError DropPermittedCapability(int cap) { + if (!cap_valid(cap)) { + return PosixError(EINVAL, "Invalid capability"); + } + + struct __user_cap_header_struct header = {_LINUX_CAPABILITY_VERSION_3, 0}; + struct __user_cap_data_struct caps[_LINUX_CAPABILITY_U32S_3] = {}; + RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capget, &header, &caps)); + MaybeSave(); + + caps[CAP_TO_INDEX(cap)].effective &= ~CAP_TO_MASK(cap); + caps[CAP_TO_INDEX(cap)].permitted &= ~CAP_TO_MASK(cap); + + header = {_LINUX_CAPABILITY_VERSION_3, 0}; + RETURN_ERROR_IF_SYSCALL_FAIL(syscall(__NR_capset, &header, &caps)); + MaybeSave(); + + return NoError(); +} + +PosixErrorOr CanCreateUserNamespace(); + +class AutoCapability { + public: + AutoCapability(int cap, bool set) : cap_(cap), set_(set) { + const bool has = EXPECT_NO_ERRNO_AND_VALUE(HaveCapability(cap)); + if (set != has) { + EXPECT_NO_ERRNO(SetCapability(cap_, set_)); + applied_ = true; + } + } + + ~AutoCapability() { + if (applied_) { + EXPECT_NO_ERRNO(SetCapability(cap_, !set_)); + } + } + + private: + int cap_; + bool set_; + bool applied_ = false; +}; + +} // namespace testing +} // namespace gvisor + +#endif // __linux__ + +#endif // GVISOR_TEST_UTIL_LINUX_CAPABILITY_UTIL_H_ -- cgit v1.2.3