// 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 #include #include #include #include #include #include #include "gtest/gtest.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "test/util/file_descriptor.h" #include "test/util/logging.h" #include "test/util/memory_util.h" #include "test/util/temp_path.h" #include "test/util/test_util.h" #include "test/util/thread_util.h" #include "test/util/timer_util.h" namespace gvisor { namespace testing { namespace { // Stress copy-on-write. Attempts to reproduce b/38430174. TEST(PreadvTest, MMConcurrencyStress) { // Fill a one-page file with zeroes (the contents don't really matter). const auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( /* parent = */ GetAbsoluteTestTmpdir(), /* content = */ std::string(kPageSize, 0), TempPath::kDefaultFileMode)); const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_RDONLY)); // Get a one-page private mapping to read to. const Mapping m = ASSERT_NO_ERRNO_AND_VALUE( MmapAnon(kPageSize, PROT_READ | PROT_WRITE, MAP_PRIVATE)); // Repeatedly fork in a separate thread to force the mapping to become // copy-on-write. std::atomic done(false); const ScopedThread t([&] { while (!done.load()) { const pid_t pid = fork(); TEST_CHECK(pid >= 0); if (pid == 0) { // In child. The parent was obviously multithreaded, so it's neither // safe nor necessary to do much more than exit. syscall(SYS_exit_group, 0); } int status; ASSERT_THAT(RetryEINTR(waitpid)(pid, &status, 0), SyscallSucceedsWithValue(pid)); EXPECT_TRUE(WIFEXITED(status) && WEXITSTATUS(status) == 0) << "status = " << status; } }); // Repeatedly read to the mapping. struct iovec iov[2]; iov[0].iov_base = m.ptr(); iov[0].iov_len = kPageSize / 2; iov[1].iov_base = reinterpret_cast(m.addr() + kPageSize / 2); iov[1].iov_len = kPageSize / 2; constexpr absl::Duration kTestDuration = absl::Seconds(5); const absl::Time end = absl::Now() + kTestDuration; while (absl::Now() < end) { // Among other causes, save/restore cycles may cause interruptions resulting // in partial reads, so we don't expect any particular return value. EXPECT_THAT(RetryEINTR(preadv)(fd.get(), iov, 2, 0), SyscallSucceeds()); } // Stop the other thread. done.store(true); // The test passes if it neither deadlocks nor crashes the OS. } // This test calls preadv with an O_PATH fd. TEST(PreadvTest, PreadvWithOpath) { SKIP_IF(IsRunningWithVFS1()); const TempPath file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(file.path(), O_PATH)); struct iovec iov; iov.iov_base = nullptr; iov.iov_len = 0; EXPECT_THAT(preadv(fd.get(), &iov, 1, 0), SyscallFailsWithErrno(EBADF)); } } // namespace } // namespace testing } // namespace gvisor