Bug 1839315: part 4) Link from `SheetLoadData::mWasAlternate` to spec. r=emilio DONTBUILD
[gecko.git] / ipc / gtest / TestSharedMemory.cpp
blob5d27bd81d0521e273a49411b0a64f1f31aa22100
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "gtest/gtest.h"
9 #include "base/shared_memory.h"
11 #include "mozilla/RefPtr.h"
12 #include "mozilla/ipc/SharedMemory.h"
13 #include "mozilla/ipc/SharedMemoryBasic.h"
15 #ifdef XP_LINUX
16 # include <errno.h>
17 # include <linux/magic.h>
18 # include <stdio.h>
19 # include <string.h>
20 # include <sys/statfs.h>
21 # include <sys/utsname.h>
22 #endif
24 #ifdef XP_WIN
25 # include <windows.h>
26 #endif
28 namespace mozilla {
30 // Try to map a frozen shm for writing. Threat model: the process is
31 // compromised and then receives a frozen handle.
32 TEST(IPCSharedMemory, FreezeAndMapRW)
34 base::SharedMemory shm;
36 // Create and initialize
37 ASSERT_TRUE(shm.CreateFreezeable(1));
38 ASSERT_TRUE(shm.Map(1));
39 auto mem = reinterpret_cast<char*>(shm.memory());
40 ASSERT_TRUE(mem);
41 *mem = 'A';
43 // Freeze
44 ASSERT_TRUE(shm.Freeze());
45 ASSERT_FALSE(shm.memory());
47 // Re-create as writeable
48 auto handle = shm.TakeHandle();
49 ASSERT_TRUE(shm.IsHandleValid(handle));
50 ASSERT_FALSE(shm.IsValid());
51 ASSERT_TRUE(shm.SetHandle(std::move(handle), /* read-only */ false));
52 ASSERT_TRUE(shm.IsValid());
54 // This should fail
55 EXPECT_FALSE(shm.Map(1));
58 // Try to restore write permissions to a frozen mapping. Threat
59 // model: the process has mapped frozen shm normally and then is
60 // compromised, or as for FreezeAndMapRW (see also the
61 // proof-of-concept at https://crbug.com/project-zero/1671 ).
62 TEST(IPCSharedMemory, FreezeAndReprotect)
64 base::SharedMemory shm;
66 // Create and initialize
67 ASSERT_TRUE(shm.CreateFreezeable(1));
68 ASSERT_TRUE(shm.Map(1));
69 auto mem = reinterpret_cast<char*>(shm.memory());
70 ASSERT_TRUE(mem);
71 *mem = 'A';
73 // Freeze
74 ASSERT_TRUE(shm.Freeze());
75 ASSERT_FALSE(shm.memory());
77 // Re-map
78 ASSERT_TRUE(shm.Map(1));
79 mem = reinterpret_cast<char*>(shm.memory());
80 ASSERT_EQ(*mem, 'A');
82 // Try to alter protection; should fail
83 EXPECT_FALSE(ipc::SharedMemory::SystemProtectFallible(
84 mem, 1, ipc::SharedMemory::RightsReadWrite));
87 #ifndef XP_WIN
88 // This essentially tests whether FreezeAndReprotect would have failed
89 // without the freeze. It doesn't work on Windows: VirtualProtect
90 // can't exceed the permissions set in MapViewOfFile regardless of the
91 // security status of the original handle.
92 TEST(IPCSharedMemory, Reprotect)
94 base::SharedMemory shm;
96 // Create and initialize
97 ASSERT_TRUE(shm.CreateFreezeable(1));
98 ASSERT_TRUE(shm.Map(1));
99 auto mem = reinterpret_cast<char*>(shm.memory());
100 ASSERT_TRUE(mem);
101 *mem = 'A';
103 // Re-create as read-only
104 auto handle = shm.TakeHandle();
105 ASSERT_TRUE(shm.IsHandleValid(handle));
106 ASSERT_FALSE(shm.IsValid());
107 ASSERT_TRUE(shm.SetHandle(std::move(handle), /* read-only */ true));
108 ASSERT_TRUE(shm.IsValid());
110 // Re-map
111 ASSERT_TRUE(shm.Map(1));
112 mem = reinterpret_cast<char*>(shm.memory());
113 ASSERT_EQ(*mem, 'A');
115 // Try to alter protection; should succeed, because not frozen
116 EXPECT_TRUE(ipc::SharedMemory::SystemProtectFallible(
117 mem, 1, ipc::SharedMemory::RightsReadWrite));
119 #endif
121 #ifdef XP_WIN
122 // Try to regain write permissions on a read-only handle using
123 // DuplicateHandle; this will succeed if the object has no DACL.
124 // See also https://crbug.com/338538
125 TEST(IPCSharedMemory, WinUnfreeze)
127 base::SharedMemory shm;
129 // Create and initialize
130 ASSERT_TRUE(shm.CreateFreezeable(1));
131 ASSERT_TRUE(shm.Map(1));
132 auto mem = reinterpret_cast<char*>(shm.memory());
133 ASSERT_TRUE(mem);
134 *mem = 'A';
136 // Freeze
137 ASSERT_TRUE(shm.Freeze());
138 ASSERT_FALSE(shm.memory());
140 // Extract handle.
141 auto handle = shm.TakeHandle();
142 ASSERT_TRUE(shm.IsHandleValid(handle));
143 ASSERT_FALSE(shm.IsValid());
145 // Unfreeze.
146 HANDLE newHandle = INVALID_HANDLE_VALUE;
147 bool unfroze = ::DuplicateHandle(
148 GetCurrentProcess(), handle.release(), GetCurrentProcess(), &newHandle,
149 FILE_MAP_ALL_ACCESS, false, DUPLICATE_CLOSE_SOURCE);
150 ASSERT_FALSE(unfroze);
152 #endif
154 // Test that a read-only copy sees changes made to the writeable
155 // mapping in the case that the page wasn't accessed before the copy.
156 TEST(IPCSharedMemory, ROCopyAndWrite)
158 base::SharedMemory shmRW, shmRO;
160 // Create and initialize
161 ASSERT_TRUE(shmRW.CreateFreezeable(1));
162 ASSERT_TRUE(shmRW.Map(1));
163 auto memRW = reinterpret_cast<char*>(shmRW.memory());
164 ASSERT_TRUE(memRW);
166 // Create read-only copy
167 ASSERT_TRUE(shmRW.ReadOnlyCopy(&shmRO));
168 EXPECT_FALSE(shmRW.IsValid());
169 ASSERT_EQ(shmRW.memory(), memRW);
170 ASSERT_EQ(shmRO.max_size(), size_t(1));
172 // Map read-only
173 ASSERT_TRUE(shmRO.IsValid());
174 ASSERT_TRUE(shmRO.Map(1));
175 auto memRO = reinterpret_cast<const char*>(shmRO.memory());
176 ASSERT_TRUE(memRO);
177 ASSERT_NE(memRW, memRO);
179 // Check
180 *memRW = 'A';
181 EXPECT_EQ(*memRO, 'A');
184 // Test that a read-only copy sees changes made to the writeable
185 // mapping in the case that the page was accessed before the copy
186 // (and, before that, sees the state as of when the copy was made).
187 TEST(IPCSharedMemory, ROCopyAndRewrite)
189 base::SharedMemory shmRW, shmRO;
191 // Create and initialize
192 ASSERT_TRUE(shmRW.CreateFreezeable(1));
193 ASSERT_TRUE(shmRW.Map(1));
194 auto memRW = reinterpret_cast<char*>(shmRW.memory());
195 ASSERT_TRUE(memRW);
196 *memRW = 'A';
198 // Create read-only copy
199 ASSERT_TRUE(shmRW.ReadOnlyCopy(&shmRO));
200 EXPECT_FALSE(shmRW.IsValid());
201 ASSERT_EQ(shmRW.memory(), memRW);
202 ASSERT_EQ(shmRO.max_size(), size_t(1));
204 // Map read-only
205 ASSERT_TRUE(shmRO.IsValid());
206 ASSERT_TRUE(shmRO.Map(1));
207 auto memRO = reinterpret_cast<const char*>(shmRO.memory());
208 ASSERT_TRUE(memRO);
209 ASSERT_NE(memRW, memRO);
211 // Check
212 ASSERT_EQ(*memRW, 'A');
213 EXPECT_EQ(*memRO, 'A');
214 *memRW = 'X';
215 EXPECT_EQ(*memRO, 'X');
218 // See FreezeAndMapRW.
219 TEST(IPCSharedMemory, ROCopyAndMapRW)
221 base::SharedMemory shmRW, shmRO;
223 // Create and initialize
224 ASSERT_TRUE(shmRW.CreateFreezeable(1));
225 ASSERT_TRUE(shmRW.Map(1));
226 auto memRW = reinterpret_cast<char*>(shmRW.memory());
227 ASSERT_TRUE(memRW);
228 *memRW = 'A';
230 // Create read-only copy
231 ASSERT_TRUE(shmRW.ReadOnlyCopy(&shmRO));
232 ASSERT_TRUE(shmRO.IsValid());
234 // Re-create as writeable
235 auto handle = shmRO.TakeHandle();
236 ASSERT_TRUE(shmRO.IsHandleValid(handle));
237 ASSERT_FALSE(shmRO.IsValid());
238 ASSERT_TRUE(shmRO.SetHandle(std::move(handle), /* read-only */ false));
239 ASSERT_TRUE(shmRO.IsValid());
241 // This should fail
242 EXPECT_FALSE(shmRO.Map(1));
245 // See FreezeAndReprotect
246 TEST(IPCSharedMemory, ROCopyAndReprotect)
248 base::SharedMemory shmRW, shmRO;
250 // Create and initialize
251 ASSERT_TRUE(shmRW.CreateFreezeable(1));
252 ASSERT_TRUE(shmRW.Map(1));
253 auto memRW = reinterpret_cast<char*>(shmRW.memory());
254 ASSERT_TRUE(memRW);
255 *memRW = 'A';
257 // Create read-only copy
258 ASSERT_TRUE(shmRW.ReadOnlyCopy(&shmRO));
259 ASSERT_TRUE(shmRO.IsValid());
261 // Re-map
262 ASSERT_TRUE(shmRO.Map(1));
263 auto memRO = reinterpret_cast<char*>(shmRO.memory());
264 ASSERT_EQ(*memRO, 'A');
266 // Try to alter protection; should fail
267 EXPECT_FALSE(ipc::SharedMemory::SystemProtectFallible(
268 memRO, 1, ipc::SharedMemory::RightsReadWrite));
271 TEST(IPCSharedMemory, IsZero)
273 base::SharedMemory shm;
275 static constexpr size_t kSize = 65536;
276 ASSERT_TRUE(shm.Create(kSize));
277 ASSERT_TRUE(shm.Map(kSize));
279 auto* mem = reinterpret_cast<char*>(shm.memory());
280 for (size_t i = 0; i < kSize; ++i) {
281 ASSERT_EQ(mem[i], 0) << "offset " << i;
285 #ifndef FUZZING
286 TEST(IPCSharedMemory, BasicIsZero)
288 auto shm = MakeRefPtr<ipc::SharedMemoryBasic>();
290 static constexpr size_t kSize = 65536;
291 ASSERT_TRUE(shm->Create(kSize));
292 ASSERT_TRUE(shm->Map(kSize));
294 auto* mem = reinterpret_cast<char*>(shm->memory());
295 for (size_t i = 0; i < kSize; ++i) {
296 ASSERT_EQ(mem[i], 0) << "offset " << i;
299 #endif
301 #if defined(XP_LINUX) && !defined(ANDROID)
302 // Test that memfd_create is used where expected.
304 // More precisely: if memfd_create support is expected, verify that
305 // shared memory isn't subject to a filesystem size limit.
306 TEST(IPCSharedMemory, IsMemfd)
308 static constexpr int kMajor = 3;
309 static constexpr int kMinor = 17;
311 struct utsname uts;
312 ASSERT_EQ(uname(&uts), 0) << strerror(errno);
313 ASSERT_STREQ(uts.sysname, "Linux");
314 int major, minor;
315 ASSERT_EQ(sscanf(uts.release, "%d.%d", &major, &minor), 2);
316 bool expectMemfd = major > kMajor || (major == kMajor && minor >= kMinor);
318 base::SharedMemory shm;
319 ASSERT_TRUE(shm.Create(1));
320 UniqueFileHandle fd = shm.TakeHandle();
321 ASSERT_TRUE(fd);
323 struct statfs fs;
324 ASSERT_EQ(fstatfs(fd.get(), &fs), 0) << strerror(errno);
325 EXPECT_EQ(fs.f_type, TMPFS_MAGIC);
326 static constexpr decltype(fs.f_blocks) kNoLimit = 0;
327 if (expectMemfd) {
328 EXPECT_EQ(fs.f_blocks, kNoLimit);
329 } else {
330 // On older kernels, we expect the memfd / no-limit test to fail.
331 // (In theory it could succeed if backported memfd support exists;
332 // if that ever happens, this check can be removed.)
333 EXPECT_NE(fs.f_blocks, kNoLimit);
336 #endif
338 } // namespace mozilla