| /* | 
 |  * 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 |