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 "base/memory/shared_memory_mapping.h"
6
7 #include <stdint.h>
8
9 #include <atomic>
10 #include <limits>
11
12 #include "base/containers/span.h"
13 #include "base/memory/read_only_shared_memory_region.h"
14 #include "base/memory/writable_shared_memory_region.h"
15 #include "base/ranges/algorithm.h"
16 #include "build/build_config.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 namespace base {
21
22 class SharedMemoryMappingTest : public ::testing::Test {
23 protected:
CreateMapping(size_t size)24 void CreateMapping(size_t size) {
25 auto result = ReadOnlySharedMemoryRegion::Create(size);
26 ASSERT_TRUE(result.IsValid());
27 write_mapping_ = std::move(result.mapping);
28 read_mapping_ = result.region.Map();
29 ASSERT_TRUE(read_mapping_.IsValid());
30 }
31
32 WritableSharedMemoryMapping write_mapping_;
33 ReadOnlySharedMemoryMapping read_mapping_;
34 };
35
TEST_F(SharedMemoryMappingTest,Invalid)36 TEST_F(SharedMemoryMappingTest, Invalid) {
37 EXPECT_EQ(nullptr, write_mapping_.GetMemoryAs<uint8_t>());
38 EXPECT_EQ(nullptr, read_mapping_.GetMemoryAs<uint8_t>());
39 EXPECT_TRUE(write_mapping_.GetMemoryAsSpan<uint8_t>().empty());
40 EXPECT_TRUE(read_mapping_.GetMemoryAsSpan<uint8_t>().empty());
41 EXPECT_TRUE(write_mapping_.GetMemoryAsSpan<uint8_t>(1).empty());
42 EXPECT_TRUE(read_mapping_.GetMemoryAsSpan<uint8_t>(1).empty());
43 }
44
TEST_F(SharedMemoryMappingTest,Scalar)45 TEST_F(SharedMemoryMappingTest, Scalar) {
46 CreateMapping(sizeof(uint32_t));
47
48 uint32_t* write_ptr = write_mapping_.GetMemoryAs<uint32_t>();
49 ASSERT_NE(nullptr, write_ptr);
50
51 const uint32_t* read_ptr = read_mapping_.GetMemoryAs<uint32_t>();
52 ASSERT_NE(nullptr, read_ptr);
53
54 *write_ptr = 0u;
55 EXPECT_EQ(0u, *read_ptr);
56
57 *write_ptr = 0x12345678u;
58 EXPECT_EQ(0x12345678u, *read_ptr);
59 }
60
TEST_F(SharedMemoryMappingTest,SpanWithAutoDeducedElementCount)61 TEST_F(SharedMemoryMappingTest, SpanWithAutoDeducedElementCount) {
62 CreateMapping(sizeof(uint8_t) * 8);
63
64 span<uint8_t> write_span = write_mapping_.GetMemoryAsSpan<uint8_t>();
65 ASSERT_EQ(8u, write_span.size());
66
67 span<const uint32_t> read_span = read_mapping_.GetMemoryAsSpan<uint32_t>();
68 ASSERT_EQ(2u, read_span.size());
69
70 ranges::fill(write_span, 0);
71 EXPECT_EQ(0u, read_span[0]);
72 EXPECT_EQ(0u, read_span[1]);
73
74 for (size_t i = 0; i < write_span.size(); ++i)
75 write_span[i] = i + 1;
76 EXPECT_EQ(0x04030201u, read_span[0]);
77 EXPECT_EQ(0x08070605u, read_span[1]);
78 }
79
TEST_F(SharedMemoryMappingTest,SpanWithExplicitElementCount)80 TEST_F(SharedMemoryMappingTest, SpanWithExplicitElementCount) {
81 CreateMapping(sizeof(uint8_t) * 8);
82
83 span<uint8_t> write_span = write_mapping_.GetMemoryAsSpan<uint8_t>(8);
84 ASSERT_EQ(8u, write_span.size());
85
86 span<uint8_t> write_span_2 = write_mapping_.GetMemoryAsSpan<uint8_t>(4);
87 ASSERT_EQ(4u, write_span_2.size());
88
89 span<const uint32_t> read_span = read_mapping_.GetMemoryAsSpan<uint32_t>(2);
90 ASSERT_EQ(2u, read_span.size());
91
92 span<const uint32_t> read_span_2 = read_mapping_.GetMemoryAsSpan<uint32_t>(1);
93 ASSERT_EQ(1u, read_span_2.size());
94
95 ranges::fill(write_span, 0);
96 EXPECT_EQ(0u, read_span[0]);
97 EXPECT_EQ(0u, read_span[1]);
98 EXPECT_EQ(0u, read_span_2[0]);
99
100 for (size_t i = 0; i < write_span.size(); ++i)
101 write_span[i] = i + 1;
102 EXPECT_EQ(0x04030201u, read_span[0]);
103 EXPECT_EQ(0x08070605u, read_span[1]);
104 EXPECT_EQ(0x04030201u, read_span_2[0]);
105
106 ranges::fill(write_span_2, 0);
107 EXPECT_EQ(0u, read_span[0]);
108 EXPECT_EQ(0x08070605u, read_span[1]);
109 EXPECT_EQ(0u, read_span_2[0]);
110 }
111
TEST_F(SharedMemoryMappingTest,SpanWithZeroElementCount)112 TEST_F(SharedMemoryMappingTest, SpanWithZeroElementCount) {
113 CreateMapping(sizeof(uint8_t) * 8);
114
115 EXPECT_TRUE(write_mapping_.GetMemoryAsSpan<uint8_t>(0).empty());
116
117 EXPECT_TRUE(read_mapping_.GetMemoryAsSpan<uint8_t>(0).empty());
118 }
119
TEST_F(SharedMemoryMappingTest,TooBigScalar)120 TEST_F(SharedMemoryMappingTest, TooBigScalar) {
121 CreateMapping(sizeof(uint8_t));
122
123 EXPECT_EQ(nullptr, write_mapping_.GetMemoryAs<uint32_t>());
124
125 EXPECT_EQ(nullptr, read_mapping_.GetMemoryAs<uint32_t>());
126 }
127
TEST_F(SharedMemoryMappingTest,TooBigSpanWithAutoDeducedElementCount)128 TEST_F(SharedMemoryMappingTest, TooBigSpanWithAutoDeducedElementCount) {
129 CreateMapping(sizeof(uint8_t));
130
131 EXPECT_TRUE(write_mapping_.GetMemoryAsSpan<uint32_t>().empty());
132
133 EXPECT_TRUE(read_mapping_.GetMemoryAsSpan<uint32_t>().empty());
134 }
135
TEST_F(SharedMemoryMappingTest,TooBigSpanWithExplicitElementCount)136 TEST_F(SharedMemoryMappingTest, TooBigSpanWithExplicitElementCount) {
137 CreateMapping(sizeof(uint8_t));
138
139 // Deliberately pick element counts such that a naive bounds calculation would
140 // overflow.
141 EXPECT_TRUE(write_mapping_
142 .GetMemoryAsSpan<uint32_t>(std::numeric_limits<size_t>::max())
143 .empty());
144
145 EXPECT_TRUE(read_mapping_
146 .GetMemoryAsSpan<uint32_t>(std::numeric_limits<size_t>::max())
147 .empty());
148 }
149
TEST_F(SharedMemoryMappingTest,Atomic)150 TEST_F(SharedMemoryMappingTest, Atomic) {
151 CreateMapping(sizeof(std::atomic<uint32_t>));
152
153 auto* write_ptr = write_mapping_.GetMemoryAs<std::atomic<uint32_t>>();
154 ASSERT_NE(nullptr, write_ptr);
155
156 // Placement new to initialize the std::atomic in place.
157 new (write_ptr) std::atomic<uint32_t>;
158
159 const auto* read_ptr = read_mapping_.GetMemoryAs<std::atomic<uint32_t>>();
160 ASSERT_NE(nullptr, read_ptr);
161
162 write_ptr->store(0u, std::memory_order_relaxed);
163 EXPECT_EQ(0u, read_ptr->load(std::memory_order_relaxed));
164
165 write_ptr->store(0x12345678u, std::memory_order_relaxed);
166 EXPECT_EQ(0x12345678u, read_ptr->load(std::memory_order_relaxed));
167 }
168
TEST_F(SharedMemoryMappingTest,TooBigAtomic)169 TEST_F(SharedMemoryMappingTest, TooBigAtomic) {
170 CreateMapping(sizeof(std::atomic<uint8_t>));
171
172 EXPECT_EQ(nullptr, write_mapping_.GetMemoryAs<std::atomic<uint32_t>>());
173
174 EXPECT_EQ(nullptr, read_mapping_.GetMemoryAs<std::atomic<uint32_t>>());
175 }
176
177 // TODO(dcheng): This test is temporarily disabled on iOS. iOS devices allow
178 // the creation of a 1GB shared memory region, but don't allow the region to be
179 // mapped.
180 #if !BUILDFLAG(IS_IOS)
181 // TODO(crbug.com/40846204) Fix flakiness and re-enable on Linux and ChromeOS.
182 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
183 #define MAYBE_TotalMappedSizeLimit DISABLED_TotalMappedSizeLimit
184 #else
185 #define MAYBE_TotalMappedSizeLimit TotalMappedSizeLimit
186 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
TEST_F(SharedMemoryMappingTest,MAYBE_TotalMappedSizeLimit)187 TEST_F(SharedMemoryMappingTest, MAYBE_TotalMappedSizeLimit) {
188 // Nothing interesting to test if the address space isn't 64 bits, since
189 // there's no real limit enforced on 32 bits other than complete address
190 // space exhaustion.
191 // Also exclude NaCl since pointers are 32 bits on all architectures:
192 // https://bugs.chromium.org/p/nativeclient/issues/detail?id=1162
193 #if defined(ARCH_CPU_64_BITS) && !BUILDFLAG(IS_NACL)
194 auto region = WritableSharedMemoryRegion::Create(1024 * 1024 * 1024);
195 ASSERT_TRUE(region.IsValid());
196 // The limit is 32GB of mappings on 64-bit platforms, so the final mapping
197 // should fail.
198 std::vector<WritableSharedMemoryMapping> mappings(32);
199 for (size_t i = 0; i < mappings.size(); ++i) {
200 SCOPED_TRACE(i);
201 auto& mapping = mappings[i];
202 mapping = region.Map();
203 EXPECT_EQ(&mapping != &mappings.back(), mapping.IsValid());
204 }
205 #endif // defined(ARCH_CPU_64_BITS)
206 }
207 #endif // !BUILDFLAG(IS_IOS)
208
209 } // namespace base
210