• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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