1 // Copyright 2018 The Chromium Authors
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 <algorithm>
6 #include <utility>
7
8 #include "base/containers/span.h"
9 #include "base/memory/platform_shared_memory_region.h"
10 #include "base/memory/read_only_shared_memory_region.h"
11 #include "base/memory/unsafe_shared_memory_region.h"
12 #include "base/memory/writable_shared_memory_region.h"
13 #include "base/system/sys_info.h"
14 #include "base/test/test_shared_memory_util.h"
15 #include "build/build_config.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17
18 namespace base {
19
20 const size_t kRegionSize = 1024;
21
IsMemoryFilledWithByte(span<const uint8_t> memory,uint8_t byte)22 bool IsMemoryFilledWithByte(span<const uint8_t> memory, uint8_t byte) {
23 for (uint8_t c : memory) {
24 if (c != byte) {
25 return false;
26 }
27 }
28 return true;
29 }
30
31 template <typename SharedMemoryRegionType>
32 class SharedMemoryRegionTest : public ::testing::Test {
33 public:
SetUp()34 void SetUp() override {
35 std::tie(region_, rw_mapping_) =
36 CreateMappedRegion<SharedMemoryRegionType>(kRegionSize);
37 ASSERT_TRUE(region_.IsValid());
38 ASSERT_TRUE(rw_mapping_.IsValid());
39 span<uint8_t> mapped = base::span(rw_mapping_);
40 EXPECT_EQ(mapped.size(), kRegionSize);
41 std::ranges::fill(mapped, uint8_t{'G'});
42 EXPECT_TRUE(IsMemoryFilledWithByte(mapped, 'G'));
43 }
44
45 protected:
46 SharedMemoryRegionType region_;
47 WritableSharedMemoryMapping rw_mapping_;
48 };
49
50 typedef ::testing::Types<WritableSharedMemoryRegion,
51 UnsafeSharedMemoryRegion,
52 ReadOnlySharedMemoryRegion>
53 AllRegionTypes;
54 TYPED_TEST_SUITE(SharedMemoryRegionTest, AllRegionTypes);
55
TYPED_TEST(SharedMemoryRegionTest,NonValidRegion)56 TYPED_TEST(SharedMemoryRegionTest, NonValidRegion) {
57 TypeParam region;
58 EXPECT_FALSE(region.IsValid());
59 // We shouldn't crash on Map but should return an invalid mapping.
60 typename TypeParam::MappingType mapping = region.Map();
61 EXPECT_FALSE(mapping.IsValid());
62 }
63
TYPED_TEST(SharedMemoryRegionTest,MoveRegion)64 TYPED_TEST(SharedMemoryRegionTest, MoveRegion) {
65 TypeParam moved_region = std::move(this->region_);
66 EXPECT_FALSE(this->region_.IsValid());
67 ASSERT_TRUE(moved_region.IsValid());
68
69 // Check that moved region maps correctly.
70 typename TypeParam::MappingType mapping = moved_region.Map();
71 ASSERT_TRUE(mapping.IsValid());
72 EXPECT_NE(this->rw_mapping_.data(), mapping.data());
73 EXPECT_EQ(base::span(this->rw_mapping_), base::span(mapping));
74
75 // Verify that the second mapping reflects changes in the first.
76 std::ranges::fill(base::span(this->rw_mapping_), '#');
77 EXPECT_EQ(base::span(this->rw_mapping_), base::span(mapping));
78 }
79
TYPED_TEST(SharedMemoryRegionTest,MappingValidAfterClose)80 TYPED_TEST(SharedMemoryRegionTest, MappingValidAfterClose) {
81 // Check the mapping is still valid after the region is closed.
82 this->region_ = TypeParam();
83 EXPECT_FALSE(this->region_.IsValid());
84 ASSERT_TRUE(this->rw_mapping_.IsValid());
85 EXPECT_TRUE(IsMemoryFilledWithByte(base::span(this->rw_mapping_), 'G'));
86 }
87
TYPED_TEST(SharedMemoryRegionTest,MapTwice)88 TYPED_TEST(SharedMemoryRegionTest, MapTwice) {
89 // The second mapping is either writable or read-only.
90 typename TypeParam::MappingType mapping = this->region_.Map();
91 ASSERT_TRUE(mapping.IsValid());
92 EXPECT_NE(this->rw_mapping_.data(), mapping.data());
93 EXPECT_EQ(base::span(this->rw_mapping_), base::span(mapping));
94
95 // Verify that the second mapping reflects changes in the first.
96 std::ranges::fill(base::span(this->rw_mapping_), '#');
97 EXPECT_EQ(base::span(this->rw_mapping_), base::span(mapping));
98
99 // Close the region and unmap the first memory segment, verify the second
100 // still has the right data.
101 this->region_ = TypeParam();
102 this->rw_mapping_ = WritableSharedMemoryMapping();
103 EXPECT_TRUE(IsMemoryFilledWithByte(base::span(mapping), '#'));
104 }
105
TYPED_TEST(SharedMemoryRegionTest,MapUnmapMap)106 TYPED_TEST(SharedMemoryRegionTest, MapUnmapMap) {
107 this->rw_mapping_ = WritableSharedMemoryMapping();
108
109 typename TypeParam::MappingType mapping = this->region_.Map();
110 ASSERT_TRUE(mapping.IsValid());
111 EXPECT_TRUE(IsMemoryFilledWithByte(base::span(mapping), 'G'));
112 }
113
TYPED_TEST(SharedMemoryRegionTest,SerializeAndDeserialize)114 TYPED_TEST(SharedMemoryRegionTest, SerializeAndDeserialize) {
115 subtle::PlatformSharedMemoryRegion platform_region =
116 TypeParam::TakeHandleForSerialization(std::move(this->region_));
117 EXPECT_EQ(platform_region.GetGUID(), this->rw_mapping_.guid());
118 TypeParam region = TypeParam::Deserialize(std::move(platform_region));
119 EXPECT_TRUE(region.IsValid());
120 EXPECT_FALSE(this->region_.IsValid());
121 typename TypeParam::MappingType mapping = region.Map();
122 ASSERT_TRUE(mapping.IsValid());
123 EXPECT_TRUE(IsMemoryFilledWithByte(base::span(mapping), 'G'));
124
125 // Verify that the second mapping reflects changes in the first.
126 std::ranges::fill(base::span(this->rw_mapping_), '#');
127 EXPECT_EQ(base::span(this->rw_mapping_), base::span(mapping));
128 }
129
130 // Map() will return addresses which are aligned to the platform page size, this
131 // varies from platform to platform though. Since we'd like to advertise a
132 // minimum alignment that callers can count on, test for it here.
TYPED_TEST(SharedMemoryRegionTest,MapMinimumAlignment)133 TYPED_TEST(SharedMemoryRegionTest, MapMinimumAlignment) {
134 EXPECT_EQ(0U,
135 reinterpret_cast<uintptr_t>(this->rw_mapping_.data()) &
136 (subtle::PlatformSharedMemoryRegion::kMapMinimumAlignment - 1));
137 }
138
TYPED_TEST(SharedMemoryRegionTest,MapSize)139 TYPED_TEST(SharedMemoryRegionTest, MapSize) {
140 EXPECT_EQ(this->rw_mapping_.size(), kRegionSize);
141 EXPECT_GE(this->rw_mapping_.mapped_size(), kRegionSize);
142 }
143
TYPED_TEST(SharedMemoryRegionTest,MapGranularity)144 TYPED_TEST(SharedMemoryRegionTest, MapGranularity) {
145 EXPECT_LT(this->rw_mapping_.mapped_size(),
146 kRegionSize + SysInfo::VMAllocationGranularity());
147 }
148
TYPED_TEST(SharedMemoryRegionTest,MapAt)149 TYPED_TEST(SharedMemoryRegionTest, MapAt) {
150 const size_t kPageSize = SysInfo::VMAllocationGranularity();
151 ASSERT_TRUE(kPageSize >= sizeof(uint32_t));
152 ASSERT_EQ(kPageSize % sizeof(uint32_t), 0U);
153 const size_t kDataSize = kPageSize * 2;
154 const size_t kCount = kDataSize / sizeof(uint32_t);
155
156 auto [region, rw_mapping] = CreateMappedRegion<TypeParam>(kDataSize);
157 ASSERT_TRUE(region.IsValid());
158 ASSERT_TRUE(rw_mapping.IsValid());
159 auto map = rw_mapping.template GetMemoryAsSpan<uint32_t>();
160
161 for (size_t i = 0; i < kCount; ++i)
162 map[i] = i;
163
164 rw_mapping = WritableSharedMemoryMapping();
165
166 for (size_t bytes_offset = sizeof(uint32_t); bytes_offset <= kPageSize;
167 bytes_offset += sizeof(uint32_t)) {
168 typename TypeParam::MappingType mapping =
169 region.MapAt(bytes_offset, kDataSize - bytes_offset);
170 ASSERT_TRUE(mapping.IsValid());
171
172 size_t int_offset = bytes_offset / sizeof(uint32_t);
173 auto map2 = mapping.template GetMemoryAsSpan<uint32_t>();
174 for (size_t i = int_offset; i < kCount; ++i) {
175 EXPECT_EQ(map2[i - int_offset], i);
176 }
177 }
178 }
179
TYPED_TEST(SharedMemoryRegionTest,MapZeroBytesFails)180 TYPED_TEST(SharedMemoryRegionTest, MapZeroBytesFails) {
181 typename TypeParam::MappingType mapping = this->region_.MapAt(0, 0);
182 EXPECT_FALSE(mapping.IsValid());
183 }
184
TYPED_TEST(SharedMemoryRegionTest,MapMoreBytesThanRegionSizeFails)185 TYPED_TEST(SharedMemoryRegionTest, MapMoreBytesThanRegionSizeFails) {
186 size_t region_real_size = this->region_.GetSize();
187 typename TypeParam::MappingType mapping =
188 this->region_.MapAt(0, region_real_size + 1);
189 EXPECT_FALSE(mapping.IsValid());
190 }
191
192 template <typename DuplicatableSharedMemoryRegion>
193 class DuplicatableSharedMemoryRegionTest
194 : public SharedMemoryRegionTest<DuplicatableSharedMemoryRegion> {};
195
196 typedef ::testing::Types<UnsafeSharedMemoryRegion, ReadOnlySharedMemoryRegion>
197 DuplicatableRegionTypes;
198 TYPED_TEST_SUITE(DuplicatableSharedMemoryRegionTest, DuplicatableRegionTypes);
199
TYPED_TEST(DuplicatableSharedMemoryRegionTest,Duplicate)200 TYPED_TEST(DuplicatableSharedMemoryRegionTest, Duplicate) {
201 TypeParam dup_region = this->region_.Duplicate();
202 EXPECT_EQ(this->region_.GetGUID(), dup_region.GetGUID());
203 typename TypeParam::MappingType mapping = dup_region.Map();
204 ASSERT_TRUE(mapping.IsValid());
205 EXPECT_NE(this->rw_mapping_.data(), mapping.data());
206 EXPECT_EQ(this->rw_mapping_.guid(), mapping.guid());
207 EXPECT_TRUE(IsMemoryFilledWithByte(base::span(mapping), 'G'));
208 }
209
210 class ReadOnlySharedMemoryRegionTest : public ::testing::Test {
211 public:
GetInitiallyReadOnlyRegion(size_t size)212 ReadOnlySharedMemoryRegion GetInitiallyReadOnlyRegion(size_t size) {
213 MappedReadOnlyRegion mapped_region =
214 ReadOnlySharedMemoryRegion::Create(size);
215 ReadOnlySharedMemoryRegion region = std::move(mapped_region.region);
216 return region;
217 }
218
GetConvertedToReadOnlyRegion(size_t size)219 ReadOnlySharedMemoryRegion GetConvertedToReadOnlyRegion(size_t size) {
220 WritableSharedMemoryRegion region =
221 WritableSharedMemoryRegion::Create(kRegionSize);
222 ReadOnlySharedMemoryRegion ro_region =
223 WritableSharedMemoryRegion::ConvertToReadOnly(std::move(region));
224 return ro_region;
225 }
226 };
227
TEST_F(ReadOnlySharedMemoryRegionTest,InitiallyReadOnlyRegionCannotBeMappedAsWritable)228 TEST_F(ReadOnlySharedMemoryRegionTest,
229 InitiallyReadOnlyRegionCannotBeMappedAsWritable) {
230 ReadOnlySharedMemoryRegion region = GetInitiallyReadOnlyRegion(kRegionSize);
231 ASSERT_TRUE(region.IsValid());
232
233 EXPECT_TRUE(CheckReadOnlyPlatformSharedMemoryRegionForTesting(
234 ReadOnlySharedMemoryRegion::TakeHandleForSerialization(
235 std::move(region))));
236 }
237
TEST_F(ReadOnlySharedMemoryRegionTest,ConvertedToReadOnlyRegionCannotBeMappedAsWritable)238 TEST_F(ReadOnlySharedMemoryRegionTest,
239 ConvertedToReadOnlyRegionCannotBeMappedAsWritable) {
240 ReadOnlySharedMemoryRegion region = GetConvertedToReadOnlyRegion(kRegionSize);
241 ASSERT_TRUE(region.IsValid());
242
243 EXPECT_TRUE(CheckReadOnlyPlatformSharedMemoryRegionForTesting(
244 ReadOnlySharedMemoryRegion::TakeHandleForSerialization(
245 std::move(region))));
246 }
247
TEST_F(ReadOnlySharedMemoryRegionTest,InitiallyReadOnlyRegionProducedMappingWriteDeathTest)248 TEST_F(ReadOnlySharedMemoryRegionTest,
249 InitiallyReadOnlyRegionProducedMappingWriteDeathTest) {
250 ReadOnlySharedMemoryRegion region = GetInitiallyReadOnlyRegion(kRegionSize);
251 ASSERT_TRUE(region.IsValid());
252 ReadOnlySharedMemoryMapping mapping = region.Map();
253 ASSERT_TRUE(mapping.IsValid());
254 base::span<const uint8_t> mem(mapping);
255 base::span<uint8_t> mut_mem =
256 // SAFETY: The data() and size() from `mem` produce a valid span as they
257 // come from another span of the same type (modulo const). Const-casting
258 // is not Undefined Behaviour here as it's not pointing to a const object.
259 // We're testing that we crash if writing to a ReadOnly shared memory
260 // backing.
261 UNSAFE_BUFFERS(base::span(const_cast<uint8_t*>(mem.data()), mem.size()));
262 EXPECT_DEATH_IF_SUPPORTED(std::ranges::fill(mut_mem, 'G'), "");
263 }
264
TEST_F(ReadOnlySharedMemoryRegionTest,ConvertedToReadOnlyRegionProducedMappingWriteDeathTest)265 TEST_F(ReadOnlySharedMemoryRegionTest,
266 ConvertedToReadOnlyRegionProducedMappingWriteDeathTest) {
267 ReadOnlySharedMemoryRegion region = GetConvertedToReadOnlyRegion(kRegionSize);
268 ASSERT_TRUE(region.IsValid());
269 ReadOnlySharedMemoryMapping mapping = region.Map();
270 ASSERT_TRUE(mapping.IsValid());
271 base::span<const uint8_t> mem(mapping);
272 base::span<uint8_t> mut_mem =
273 // SAFETY: The data() and size() from `mem` produce a valid span as they
274 // come from another span of the same type (modulo const). Const-casting
275 // is not Undefined Behaviour here as it's not pointing to a const object.
276 // We're testing that we crash if writing to a ReadOnly shared memory
277 // backing.
278 UNSAFE_BUFFERS(base::span(const_cast<uint8_t*>(mem.data()), mem.size()));
279 EXPECT_DEATH_IF_SUPPORTED(std::ranges::fill(mut_mem, 'G'), "");
280 }
281
282 } // namespace base
283