gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/close_range.cc (about)

     1  // Copyright 2022 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 <asm-generic/errno-base.h>
    16  #include <unistd.h>
    17  
    18  #include <vector>
    19  
    20  #include "gtest/gtest.h"
    21  #include "absl/base/macros.h"
    22  #include "test/util/file_descriptor.h"
    23  #include "test/util/posix_error.h"
    24  #include "test/util/temp_path.h"
    25  #include "test/util/test_util.h"
    26  #include "test/util/thread_util.h"
    27  
    28  namespace gvisor {
    29  namespace testing {
    30  
    31  namespace {
    32  
    33  #ifndef CLOSE_RANGE_UNSHARE
    34  #define CLOSE_RANGE_UNSHARE (1U << 1)
    35  #endif
    36  #ifndef CLOSE_RANGE_CLOEXEC
    37  #define CLOSE_RANGE_CLOEXEC (1U << 2)
    38  #endif
    39  
    40  #ifndef SYS_close_range
    41  #if defined(__x86_64__) || defined(__aarch64__)
    42  #define SYS_close_range 436
    43  #else
    44  #error "Unknown architecture"
    45  #endif
    46  #endif  // SYS_close_range
    47  
    48  int close_range(unsigned int first, unsigned int last, unsigned int flags) {
    49    return syscall(SYS_close_range, first, last, flags);
    50  }
    51  
    52  class CloseRangeTest : public ::testing::Test {
    53   public:
    54    void CreateFiles(int num_files) {
    55      file_names_.reserve(num_files);
    56      for (int i = 0; i < num_files; ++i) {
    57        file_names_.push_back(NewTempAbsPath());
    58        int fd;
    59        ASSERT_THAT(fd = open(file_names_[i].c_str(), O_CREAT, 0644),
    60                    SyscallSucceeds());
    61        ASSERT_THAT(close(fd), SyscallSucceeds());
    62      }
    63    }
    64  
    65    void OpenFilesRdwr() {
    66      fds_.clear();
    67      fds_.reserve(file_names_.size());
    68      for (std::string &file_name : file_names_) {
    69        int fd;
    70        ASSERT_THAT(fd = open(file_name.c_str(), O_RDWR), SyscallSucceeds());
    71        fds_.push_back(fd);
    72      }
    73    }
    74  
    75   private:
    76    void TearDown() override {
    77      for (std::string &name : file_names_) {
    78        unlink(name.c_str());
    79      }
    80    }
    81  
    82   protected:
    83    std::vector<std::string> file_names_;
    84    std::vector<unsigned int> fds_;
    85  };
    86  
    87  // Base test to confirm that all files in contiguous range get closed.
    88  TEST_F(CloseRangeTest, ContiguousRange) {
    89    SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
    90    int num_files_in_range = 10;
    91    unsigned int flags = 0;
    92  
    93    CreateFiles(num_files_in_range);
    94    OpenFilesRdwr();
    95  
    96    for (int fd : fds_) {
    97      auto ret = ReadAllFd(fd);
    98      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
    99    }
   100  
   101    EXPECT_THAT(close_range(fds_[0], fds_[num_files_in_range - 1], flags),
   102                SyscallSucceeds());
   103    for (int fd : fds_) {
   104      auto ret = ReadAllFd(fd);
   105      EXPECT_THAT(ret, PosixErrorIs(EBADF));
   106    }
   107  }
   108  
   109  // Test to confirm that a range with files already closed in the range still
   110  // closes the remaining files.
   111  TEST_F(CloseRangeTest, RangeWithHoles) {
   112    SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
   113    int num_files_in_range = 10;
   114    unsigned int flags = 0;
   115  
   116    CreateFiles(num_files_in_range);
   117    OpenFilesRdwr();
   118  
   119    for (int fd : fds_) {
   120      auto ret = ReadAllFd(fd);
   121      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   122    }
   123  
   124    EXPECT_THAT(close(fds_[2]), SyscallSucceeds());
   125    EXPECT_THAT(close(fds_[7]), SyscallSucceeds());
   126  
   127    EXPECT_THAT(close_range(fds_[0], fds_[num_files_in_range - 1], flags),
   128                SyscallSucceeds());
   129    for (int fd : fds_) {
   130      auto ret = ReadAllFd(fd);
   131      EXPECT_THAT(ret, PosixErrorIs(EBADF));
   132    }
   133  }
   134  
   135  // Test to confirm that closing a range with fds preceding and following the
   136  // range leaves those other fds open.
   137  TEST_F(CloseRangeTest, RangeInMiddleOfOpenFiles) {
   138    SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
   139    int num_files_in_range = 10;
   140    unsigned int flags = 0;
   141  
   142    CreateFiles(num_files_in_range);
   143    OpenFilesRdwr();
   144  
   145    for (int fd : fds_) {
   146      auto ret = ReadAllFd(fd);
   147      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   148    }
   149  
   150    size_t slice_start = 4;
   151    size_t slice_end = 7;
   152    EXPECT_THAT(close_range(fds_[slice_start], fds_[slice_end], flags),
   153                SyscallSucceeds());
   154    for (int fd :
   155         std::vector(fds_.begin() + slice_start, fds_.begin() + slice_end + 1)) {
   156      auto ret = ReadAllFd(fd);
   157      EXPECT_THAT(ret, PosixErrorIs(EBADF));
   158    }
   159    for (int fd : std::vector(fds_.begin(), fds_.begin() + slice_start)) {
   160      auto ret = ReadAllFd(fd);
   161      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   162    }
   163    for (int fd : std::vector(fds_.begin() + slice_end + 1, fds_.end())) {
   164      auto ret = ReadAllFd(fd);
   165      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   166    }
   167  }
   168  
   169  // Test to confirm that calling close_range on just one file succeeds.
   170  TEST_F(CloseRangeTest, SingleFile) {
   171    SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
   172    int num_files_in_range = 1;
   173    unsigned int flags = 0; /*  */
   174  
   175    CreateFiles(num_files_in_range);
   176    OpenFilesRdwr();
   177  
   178    auto ret = ReadAllFd(fds_[0]);
   179    EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   180  
   181    EXPECT_THAT(close_range(fds_[0], fds_[0], flags), SyscallSucceeds());
   182  
   183    ret = ReadAllFd(fds_[0]);
   184    EXPECT_THAT(ret, PosixErrorIs(EBADF));
   185  }
   186  
   187  // Test to confirm that calling close_range twice on the same range does not
   188  // cause errors.
   189  TEST_F(CloseRangeTest, CallCloseRangeTwice) {
   190    SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
   191    int num_files_in_range = 10;
   192    unsigned int flags = 0;
   193  
   194    CreateFiles(num_files_in_range);
   195    OpenFilesRdwr();
   196  
   197    for (int fd : fds_) {
   198      auto ret = ReadAllFd(fd);
   199      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   200    }
   201  
   202    EXPECT_THAT(close_range(fds_[0], fds_[num_files_in_range - 1], flags),
   203                SyscallSucceeds());
   204    for (int fd : fds_) {
   205      auto ret = ReadAllFd(fd);
   206      EXPECT_THAT(ret, PosixErrorIs(EBADF));
   207    }
   208  
   209    EXPECT_THAT(close_range(fds_[0], fds_[num_files_in_range - 1], flags),
   210                SyscallSucceeds());
   211  }
   212  
   213  // Test that using CLOEXEC flag does not close the file for this process.
   214  TEST_F(CloseRangeTest, CloexecFlagTest) {
   215    SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
   216    int num_files_in_range = 10;
   217    unsigned int flags = CLOSE_RANGE_CLOEXEC;
   218  
   219    // CLOEXEC is only available on >=5.11 kernels.
   220    SKIP_IF(close_range(1, 0, flags) < 0 && errno == EINVAL);
   221  
   222    CreateFiles(num_files_in_range);
   223    OpenFilesRdwr();
   224  
   225    for (int fd : fds_) {
   226      auto ret = ReadAllFd(fd);
   227      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   228    }
   229  
   230    EXPECT_THAT(close_range(fds_[0], fds_[num_files_in_range - 1], flags),
   231                SyscallSucceeds());
   232    for (int fd : fds_) {
   233      auto ret = ReadAllFd(fd);
   234      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   235    }
   236  }
   237  
   238  // Test that using UNSHARE flag still properly closes the files.
   239  TEST_F(CloseRangeTest, UnshareFlagTest) {
   240    SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
   241    int num_files_in_range = 10;
   242    unsigned int flags = CLOSE_RANGE_UNSHARE;
   243  
   244    CreateFiles(num_files_in_range);
   245    OpenFilesRdwr();
   246  
   247    for (int fd : fds_) {
   248      auto ret = ReadAllFd(fd);
   249      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   250    }
   251  
   252    EXPECT_THAT(close_range(fds_[0], fds_[num_files_in_range - 1], flags),
   253                SyscallSucceeds());
   254    for (int fd : fds_) {
   255      auto ret = ReadAllFd(fd);
   256      EXPECT_THAT(ret, PosixErrorIs(EBADF));
   257    }
   258  }
   259  
   260  // Test that using the UNSHARE flag and closing files at the start of the range
   261  // still leaves the latter files opened.
   262  TEST_F(CloseRangeTest, UnshareFlagAndCloseRangeAtStart) {
   263    SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
   264    int num_files_in_range = 10;
   265    unsigned int flags = CLOSE_RANGE_UNSHARE;
   266  
   267    CreateFiles(num_files_in_range);
   268    OpenFilesRdwr();
   269  
   270    for (int fd : fds_) {
   271      auto ret = ReadAllFd(fd);
   272      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   273    }
   274  
   275    size_t range_split = 5;
   276    EXPECT_THAT(close_range(fds_[0], fds_[range_split - 1], flags),
   277                SyscallSucceeds());
   278    for (int fd : std::vector(fds_.begin(), fds_.begin() + range_split)) {
   279      auto ret = ReadAllFd(fd);
   280      EXPECT_THAT(ret, PosixErrorIs(EBADF));
   281    }
   282    for (int fd : std::vector(fds_.begin() + range_split, fds_.end())) {
   283      auto ret = ReadAllFd(fd);
   284      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   285    }
   286  }
   287  
   288  // Test that using the UNSHARE flag and closing files at the end of the range
   289  // still leaves the earlier files opened.
   290  TEST_F(CloseRangeTest, UnshareFlagAndCloseRangeAtEnd) {
   291    SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
   292    int num_files_in_range = 10;
   293    unsigned int flags = CLOSE_RANGE_UNSHARE;
   294  
   295    CreateFiles(num_files_in_range);
   296    OpenFilesRdwr();
   297  
   298    for (int fd : fds_) {
   299      auto ret = ReadAllFd(fd);
   300      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   301    }
   302  
   303    size_t range_split = 5;
   304    EXPECT_THAT(
   305        close_range(fds_[range_split], fds_[num_files_in_range - 1], flags),
   306        SyscallSucceeds());
   307    for (int fd : std::vector(fds_.begin(), fds_.begin() + range_split)) {
   308      auto ret = ReadAllFd(fd);
   309      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   310    }
   311    for (int fd : std::vector(fds_.begin() + range_split, fds_.end())) {
   312      auto ret = ReadAllFd(fd);
   313      EXPECT_THAT(ret, PosixErrorIs(EBADF));
   314    }
   315  }
   316  
   317  // Test that using both CLOEXEC and UNSHARE flags does not close files for this
   318  // process.
   319  TEST_F(CloseRangeTest, CloexecAndUnshareFlagTest) {
   320    SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
   321    int num_files_in_range = 10;
   322    unsigned int flags = CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE;
   323  
   324    // CLOEXEC is only available on >=5.11 kernels.
   325    SKIP_IF(close_range(1, 0, flags) < 0 && errno == EINVAL);
   326  
   327    CreateFiles(num_files_in_range);
   328    OpenFilesRdwr();
   329  
   330    for (int fd : fds_) {
   331      auto ret = ReadAllFd(fd);
   332      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   333    }
   334  
   335    EXPECT_THAT(close_range(fds_[0], fds_[num_files_in_range - 1], flags),
   336                SyscallSucceeds());
   337    for (int fd : fds_) {
   338      auto ret = ReadAllFd(fd);
   339      EXPECT_THAT(ret, IsPosixErrorOkMatcher());
   340    }
   341  }
   342  
   343  // Test that calling with invalid range does not succeed.
   344  TEST_F(CloseRangeTest, RangeFirstGreaterThanLast) {
   345    SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
   346    int num_files_in_range = 10;
   347    unsigned int flags = 0;
   348  
   349    CreateFiles(num_files_in_range);
   350    OpenFilesRdwr();
   351  
   352    EXPECT_THAT(close_range(fds_[num_files_in_range - 1], fds_[0], flags),
   353                SyscallFailsWithErrno(EINVAL));
   354  }
   355  
   356  // Test that calling with invalid flags does not succeed.
   357  TEST_F(CloseRangeTest, InvalidFlags) {
   358    SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
   359    int num_files_in_range = 10;
   360  
   361    CreateFiles(num_files_in_range);
   362    OpenFilesRdwr();
   363  
   364    unsigned int flags = CLOSE_RANGE_CLOEXEC | CLOSE_RANGE_UNSHARE | 0xF;
   365    EXPECT_THAT(close_range(fds_[0], fds_[num_files_in_range - 1], flags),
   366                SyscallFailsWithErrno(EINVAL));
   367  
   368    flags = 0xF0;
   369    EXPECT_THAT(close_range(fds_[0], fds_[num_files_in_range - 1], flags),
   370                SyscallFailsWithErrno(EINVAL));
   371  
   372    flags = CLOSE_RANGE_CLOEXEC | 0xF00;
   373    EXPECT_THAT(close_range(fds_[0], fds_[num_files_in_range - 1], flags),
   374                SyscallFailsWithErrno(EINVAL));
   375  }
   376  
   377  // Test that calling close_range concurrently while creating new files yields
   378  // expected results.
   379  TEST_F(CloseRangeTest, ConcurrentCalls) {
   380    SKIP_IF(!IsRunningOnGvisor() && close_range(1, 0, 0) < 0 && errno == ENOSYS);
   381    const int num_files_in_range = 10;
   382    const unsigned int flags = CLOSE_RANGE_UNSHARE;
   383    const int num_threads = 100;
   384    std::unique_ptr<ScopedThread> threads[num_threads];
   385  
   386    CreateFiles(num_files_in_range);
   387    OpenFilesRdwr();
   388  
   389    auto cr_call = []() {
   390      EXPECT_THAT(close_range(num_files_in_range / 2,
   391                              num_files_in_range + num_threads, flags),
   392                  SyscallSucceeds());
   393    };
   394    auto open_file_call = []() {
   395      auto file = NewTempAbsPath();
   396      EXPECT_THAT(open(file.c_str(), O_CREAT, 0644), SyscallSucceeds());
   397    };
   398  
   399    for (int i = 0; i < num_threads; i++) {
   400      if (i % 2 == 0) {
   401        threads[i] = std::make_unique<ScopedThread>(cr_call);
   402      } else {
   403        threads[i] = std::make_unique<ScopedThread>(open_file_call);
   404      }
   405    }
   406  }
   407  
   408  }  // namespace
   409  
   410  }  // namespace testing
   411  }  // namespace gvisor