| /* |
| * Copyright (C) 2017 The Android Open Source Project |
| * |
| * 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 "src/tracing/ipc/posix_shared_memory.h" |
| |
| #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \ |
| PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \ |
| PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE) |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| |
| #include "perfetto/base/build_config.h" |
| #include "perfetto/ext/base/file_utils.h" |
| #include "perfetto/ext/base/scoped_file.h" |
| #include "perfetto/ext/base/temp_file.h" |
| #include "perfetto/ext/base/utils.h" |
| #include "src/base/test/test_task_runner.h" |
| #include "src/base/test/vm_test_utils.h" |
| #include "src/tracing/ipc/memfd.h" |
| #include "test/gtest_and_gmock.h" |
| |
| namespace perfetto { |
| namespace { |
| |
| bool IsFileDescriptorClosed(int fd) { |
| return lseek(fd, 0, SEEK_CUR) == -1 && errno == EBADF; |
| } |
| |
| TEST(PosixSharedMemoryTest, DestructorUnmapsMemory) { |
| PosixSharedMemory::Factory factory; |
| std::unique_ptr<SharedMemory> shm = |
| factory.CreateSharedMemory(base::GetSysPageSize()); |
| ASSERT_NE(shm.get(), nullptr); |
| void* const shm_start = shm->start(); |
| const size_t shm_size = shm->size(); |
| ASSERT_NE(nullptr, shm_start); |
| ASSERT_EQ(base::GetSysPageSize(), shm_size); |
| |
| memcpy(shm_start, "test", 5); |
| ASSERT_TRUE(base::vm_test_utils::IsMapped(shm_start, shm_size)); |
| |
| shm.reset(); |
| ASSERT_FALSE(base::vm_test_utils::IsMapped(shm_start, shm_size)); |
| } |
| |
| TEST(PosixSharedMemoryTest, DestructorClosesFD) { |
| std::unique_ptr<PosixSharedMemory> shm = |
| PosixSharedMemory::Create(base::GetSysPageSize()); |
| ASSERT_NE(shm.get(), nullptr); |
| int fd = shm->fd(); |
| ASSERT_GE(fd, 0); |
| ASSERT_EQ(static_cast<off_t>(base::GetSysPageSize()), lseek(fd, 0, SEEK_END)); |
| |
| shm.reset(); |
| ASSERT_TRUE(IsFileDescriptorClosed(fd)); |
| } |
| |
| TEST(PosixSharedMemoryTest, AttachToFdWithoutSeals) { |
| base::TempFile tmp_file = base::TempFile::CreateUnlinked(); |
| const int fd_num = tmp_file.fd(); |
| ASSERT_EQ(0, ftruncate(fd_num, static_cast<off_t>(base::GetSysPageSize()))); |
| ASSERT_EQ(7, base::WriteAll(fd_num, "foobar", 7)); |
| |
| std::unique_ptr<PosixSharedMemory> shm = PosixSharedMemory::AttachToFd( |
| tmp_file.ReleaseFD(), /*require_seals_if_supported=*/false); |
| ASSERT_NE(shm.get(), nullptr); |
| void* const shm_start = shm->start(); |
| const size_t shm_size = shm->size(); |
| ASSERT_NE(nullptr, shm_start); |
| ASSERT_EQ(base::GetSysPageSize(), shm_size); |
| ASSERT_EQ(0, memcmp("foobar", shm_start, 7)); |
| |
| ASSERT_FALSE(IsFileDescriptorClosed(fd_num)); |
| |
| shm.reset(); |
| ASSERT_TRUE(IsFileDescriptorClosed(fd_num)); |
| ASSERT_FALSE(base::vm_test_utils::IsMapped(shm_start, shm_size)); |
| } |
| |
| TEST(PosixSharedMemoryTest, AttachToFdRequiresSeals) { |
| base::TempFile tmp_file = base::TempFile::CreateUnlinked(); |
| const int fd_num = tmp_file.fd(); |
| ASSERT_EQ(0, ftruncate(fd_num, static_cast<off_t>(base::GetSysPageSize()))); |
| |
| std::unique_ptr<PosixSharedMemory> shm = |
| PosixSharedMemory::AttachToFd(tmp_file.ReleaseFD()); |
| |
| if (HasMemfdSupport()) { |
| EXPECT_EQ(shm.get(), nullptr); |
| } else { |
| ASSERT_NE(shm.get(), nullptr); |
| EXPECT_NE(shm->start(), nullptr); |
| } |
| } |
| |
| TEST(PosixSharedMemoryTest, CreateAndMap) { |
| // Deliberately trying to cover cases where the shm size is smaller than the |
| // system page size (crbug.com/1116576). |
| const size_t kLessThanAPage = 2048; |
| std::unique_ptr<PosixSharedMemory> shm = |
| PosixSharedMemory::Create(kLessThanAPage); |
| void* const shm_start = shm->start(); |
| const size_t shm_size = shm->size(); |
| ASSERT_NE(shm_start, nullptr); |
| ASSERT_EQ(shm_size, kLessThanAPage); |
| |
| memcpy(shm_start, "test", 5); |
| ASSERT_TRUE(base::vm_test_utils::IsMapped(shm_start, shm_size)); |
| |
| base::ScopedFile shm_fd2(dup(shm->fd())); |
| std::unique_ptr<PosixSharedMemory> shm2 = |
| PosixSharedMemory::AttachToFd(std::move(shm_fd2)); |
| ASSERT_NE(shm2.get(), nullptr); |
| void* const shm2_start = shm2->start(); |
| const size_t shm2_size = shm2->size(); |
| ASSERT_NE(shm2_start, nullptr); |
| ASSERT_EQ(shm2_size, shm_size); |
| |
| ASSERT_EQ(0, memcmp("test", shm2->start(), 5)); |
| ASSERT_TRUE(base::vm_test_utils::IsMapped(shm2_start, shm2_size)); |
| |
| shm2.reset(); |
| ASSERT_FALSE(base::vm_test_utils::IsMapped(shm2_start, shm2_size)); |
| ASSERT_TRUE(base::vm_test_utils::IsMapped(shm_start, shm_size)); |
| |
| shm.reset(); |
| ASSERT_FALSE(base::vm_test_utils::IsMapped(shm2_start, shm2_size)); |
| ASSERT_FALSE(base::vm_test_utils::IsMapped(shm_start, shm_size)); |
| } |
| |
| } // namespace |
| } // namespace perfetto |
| #endif // OS_LINUX || OS_ANDROID || OS_APPLE |