github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/util/capability_util.cc (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include "test/util/capability_util.h"
    16  
    17  #include <linux/capability.h>
    18  #include <sched.h>
    19  #include <sys/mman.h>
    20  #include <sys/wait.h>
    21  
    22  #include <iostream>
    23  
    24  #include "absl/strings/str_cat.h"
    25  #include "test/util/memory_util.h"
    26  #include "test/util/posix_error.h"
    27  #include "test/util/save_util.h"
    28  #include "test/util/test_util.h"
    29  
    30  namespace gvisor {
    31  namespace testing {
    32  
    33  PosixErrorOr<bool> CanCreateUserNamespace() {
    34    // The most reliable way to determine if userns creation is possible is by
    35    // trying to create one; see below.
    36    ASSIGN_OR_RETURN_ERRNO(
    37        auto child_stack,
    38        MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE));
    39    int const child_pid = clone(
    40        +[](void*) { return 0; },
    41        reinterpret_cast<void*>(child_stack.addr() + kPageSize),
    42        CLONE_NEWUSER | SIGCHLD, /* arg = */ nullptr);
    43    if (child_pid > 0) {
    44      int status;
    45      int const ret = waitpid(child_pid, &status, /* options = */ 0);
    46      MaybeSave();
    47      if (ret < 0) {
    48        return PosixError(errno, "waitpid");
    49      }
    50      if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
    51        return PosixError(
    52            ESRCH, absl::StrCat("child process exited with status ", status));
    53      }
    54      return true;
    55    } else if (errno == EPERM) {
    56      // Per clone(2), EPERM can be returned if:
    57      //
    58      // - "CLONE_NEWUSER was specified in flags, but either the effective user ID
    59      // or the effective group ID of the caller does not have a mapping in the
    60      // parent namespace (see user_namespaces(7))."
    61      //
    62      // - "(since Linux 3.9) CLONE_NEWUSER was specified in flags and the caller
    63      // is in a chroot environment (i.e., the caller's root directory does
    64      // not match the root directory of the mount namespace in which it
    65      // resides)."
    66      std::cerr << "clone(CLONE_NEWUSER) failed with EPERM" << std::endl;
    67      return false;
    68    } else if (errno == EUSERS) {
    69      // "(since Linux 3.11) CLONE_NEWUSER was specified in flags, and the call
    70      // would cause the limit on the number of nested user namespaces to be
    71      // exceeded. See user_namespaces(7)."
    72      std::cerr << "clone(CLONE_NEWUSER) failed with EUSERS" << std::endl;
    73      return false;
    74    } else {
    75      // Unexpected error code; indicate an actual error.
    76      return PosixError(errno, "clone(CLONE_NEWUSER)");
    77    }
    78  }
    79  
    80  }  // namespace testing
    81  }  // namespace gvisor