1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/tracing/ipc/posix_shared_memory.h"
18
19 #if PERFETTO_BUILDFLAG(PERFETTO_OS_LINUX) || \
20 PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) || \
21 PERFETTO_BUILDFLAG(PERFETTO_OS_APPLE)
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29
30 #include "perfetto/base/build_config.h"
31 #include "perfetto/ext/base/file_utils.h"
32 #include "perfetto/ext/base/scoped_file.h"
33 #include "perfetto/ext/base/temp_file.h"
34 #include "perfetto/ext/base/utils.h"
35 #include "src/base/test/test_task_runner.h"
36 #include "src/base/test/vm_test_utils.h"
37 #include "src/tracing/ipc/memfd.h"
38 #include "test/gtest_and_gmock.h"
39
40 namespace perfetto {
41 namespace {
42
IsFileDescriptorClosed(int fd)43 bool IsFileDescriptorClosed(int fd) {
44 return lseek(fd, 0, SEEK_CUR) == -1 && errno == EBADF;
45 }
46
TEST(PosixSharedMemoryTest,DestructorUnmapsMemory)47 TEST(PosixSharedMemoryTest, DestructorUnmapsMemory) {
48 PosixSharedMemory::Factory factory;
49 std::unique_ptr<SharedMemory> shm =
50 factory.CreateSharedMemory(base::GetSysPageSize());
51 ASSERT_NE(shm.get(), nullptr);
52 void* const shm_start = shm->start();
53 const size_t shm_size = shm->size();
54 ASSERT_NE(nullptr, shm_start);
55 ASSERT_EQ(base::GetSysPageSize(), shm_size);
56
57 memcpy(shm_start, "test", 5);
58 ASSERT_TRUE(base::vm_test_utils::IsMapped(shm_start, shm_size));
59
60 shm.reset();
61 ASSERT_FALSE(base::vm_test_utils::IsMapped(shm_start, shm_size));
62 }
63
TEST(PosixSharedMemoryTest,DestructorClosesFD)64 TEST(PosixSharedMemoryTest, DestructorClosesFD) {
65 std::unique_ptr<PosixSharedMemory> shm =
66 PosixSharedMemory::Create(base::GetSysPageSize());
67 ASSERT_NE(shm.get(), nullptr);
68 int fd = shm->fd();
69 ASSERT_GE(fd, 0);
70 ASSERT_EQ(static_cast<off_t>(base::GetSysPageSize()), lseek(fd, 0, SEEK_END));
71
72 shm.reset();
73 ASSERT_TRUE(IsFileDescriptorClosed(fd));
74 }
75
TEST(PosixSharedMemoryTest,AttachToFdWithoutSeals)76 TEST(PosixSharedMemoryTest, AttachToFdWithoutSeals) {
77 base::TempFile tmp_file = base::TempFile::CreateUnlinked();
78 const int fd_num = tmp_file.fd();
79 ASSERT_EQ(0, ftruncate(fd_num, static_cast<off_t>(base::GetSysPageSize())));
80 ASSERT_EQ(7, base::WriteAll(fd_num, "foobar", 7));
81
82 std::unique_ptr<PosixSharedMemory> shm = PosixSharedMemory::AttachToFd(
83 tmp_file.ReleaseFD(), /*require_seals_if_supported=*/false);
84 ASSERT_NE(shm.get(), nullptr);
85 void* const shm_start = shm->start();
86 const size_t shm_size = shm->size();
87 ASSERT_NE(nullptr, shm_start);
88 ASSERT_EQ(base::GetSysPageSize(), shm_size);
89 ASSERT_EQ(0, memcmp("foobar", shm_start, 7));
90
91 ASSERT_FALSE(IsFileDescriptorClosed(fd_num));
92
93 shm.reset();
94 ASSERT_TRUE(IsFileDescriptorClosed(fd_num));
95 ASSERT_FALSE(base::vm_test_utils::IsMapped(shm_start, shm_size));
96 }
97
TEST(PosixSharedMemoryTest,AttachToFdRequiresSeals)98 TEST(PosixSharedMemoryTest, AttachToFdRequiresSeals) {
99 base::TempFile tmp_file = base::TempFile::CreateUnlinked();
100 const int fd_num = tmp_file.fd();
101 ASSERT_EQ(0, ftruncate(fd_num, static_cast<off_t>(base::GetSysPageSize())));
102
103 std::unique_ptr<PosixSharedMemory> shm =
104 PosixSharedMemory::AttachToFd(tmp_file.ReleaseFD());
105
106 if (HasMemfdSupport()) {
107 EXPECT_EQ(shm.get(), nullptr);
108 } else {
109 ASSERT_NE(shm.get(), nullptr);
110 EXPECT_NE(shm->start(), nullptr);
111 }
112 }
113
TEST(PosixSharedMemoryTest,CreateAndMap)114 TEST(PosixSharedMemoryTest, CreateAndMap) {
115 // Deliberately trying to cover cases where the shm size is smaller than the
116 // system page size (crbug.com/1116576).
117 const size_t kLessThanAPage = 2048;
118 std::unique_ptr<PosixSharedMemory> shm =
119 PosixSharedMemory::Create(kLessThanAPage);
120 void* const shm_start = shm->start();
121 const size_t shm_size = shm->size();
122 ASSERT_NE(shm_start, nullptr);
123 ASSERT_EQ(shm_size, kLessThanAPage);
124
125 memcpy(shm_start, "test", 5);
126 ASSERT_TRUE(base::vm_test_utils::IsMapped(shm_start, shm_size));
127
128 base::ScopedFile shm_fd2(dup(shm->fd()));
129 std::unique_ptr<PosixSharedMemory> shm2 =
130 PosixSharedMemory::AttachToFd(std::move(shm_fd2));
131 ASSERT_NE(shm2.get(), nullptr);
132 void* const shm2_start = shm2->start();
133 const size_t shm2_size = shm2->size();
134 ASSERT_NE(shm2_start, nullptr);
135 ASSERT_EQ(shm2_size, shm_size);
136
137 ASSERT_EQ(0, memcmp("test", shm2->start(), 5));
138 ASSERT_TRUE(base::vm_test_utils::IsMapped(shm2_start, shm2_size));
139
140 shm2.reset();
141 ASSERT_FALSE(base::vm_test_utils::IsMapped(shm2_start, shm2_size));
142 ASSERT_TRUE(base::vm_test_utils::IsMapped(shm_start, shm_size));
143
144 shm.reset();
145 ASSERT_FALSE(base::vm_test_utils::IsMapped(shm2_start, shm2_size));
146 ASSERT_FALSE(base::vm_test_utils::IsMapped(shm_start, shm_size));
147 }
148
149 } // namespace
150 } // namespace perfetto
151 #endif // OS_LINUX || OS_ANDROID || OS_APPLE
152