gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/dup.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 <fcntl.h>
    16  #include <sys/resource.h>
    17  #include <unistd.h>
    18  
    19  #include <cerrno>
    20  #include <memory>
    21  
    22  #include "gtest/gtest.h"
    23  #include "absl/memory/memory.h"
    24  #include "absl/strings/str_cat.h"
    25  #include "test/util/eventfd_util.h"
    26  #include "test/util/file_descriptor.h"
    27  #include "test/util/fs_util.h"
    28  #include "test/util/linux_capability_util.h"
    29  #include "test/util/posix_error.h"
    30  #include "test/util/temp_path.h"
    31  #include "test/util/test_util.h"
    32  
    33  namespace gvisor {
    34  namespace testing {
    35  
    36  namespace {
    37  
    38  PosixErrorOr<FileDescriptor> Dup2(const FileDescriptor& fd, int target_fd) {
    39    int new_fd = dup2(fd.get(), target_fd);
    40    if (new_fd < 0) {
    41      return PosixError(errno, "Dup2");
    42    }
    43    return FileDescriptor(new_fd);
    44  }
    45  
    46  PosixErrorOr<FileDescriptor> Dup3(const FileDescriptor& fd, int target_fd,
    47                                    int flags) {
    48    int new_fd = dup3(fd.get(), target_fd, flags);
    49    if (new_fd < 0) {
    50      return PosixError(errno, "Dup2");
    51    }
    52    return FileDescriptor(new_fd);
    53  }
    54  
    55  TEST(DupTest, Dup) {
    56    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
    57    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY));
    58  
    59    // Dup the descriptor and make sure it's the same file.
    60    FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup());
    61    ASSERT_NE(fd.get(), nfd.get());
    62    ASSERT_NO_ERRNO(CheckSameFile(fd, nfd));
    63  }
    64  
    65  TEST(DupTest, DupClearsCloExec) {
    66    // Open an eventfd file descriptor with FD_CLOEXEC descriptor flag set.
    67    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NewEventFD(0, EFD_CLOEXEC));
    68    EXPECT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC));
    69  
    70    // Duplicate the descriptor. Ensure that it doesn't have FD_CLOEXEC set.
    71    FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup());
    72    ASSERT_NE(fd.get(), nfd.get());
    73    ASSERT_NO_ERRNO(CheckSameFile(fd, nfd));
    74    EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(0));
    75  }
    76  
    77  TEST(DupTest, DupWithOpath) {
    78    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
    79    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_PATH));
    80    int flags;
    81    ASSERT_THAT(flags = fcntl(fd.get(), F_GETFL), SyscallSucceeds());
    82  
    83    // Dup the descriptor and make sure it's the same file.
    84    FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup());
    85    ASSERT_NE(fd.get(), nfd.get());
    86    ASSERT_NO_ERRNO(CheckSameFile(fd, nfd));
    87    EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(flags));
    88  }
    89  
    90  TEST(DupTest, Dup2) {
    91    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
    92    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY));
    93  
    94    // Regular dup once.
    95    FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup());
    96  
    97    ASSERT_NE(fd.get(), nfd.get());
    98    ASSERT_NO_ERRNO(CheckSameFile(fd, nfd));
    99  
   100    // Dup over the file above.
   101    int target_fd = nfd.release();
   102    FileDescriptor nfd2 = ASSERT_NO_ERRNO_AND_VALUE(Dup2(fd, target_fd));
   103    EXPECT_EQ(target_fd, nfd2.get());
   104    ASSERT_NO_ERRNO(CheckSameFile(fd, nfd2));
   105  }
   106  
   107  TEST(DupTest, Rlimit) {
   108    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   109    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY));
   110  
   111    struct rlimit rl = {};
   112    EXPECT_THAT(getrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds());
   113  
   114    ASSERT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds());
   115  
   116    constexpr int kFDLimit = 101;
   117    // Create a file descriptor that will be above the limit.
   118    FileDescriptor aboveLimitFD =
   119        ASSERT_NO_ERRNO_AND_VALUE(Dup2(fd, kFDLimit * 2 - 1));
   120  
   121    rl.rlim_cur = kFDLimit;
   122    ASSERT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds());
   123    ASSERT_THAT(dup3(fd.get(), kFDLimit, 0), SyscallFails());
   124  
   125    std::vector<std::unique_ptr<FileDescriptor>> fds;
   126    int prev_fd = fd.get();
   127    int used_fds = 0;
   128    for (int i = 0; i < kFDLimit; ++i) {
   129      int new_fd = dup(fd.get());
   130      if (new_fd == -1) {
   131        break;
   132      }
   133      auto f = std::make_unique<FileDescriptor>(new_fd);
   134      EXPECT_LT(new_fd, kFDLimit);
   135      EXPECT_GT(new_fd, prev_fd);
   136      // Check that all fds in (prev_fd, new_fd) are used.
   137      for (int j = prev_fd + 1; j < new_fd; ++j) {
   138        if (fcntl(j, F_GETFD) != -1) used_fds++;
   139      }
   140      prev_fd = new_fd;
   141      fds.push_back(std::move(f));
   142    }
   143    EXPECT_EQ(fds.size() + used_fds, kFDLimit - fd.get() - 1);
   144  }
   145  
   146  TEST(RlimitTest, DupLimitedByNROpenSysctl) {
   147    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_SYS_RESOURCE)));
   148  
   149    const int kNROpen = 1 << 22;
   150    FileDescriptor fd =
   151        ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/sys/fs/nr_open", O_WRONLY));
   152    auto data = absl::StrCat(kNROpen);
   153    EXPECT_THAT(write(fd.get(), data.c_str(), data.size()),
   154                SyscallSucceedsWithValue(data.size()));
   155  
   156    struct rlimit rl = {
   157        .rlim_cur = kNROpen + 1,
   158        .rlim_max = kNROpen + 1,
   159    };
   160    EXPECT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallFailsWithErrno(EPERM));
   161    rl.rlim_cur = kNROpen;
   162    rl.rlim_max = kNROpen;
   163    ASSERT_THAT(setrlimit(RLIMIT_NOFILE, &rl), SyscallSucceeds());
   164  
   165    ASSERT_THAT(dup3(fd.get(), kNROpen, 0), SyscallFailsWithErrno(EBADF));
   166    ASSERT_THAT(dup3(fd.get(), kNROpen - 1, 0), SyscallSucceeds());
   167  }
   168  
   169  TEST(DupTest, Dup2SameFD) {
   170    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   171    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY));
   172  
   173    // Should succeed.
   174    ASSERT_THAT(dup2(fd.get(), fd.get()), SyscallSucceedsWithValue(fd.get()));
   175  }
   176  
   177  TEST(DupTest, Dup2WithOpath) {
   178    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   179    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_PATH));
   180    int flags;
   181    ASSERT_THAT(flags = fcntl(fd.get(), F_GETFL), SyscallSucceeds());
   182  
   183    // Regular dup once.
   184    FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup());
   185  
   186    ASSERT_NE(fd.get(), nfd.get());
   187    ASSERT_NO_ERRNO(CheckSameFile(fd, nfd));
   188    EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(flags));
   189  
   190    // Dup over the file above.
   191    int target_fd = nfd.release();
   192    FileDescriptor nfd2 = ASSERT_NO_ERRNO_AND_VALUE(Dup2(fd, target_fd));
   193    EXPECT_EQ(target_fd, nfd2.get());
   194    ASSERT_NO_ERRNO(CheckSameFile(fd, nfd2));
   195    EXPECT_THAT(fcntl(nfd2.get(), F_GETFL), SyscallSucceedsWithValue(flags));
   196  }
   197  
   198  TEST(DupTest, Dup3) {
   199    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   200    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY));
   201  
   202    // Regular dup once.
   203    FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup());
   204    ASSERT_NE(fd.get(), nfd.get());
   205    ASSERT_NO_ERRNO(CheckSameFile(fd, nfd));
   206  
   207    // Dup over the file above, check that it has no CLOEXEC.
   208    nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), 0));
   209    ASSERT_NO_ERRNO(CheckSameFile(fd, nfd));
   210    EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(0));
   211  
   212    // Dup over the file again, check that it does not CLOEXEC.
   213    nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), O_CLOEXEC));
   214    ASSERT_NO_ERRNO(CheckSameFile(fd, nfd));
   215    EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC));
   216  }
   217  
   218  TEST(DupTest, Dup3FailsSameFD) {
   219    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   220    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY));
   221  
   222    // Only dup3 fails if the new and old fd are the same.
   223    ASSERT_THAT(dup3(fd.get(), fd.get(), 0), SyscallFailsWithErrno(EINVAL));
   224  }
   225  
   226  TEST(DupTest, Dup3WithOpath) {
   227    auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
   228    FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_PATH));
   229    EXPECT_THAT(fcntl(fd.get(), F_GETFD), SyscallSucceedsWithValue(0));
   230    int flags;
   231    ASSERT_THAT(flags = fcntl(fd.get(), F_GETFL), SyscallSucceeds());
   232  
   233    // Regular dup once.
   234    FileDescriptor nfd = ASSERT_NO_ERRNO_AND_VALUE(fd.Dup());
   235    ASSERT_NE(fd.get(), nfd.get());
   236    ASSERT_NO_ERRNO(CheckSameFile(fd, nfd));
   237  
   238    // Dup over the file above, check that it has no CLOEXEC.
   239    nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), 0));
   240    ASSERT_NO_ERRNO(CheckSameFile(fd, nfd));
   241    EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(0));
   242    EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(flags));
   243  
   244    // Dup over the file again, check that it does not CLOEXEC.
   245    nfd = ASSERT_NO_ERRNO_AND_VALUE(Dup3(fd, nfd.release(), O_CLOEXEC));
   246    ASSERT_NO_ERRNO(CheckSameFile(fd, nfd));
   247    EXPECT_THAT(fcntl(nfd.get(), F_GETFD), SyscallSucceedsWithValue(FD_CLOEXEC));
   248    EXPECT_THAT(fcntl(nfd.get(), F_GETFL), SyscallSucceedsWithValue(flags));
   249  }
   250  
   251  }  // namespace
   252  
   253  }  // namespace testing
   254  }  // namespace gvisor