1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/memory/platform_shared_memory_region.h"
6
7 #include "base/memory/shared_memory_mapping.h"
8 #include "base/process/process_metrics.h"
9 #include "base/sys_info.h"
10 #include "base/test/gtest_util.h"
11 #include "base/test/test_shared_memory_util.h"
12 #include "build/build_config.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 #if defined(OS_MACOSX) && !defined(OS_IOS)
16 #include <mach/mach_vm.h>
17 #endif
18
19 namespace base {
20 namespace subtle {
21
22 const size_t kRegionSize = 1024;
23
24 class PlatformSharedMemoryRegionTest : public ::testing::Test {};
25
26 // Tests that a default constructed region is invalid and produces invalid
27 // mappings.
TEST_F(PlatformSharedMemoryRegionTest,DefaultConstructedRegionIsInvalid)28 TEST_F(PlatformSharedMemoryRegionTest, DefaultConstructedRegionIsInvalid) {
29 PlatformSharedMemoryRegion region;
30 EXPECT_FALSE(region.IsValid());
31 WritableSharedMemoryMapping mapping = MapForTesting(®ion);
32 EXPECT_FALSE(mapping.IsValid());
33 PlatformSharedMemoryRegion duplicate = region.Duplicate();
34 EXPECT_FALSE(duplicate.IsValid());
35 EXPECT_FALSE(region.ConvertToReadOnly());
36 }
37
38 // Tests that creating a region of 0 size returns an invalid region.
TEST_F(PlatformSharedMemoryRegionTest,CreateRegionOfZeroSizeIsInvalid)39 TEST_F(PlatformSharedMemoryRegionTest, CreateRegionOfZeroSizeIsInvalid) {
40 PlatformSharedMemoryRegion region =
41 PlatformSharedMemoryRegion::CreateWritable(0);
42 EXPECT_FALSE(region.IsValid());
43
44 PlatformSharedMemoryRegion region2 =
45 PlatformSharedMemoryRegion::CreateUnsafe(0);
46 EXPECT_FALSE(region2.IsValid());
47 }
48
49 // Tests that creating a region of size bigger than the integer max value
50 // returns an invalid region.
TEST_F(PlatformSharedMemoryRegionTest,CreateTooLargeRegionIsInvalid)51 TEST_F(PlatformSharedMemoryRegionTest, CreateTooLargeRegionIsInvalid) {
52 size_t too_large_region_size =
53 static_cast<size_t>(std::numeric_limits<int>::max()) + 1;
54 PlatformSharedMemoryRegion region =
55 PlatformSharedMemoryRegion::CreateWritable(too_large_region_size);
56 EXPECT_FALSE(region.IsValid());
57
58 PlatformSharedMemoryRegion region2 =
59 PlatformSharedMemoryRegion::CreateUnsafe(too_large_region_size);
60 EXPECT_FALSE(region2.IsValid());
61 }
62
63 // Tests that regions consistently report their size as the size requested at
64 // creation time even if their allocation size is larger due to platform
65 // constraints.
TEST_F(PlatformSharedMemoryRegionTest,ReportedSizeIsRequestedSize)66 TEST_F(PlatformSharedMemoryRegionTest, ReportedSizeIsRequestedSize) {
67 constexpr size_t kTestSizes[] = {1, 2, 3, 64, 4096, 1024 * 1024};
68 for (size_t size : kTestSizes) {
69 PlatformSharedMemoryRegion region =
70 PlatformSharedMemoryRegion::CreateWritable(size);
71 EXPECT_EQ(region.GetSize(), size);
72
73 region.ConvertToReadOnly();
74 EXPECT_EQ(region.GetSize(), size);
75 }
76 }
77
78 // Tests that a writable region can be converted to read-only.
TEST_F(PlatformSharedMemoryRegionTest,ConvertWritableToReadOnly)79 TEST_F(PlatformSharedMemoryRegionTest, ConvertWritableToReadOnly) {
80 PlatformSharedMemoryRegion region =
81 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
82 ASSERT_TRUE(region.IsValid());
83 EXPECT_EQ(region.GetMode(), PlatformSharedMemoryRegion::Mode::kWritable);
84 ASSERT_TRUE(region.ConvertToReadOnly());
85 EXPECT_EQ(region.GetMode(), PlatformSharedMemoryRegion::Mode::kReadOnly);
86 }
87
88 // Tests that a writable region can be converted to unsafe.
TEST_F(PlatformSharedMemoryRegionTest,ConvertWritableToUnsafe)89 TEST_F(PlatformSharedMemoryRegionTest, ConvertWritableToUnsafe) {
90 PlatformSharedMemoryRegion region =
91 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
92 ASSERT_TRUE(region.IsValid());
93 EXPECT_EQ(region.GetMode(), PlatformSharedMemoryRegion::Mode::kWritable);
94 ASSERT_TRUE(region.ConvertToUnsafe());
95 EXPECT_EQ(region.GetMode(), PlatformSharedMemoryRegion::Mode::kUnsafe);
96 }
97
98 // Tests that the platform-specific handle converted to read-only cannot be used
99 // to perform a writable mapping with low-level system APIs like mmap().
TEST_F(PlatformSharedMemoryRegionTest,ReadOnlyHandleIsNotWritable)100 TEST_F(PlatformSharedMemoryRegionTest, ReadOnlyHandleIsNotWritable) {
101 PlatformSharedMemoryRegion region =
102 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
103 ASSERT_TRUE(region.IsValid());
104 EXPECT_TRUE(region.ConvertToReadOnly());
105 EXPECT_EQ(region.GetMode(), PlatformSharedMemoryRegion::Mode::kReadOnly);
106 EXPECT_TRUE(
107 CheckReadOnlyPlatformSharedMemoryRegionForTesting(std::move(region)));
108 }
109
110 // Tests that the PassPlatformHandle() call invalidates the region.
TEST_F(PlatformSharedMemoryRegionTest,InvalidAfterPass)111 TEST_F(PlatformSharedMemoryRegionTest, InvalidAfterPass) {
112 PlatformSharedMemoryRegion region =
113 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
114 ASSERT_TRUE(region.IsValid());
115 ignore_result(region.PassPlatformHandle());
116 EXPECT_FALSE(region.IsValid());
117 }
118
119 // Tests that the region is invalid after move.
TEST_F(PlatformSharedMemoryRegionTest,InvalidAfterMove)120 TEST_F(PlatformSharedMemoryRegionTest, InvalidAfterMove) {
121 PlatformSharedMemoryRegion region =
122 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
123 ASSERT_TRUE(region.IsValid());
124 PlatformSharedMemoryRegion moved_region = std::move(region);
125 EXPECT_FALSE(region.IsValid());
126 EXPECT_TRUE(moved_region.IsValid());
127 }
128
129 // Tests that calling Take() with the size parameter equal to zero returns an
130 // invalid region.
TEST_F(PlatformSharedMemoryRegionTest,TakeRegionOfZeroSizeIsInvalid)131 TEST_F(PlatformSharedMemoryRegionTest, TakeRegionOfZeroSizeIsInvalid) {
132 PlatformSharedMemoryRegion region =
133 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
134 ASSERT_TRUE(region.IsValid());
135 PlatformSharedMemoryRegion region2 = PlatformSharedMemoryRegion::Take(
136 region.PassPlatformHandle(), region.GetMode(), 0, region.GetGUID());
137 EXPECT_FALSE(region2.IsValid());
138 }
139
140 // Tests that calling Take() with the size parameter bigger than the integer max
141 // value returns an invalid region.
TEST_F(PlatformSharedMemoryRegionTest,TakeTooLargeRegionIsInvalid)142 TEST_F(PlatformSharedMemoryRegionTest, TakeTooLargeRegionIsInvalid) {
143 PlatformSharedMemoryRegion region =
144 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
145 ASSERT_TRUE(region.IsValid());
146 PlatformSharedMemoryRegion region2 = PlatformSharedMemoryRegion::Take(
147 region.PassPlatformHandle(), region.GetMode(),
148 static_cast<size_t>(std::numeric_limits<int>::max()) + 1,
149 region.GetGUID());
150 EXPECT_FALSE(region2.IsValid());
151 }
152
153 // Tests that mapping bytes out of the region limits fails.
TEST_F(PlatformSharedMemoryRegionTest,MapAtOutOfTheRegionLimitsTest)154 TEST_F(PlatformSharedMemoryRegionTest, MapAtOutOfTheRegionLimitsTest) {
155 PlatformSharedMemoryRegion region =
156 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
157 ASSERT_TRUE(region.IsValid());
158 WritableSharedMemoryMapping mapping =
159 MapAtForTesting(®ion, 0, region.GetSize() + 1);
160 EXPECT_FALSE(mapping.IsValid());
161 }
162
163 // Tests that mapping with a size and offset causing overflow fails.
TEST_F(PlatformSharedMemoryRegionTest,MapAtWithOverflowTest)164 TEST_F(PlatformSharedMemoryRegionTest, MapAtWithOverflowTest) {
165 PlatformSharedMemoryRegion region =
166 PlatformSharedMemoryRegion::CreateWritable(
167 SysInfo::VMAllocationGranularity() * 2);
168 ASSERT_TRUE(region.IsValid());
169 size_t size = std::numeric_limits<size_t>::max();
170 size_t offset = SysInfo::VMAllocationGranularity();
171 // |size| + |offset| should be below the region size due to overflow but
172 // mapping a region with these parameters should be invalid.
173 EXPECT_LT(size + offset, region.GetSize());
174 WritableSharedMemoryMapping mapping = MapAtForTesting(®ion, offset, size);
175 EXPECT_FALSE(mapping.IsValid());
176 }
177
178 #if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MACOSX)
179 // Tests that the second handle is closed after a conversion to read-only on
180 // POSIX.
TEST_F(PlatformSharedMemoryRegionTest,ConvertToReadOnlyInvalidatesSecondHandle)181 TEST_F(PlatformSharedMemoryRegionTest,
182 ConvertToReadOnlyInvalidatesSecondHandle) {
183 PlatformSharedMemoryRegion region =
184 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
185 ASSERT_TRUE(region.IsValid());
186 ASSERT_TRUE(region.ConvertToReadOnly());
187 FDPair fds = region.GetPlatformHandle();
188 EXPECT_LT(fds.readonly_fd, 0);
189 }
190
191 // Tests that the second handle is closed after a conversion to unsafe on
192 // POSIX.
TEST_F(PlatformSharedMemoryRegionTest,ConvertToUnsafeInvalidatesSecondHandle)193 TEST_F(PlatformSharedMemoryRegionTest, ConvertToUnsafeInvalidatesSecondHandle) {
194 PlatformSharedMemoryRegion region =
195 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
196 ASSERT_TRUE(region.IsValid());
197 ASSERT_TRUE(region.ConvertToUnsafe());
198 FDPair fds = region.GetPlatformHandle();
199 EXPECT_LT(fds.readonly_fd, 0);
200 }
201 #endif
202
203 #if defined(OS_MACOSX) && !defined(OS_IOS)
204 // Tests that protection bits are set correctly for read-only region on MacOS.
TEST_F(PlatformSharedMemoryRegionTest,MapCurrentAndMaxProtectionSetCorrectly)205 TEST_F(PlatformSharedMemoryRegionTest, MapCurrentAndMaxProtectionSetCorrectly) {
206 PlatformSharedMemoryRegion region =
207 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
208 ASSERT_TRUE(region.IsValid());
209 ASSERT_TRUE(region.ConvertToReadOnly());
210 WritableSharedMemoryMapping ro_mapping = MapForTesting(®ion);
211 ASSERT_TRUE(ro_mapping.IsValid());
212
213 vm_region_basic_info_64 basic_info;
214 mach_vm_size_t dummy_size = 0;
215 void* temp_addr = ro_mapping.memory();
216 MachVMRegionResult result = GetBasicInfo(
217 mach_task_self(), &dummy_size,
218 reinterpret_cast<mach_vm_address_t*>(&temp_addr), &basic_info);
219 EXPECT_EQ(result, MachVMRegionResult::Success);
220 EXPECT_EQ(basic_info.protection & VM_PROT_ALL, VM_PROT_READ);
221 EXPECT_EQ(basic_info.max_protection & VM_PROT_ALL, VM_PROT_READ);
222 }
223 #endif
224
225 // Tests that platform handle permissions are checked correctly.
TEST_F(PlatformSharedMemoryRegionTest,CheckPlatformHandlePermissionsCorrespondToMode)226 TEST_F(PlatformSharedMemoryRegionTest,
227 CheckPlatformHandlePermissionsCorrespondToMode) {
228 using Mode = PlatformSharedMemoryRegion::Mode;
229 auto check = [](const PlatformSharedMemoryRegion& region,
230 PlatformSharedMemoryRegion::Mode mode) {
231 return PlatformSharedMemoryRegion::
232 CheckPlatformHandlePermissionsCorrespondToMode(
233 region.GetPlatformHandle(), mode, region.GetSize());
234 };
235
236 // Check kWritable region.
237 PlatformSharedMemoryRegion region =
238 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
239 ASSERT_TRUE(region.IsValid());
240 EXPECT_TRUE(check(region, Mode::kWritable));
241 EXPECT_FALSE(check(region, Mode::kReadOnly));
242
243 // Check kReadOnly region.
244 ASSERT_TRUE(region.ConvertToReadOnly());
245 EXPECT_TRUE(check(region, Mode::kReadOnly));
246 EXPECT_FALSE(check(region, Mode::kWritable));
247 EXPECT_FALSE(check(region, Mode::kUnsafe));
248
249 // Check kUnsafe region.
250 PlatformSharedMemoryRegion region2 =
251 PlatformSharedMemoryRegion::CreateUnsafe(kRegionSize);
252 ASSERT_TRUE(region2.IsValid());
253 EXPECT_TRUE(check(region2, Mode::kUnsafe));
254 EXPECT_FALSE(check(region2, Mode::kReadOnly));
255 }
256
257 // Tests that it's impossible to create read-only platform shared memory region.
TEST_F(PlatformSharedMemoryRegionTest,CreateReadOnlyRegionDeathTest)258 TEST_F(PlatformSharedMemoryRegionTest, CreateReadOnlyRegionDeathTest) {
259 #ifdef OFFICIAL_BUILD
260 // The official build does not print the reason a CHECK failed.
261 const char kErrorRegex[] = "";
262 #else
263 const char kErrorRegex[] =
264 "Creating a region in read-only mode will lead to this region being "
265 "non-modifiable";
266 #endif
267 EXPECT_DEATH_IF_SUPPORTED(
268 PlatformSharedMemoryRegion::Create(
269 PlatformSharedMemoryRegion::Mode::kReadOnly, kRegionSize),
270 kErrorRegex);
271 }
272
273 // Tests that it's prohibited to duplicate a writable region.
TEST_F(PlatformSharedMemoryRegionTest,DuplicateWritableRegionDeathTest)274 TEST_F(PlatformSharedMemoryRegionTest, DuplicateWritableRegionDeathTest) {
275 #ifdef OFFICIAL_BUILD
276 const char kErrorRegex[] = "";
277 #else
278 const char kErrorRegex[] =
279 "Duplicating a writable shared memory region is prohibited";
280 #endif
281 PlatformSharedMemoryRegion region =
282 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
283 ASSERT_TRUE(region.IsValid());
284 EXPECT_DEATH_IF_SUPPORTED(region.Duplicate(), kErrorRegex);
285 }
286
287 // Tests that it's prohibited to convert an unsafe region to read-only.
TEST_F(PlatformSharedMemoryRegionTest,UnsafeRegionConvertToReadOnlyDeathTest)288 TEST_F(PlatformSharedMemoryRegionTest, UnsafeRegionConvertToReadOnlyDeathTest) {
289 #ifdef OFFICIAL_BUILD
290 const char kErrorRegex[] = "";
291 #else
292 const char kErrorRegex[] =
293 "Only writable shared memory region can be converted to read-only";
294 #endif
295 PlatformSharedMemoryRegion region =
296 PlatformSharedMemoryRegion::CreateUnsafe(kRegionSize);
297 ASSERT_TRUE(region.IsValid());
298 EXPECT_DEATH_IF_SUPPORTED(region.ConvertToReadOnly(), kErrorRegex);
299 }
300
301 // Tests that it's prohibited to convert a read-only region to read-only.
TEST_F(PlatformSharedMemoryRegionTest,ReadOnlyRegionConvertToReadOnlyDeathTest)302 TEST_F(PlatformSharedMemoryRegionTest,
303 ReadOnlyRegionConvertToReadOnlyDeathTest) {
304 #ifdef OFFICIAL_BUILD
305 const char kErrorRegex[] = "";
306 #else
307 const char kErrorRegex[] =
308 "Only writable shared memory region can be converted to read-only";
309 #endif
310 PlatformSharedMemoryRegion region =
311 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
312 ASSERT_TRUE(region.IsValid());
313 EXPECT_TRUE(region.ConvertToReadOnly());
314 EXPECT_DEATH_IF_SUPPORTED(region.ConvertToReadOnly(), kErrorRegex);
315 }
316
317 // Tests that it's prohibited to convert a read-only region to unsafe.
TEST_F(PlatformSharedMemoryRegionTest,ReadOnlyRegionConvertToUnsafeDeathTest)318 TEST_F(PlatformSharedMemoryRegionTest, ReadOnlyRegionConvertToUnsafeDeathTest) {
319 #ifdef OFFICIAL_BUILD
320 const char kErrorRegex[] = "";
321 #else
322 const char kErrorRegex[] =
323 "Only writable shared memory region can be converted to unsafe";
324 #endif
325 PlatformSharedMemoryRegion region =
326 PlatformSharedMemoryRegion::CreateWritable(kRegionSize);
327 ASSERT_TRUE(region.IsValid());
328 ASSERT_TRUE(region.ConvertToReadOnly());
329 EXPECT_DEATH_IF_SUPPORTED(region.ConvertToUnsafe(), kErrorRegex);
330 }
331
332 // Tests that it's prohibited to convert an unsafe region to unsafe.
TEST_F(PlatformSharedMemoryRegionTest,UnsafeRegionConvertToUnsafeDeathTest)333 TEST_F(PlatformSharedMemoryRegionTest, UnsafeRegionConvertToUnsafeDeathTest) {
334 #ifdef OFFICIAL_BUILD
335 const char kErrorRegex[] = "";
336 #else
337 const char kErrorRegex[] =
338 "Only writable shared memory region can be converted to unsafe";
339 #endif
340 PlatformSharedMemoryRegion region =
341 PlatformSharedMemoryRegion::CreateUnsafe(kRegionSize);
342 ASSERT_TRUE(region.IsValid());
343 EXPECT_DEATH_IF_SUPPORTED(region.ConvertToUnsafe(), kErrorRegex);
344 }
345
346 } // namespace subtle
347 } // namespace base
348