diff options
Diffstat (limited to 'test/syscalls/linux/msync.cc')
-rw-r--r-- | test/syscalls/linux/msync.cc | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/test/syscalls/linux/msync.cc b/test/syscalls/linux/msync.cc new file mode 100644 index 000000000..2b2b6aef9 --- /dev/null +++ b/test/syscalls/linux/msync.cc @@ -0,0 +1,151 @@ +// 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. + +#include <sys/mman.h> +#include <unistd.h> + +#include <functional> +#include <string> +#include <utility> +#include <vector> + +#include "test/util/file_descriptor.h" +#include "test/util/memory_util.h" +#include "test/util/posix_error.h" +#include "test/util/temp_path.h" +#include "test/util/test_util.h" + +namespace gvisor { +namespace testing { + +namespace { + +// Parameters for msync tests. Use a std::tuple so we can use +// ::testing::Combine. +using MsyncTestParam = + std::tuple<int, // msync flags + std::function<PosixErrorOr<Mapping>()> // returns mapping to + // msync + >; + +class MsyncParameterizedTest : public ::testing::TestWithParam<MsyncTestParam> { + protected: + int msync_flags() const { return std::get<0>(GetParam()); } + + PosixErrorOr<Mapping> GetMapping() const { return std::get<1>(GetParam())(); } +}; + +// All valid msync(2) flag combinations, not including MS_INVALIDATE. ("Linux +// permits a call to msync() that specifies neither [MS_SYNC or MS_ASYNC], with +// semantics that are (currently) equivalent to specifying MS_ASYNC." - +// msync(2)) +constexpr std::initializer_list<int> kMsyncFlags = {MS_SYNC, MS_ASYNC, 0}; + +// Returns functions that return mappings that should be successfully +// msync()able. +std::vector<std::function<PosixErrorOr<Mapping>()>> SyncableMappings() { + std::vector<std::function<PosixErrorOr<Mapping>()>> funcs; + for (bool const writable : {false, true}) { + for (int const mflags : {MAP_PRIVATE, MAP_SHARED}) { + int const prot = PROT_READ | (writable ? PROT_WRITE : 0); + int const oflags = O_CREAT | (writable ? O_RDWR : O_RDONLY); + funcs.push_back([=] { return MmapAnon(kPageSize, prot, mflags); }); + funcs.push_back([=]() -> PosixErrorOr<Mapping> { + std::string const path = NewTempAbsPath(); + ASSIGN_OR_RETURN_ERRNO(auto fd, Open(path, oflags, 0644)); + // Don't unlink the file since that breaks save/restore. Just let the + // test infrastructure clean up all of our temporary files when we're + // done. + return Mmap(nullptr, kPageSize, prot, mflags, fd.get(), 0); + }); + } + } + return funcs; +} + +PosixErrorOr<Mapping> NoMappings() { + return PosixError(EINVAL, "unexpected attempt to create a mapping"); +} + +// "Fixture" for msync tests that hold for all valid flags, but do not create +// mappings. +using MsyncNoMappingTest = MsyncParameterizedTest; + +TEST_P(MsyncNoMappingTest, UnmappedAddressWithZeroLengthSucceeds) { + EXPECT_THAT(msync(nullptr, 0, msync_flags()), SyscallSucceeds()); +} + +TEST_P(MsyncNoMappingTest, UnmappedAddressWithNonzeroLengthFails) { + EXPECT_THAT(msync(nullptr, kPageSize, msync_flags()), + SyscallFailsWithErrno(ENOMEM)); +} + +INSTANTIATE_TEST_SUITE_P(All, MsyncNoMappingTest, + ::testing::Combine(::testing::ValuesIn(kMsyncFlags), + ::testing::Values(NoMappings))); + +// "Fixture" for msync tests that are not parameterized by msync flags, but do +// create mappings. +using MsyncNoFlagsTest = MsyncParameterizedTest; + +TEST_P(MsyncNoFlagsTest, BothSyncAndAsyncFails) { + auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); + EXPECT_THAT(msync(m.ptr(), m.len(), MS_SYNC | MS_ASYNC), + SyscallFailsWithErrno(EINVAL)); +} + +INSTANTIATE_TEST_SUITE_P( + All, MsyncNoFlagsTest, + ::testing::Combine(::testing::Values(0), // ignored + ::testing::ValuesIn(SyncableMappings()))); + +// "Fixture" for msync tests parameterized by both msync flags and sources of +// mappings. +using MsyncFullParamTest = MsyncParameterizedTest; + +TEST_P(MsyncFullParamTest, NormallySucceeds) { + auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); + EXPECT_THAT(msync(m.ptr(), m.len(), msync_flags()), SyscallSucceeds()); +} + +TEST_P(MsyncFullParamTest, UnalignedLengthSucceeds) { + auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); + EXPECT_THAT(msync(m.ptr(), m.len() - 1, msync_flags()), SyscallSucceeds()); +} + +TEST_P(MsyncFullParamTest, UnalignedAddressFails) { + auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); + EXPECT_THAT( + msync(reinterpret_cast<void*>(m.addr() + 1), m.len() - 1, msync_flags()), + SyscallFailsWithErrno(EINVAL)); +} + +TEST_P(MsyncFullParamTest, InvalidateUnlockedSucceeds) { + auto m = ASSERT_NO_ERRNO_AND_VALUE(GetMapping()); + EXPECT_THAT(msync(m.ptr(), m.len(), msync_flags() | MS_INVALIDATE), + SyscallSucceeds()); +} + +// The test for MS_INVALIDATE on mlocked pages is in mlock.cc since it requires +// probing for mlock support. + +INSTANTIATE_TEST_SUITE_P( + All, MsyncFullParamTest, + ::testing::Combine(::testing::ValuesIn(kMsyncFlags), + ::testing::ValuesIn(SyncableMappings()))); + +} // namespace + +} // namespace testing +} // namespace gvisor |