gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/msync.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 <sys/mman.h>
    16  #include <unistd.h>
    17  
    18  #include <functional>
    19  #include <string>
    20  #include <utility>
    21  #include <vector>
    22  
    23  #include "gmock/gmock.h"
    24  #include "gtest/gtest.h"
    25  #include "test/util/file_descriptor.h"
    26  #include "test/util/memory_util.h"
    27  #include "test/util/posix_error.h"
    28  #include "test/util/temp_path.h"
    29  #include "test/util/test_util.h"
    30  
    31  namespace gvisor {
    32  namespace testing {
    33  
    34  namespace {
    35  
    36  // Parameters for msync tests. Use a std::tuple so we can use
    37  // ::testing::Combine.
    38  using MsyncTestParam =
    39      std::tuple<int,                                    // msync flags
    40                 std::function<PosixErrorOr<Mapping>()>  // returns mapping to
    41                                                         // msync
    42                 >;
    43  
    44  class MsyncParameterizedTest : public ::testing::TestWithParam<MsyncTestParam> {
    45   protected:
    46    int msync_flags() const { return std::get<0>(GetParam()); }
    47  
    48    PosixErrorOr<Mapping> GetMapping() const { return std::get<1>(GetParam())(); }
    49  };
    50  
    51  // All valid msync(2) flag combinations, not including MS_INVALIDATE. ("Linux
    52  // permits a call to msync() that specifies neither [MS_SYNC or MS_ASYNC], with
    53  // semantics that are (currently) equivalent to specifying MS_ASYNC." -
    54  // msync(2))
    55  constexpr std::initializer_list<int> kMsyncFlags = {MS_SYNC, MS_ASYNC, 0};
    56  
    57  // Returns functions that return mappings that should be successfully
    58  // msync()able.
    59  std::vector<std::function<PosixErrorOr<Mapping>()>> SyncableMappings() {
    60    std::vector<std::function<PosixErrorOr<Mapping>()>> funcs;
    61    for (bool const writable : {false, true}) {
    62      for (int const mflags : {MAP_PRIVATE, MAP_SHARED}) {
    63        int const prot = PROT_READ | (writable ? PROT_WRITE : 0);
    64        int const oflags = O_CREAT | (writable ? O_RDWR : O_RDONLY);
    65        funcs.push_back([=] { return MmapAnon(kPageSize, prot, mflags); });
    66        funcs.push_back([=]() -> PosixErrorOr<Mapping> {
    67          std::string const path = NewTempAbsPath();
    68          ASSIGN_OR_RETURN_ERRNO(auto fd, Open(path, oflags, 0644));
    69          // Don't unlink the file since that breaks save/restore. Just let the
    70          // test infrastructure clean up all of our temporary files when we're
    71          // done.
    72          return Mmap(nullptr, kPageSize, prot, mflags, fd.get(), 0);
    73        });
    74      }
    75    }
    76    return funcs;
    77  }
    78  
    79  PosixErrorOr<Mapping> NoMappings() {
    80    return PosixError(EINVAL, "unexpected attempt to create a mapping");
    81  }
    82  
    83  // "Fixture" for msync tests that hold for all valid flags, but do not create
    84  // mappings.
    85  using MsyncNoMappingTest = MsyncParameterizedTest;
    86  
    87  TEST_P(MsyncNoMappingTest, UnmappedAddressWithZeroLengthSucceeds) {
    88    EXPECT_THAT(msync(nullptr, 0, msync_flags()), SyscallSucceeds());
    89  }
    90  
    91  TEST_P(MsyncNoMappingTest, UnmappedAddressWithNonzeroLengthFails) {
    92    EXPECT_THAT(msync(nullptr, kPageSize, msync_flags()),
    93                SyscallFailsWithErrno(ENOMEM));
    94  }
    95  
    96  INSTANTIATE_TEST_SUITE_P(All, MsyncNoMappingTest,
    97                           ::testing::Combine(::testing::ValuesIn(kMsyncFlags),
    98                                              ::testing::Values(NoMappings)));
    99  
   100  // "Fixture" for msync tests that are not parameterized by msync flags, but do
   101  // create mappings.
   102  using MsyncNoFlagsTest = MsyncParameterizedTest;
   103  
   104  TEST_P(MsyncNoFlagsTest, BothSyncAndAsyncFails) {
   105    auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping());
   106    EXPECT_THAT(msync(m.ptr(), m.len(), MS_SYNC | MS_ASYNC),
   107                SyscallFailsWithErrno(EINVAL));
   108  }
   109  
   110  INSTANTIATE_TEST_SUITE_P(
   111      All, MsyncNoFlagsTest,
   112      ::testing::Combine(::testing::Values(0),  // ignored
   113                         ::testing::ValuesIn(SyncableMappings())));
   114  
   115  // "Fixture" for msync tests parameterized by both msync flags and sources of
   116  // mappings.
   117  using MsyncFullParamTest = MsyncParameterizedTest;
   118  
   119  TEST_P(MsyncFullParamTest, NormallySucceeds) {
   120    auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping());
   121    EXPECT_THAT(msync(m.ptr(), m.len(), msync_flags()), SyscallSucceeds());
   122  }
   123  
   124  TEST_P(MsyncFullParamTest, UnalignedLengthSucceeds) {
   125    auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping());
   126    EXPECT_THAT(msync(m.ptr(), m.len() - 1, msync_flags()), SyscallSucceeds());
   127  }
   128  
   129  TEST_P(MsyncFullParamTest, UnalignedAddressFails) {
   130    auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping());
   131    EXPECT_THAT(
   132        msync(reinterpret_cast<void*>(m.addr() + 1), m.len() - 1, msync_flags()),
   133        SyscallFailsWithErrno(EINVAL));
   134  }
   135  
   136  TEST_P(MsyncFullParamTest, InvalidateUnlockedSucceeds) {
   137    auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping());
   138    EXPECT_THAT(msync(m.ptr(), m.len(), msync_flags() | MS_INVALIDATE),
   139                SyscallSucceeds());
   140  }
   141  
   142  // The test for MS_INVALIDATE on mlocked pages is in mlock.cc since it requires
   143  // probing for mlock support.
   144  
   145  INSTANTIATE_TEST_SUITE_P(
   146      All, MsyncFullParamTest,
   147      ::testing::Combine(::testing::ValuesIn(kMsyncFlags),
   148                         ::testing::ValuesIn(SyncableMappings())));
   149  
   150  }  // namespace
   151  
   152  }  // namespace testing
   153  }  // namespace gvisor