1 // Copyright 2017 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/test/test_shared_memory_util.h"
6
7 #include <gtest/gtest.h>
8
9 #include <stddef.h>
10 #include <stdint.h>
11
12 #include "base/logging.h"
13 #include "build/build_config.h"
14
15 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_NACL)
16 #include <errno.h>
17 #include <string.h>
18 #include <sys/mman.h>
19 #include <unistd.h>
20 #endif
21
22 #if BUILDFLAG(IS_FUCHSIA)
23 #include <lib/zx/vmar.h>
24 #include <zircon/rights.h>
25 #endif
26
27 #if BUILDFLAG(IS_APPLE)
28 #include <mach/vm_map.h>
29 #endif
30
31 #if BUILDFLAG(IS_WIN)
32 #include <aclapi.h>
33 #endif
34
35 namespace base {
36
37 #if !BUILDFLAG(IS_NACL)
38
39 static const size_t kDataSize = 1024;
40
41 // Common routine used with Posix file descriptors. Check that shared memory
42 // file descriptor |fd| does not allow writable mappings. Return true on
43 // success, false otherwise.
44 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
CheckReadOnlySharedMemoryFdPosix(int fd)45 static bool CheckReadOnlySharedMemoryFdPosix(int fd) {
46 // Note that the error on Android is EPERM, unlike other platforms where
47 // it will be EACCES.
48 #if BUILDFLAG(IS_ANDROID)
49 const int kExpectedErrno = EPERM;
50 #else
51 const int kExpectedErrno = EACCES;
52 #endif
53 errno = 0;
54 void* address =
55 mmap(nullptr, kDataSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
56 const bool success = (address != nullptr) && (address != MAP_FAILED);
57 if (success) {
58 LOG(ERROR) << "mmap() should have failed!";
59 munmap(address, kDataSize); // Cleanup.
60 return false;
61 }
62 if (errno != kExpectedErrno) {
63 LOG(ERROR) << "Expected mmap() to return " << kExpectedErrno
64 << " but returned " << errno << ": " << strerror(errno) << "\n";
65 return false;
66 }
67 return true;
68 }
69 #endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_APPLE)
70
71 #if BUILDFLAG(IS_FUCHSIA)
72 // Fuchsia specific implementation.
CheckReadOnlySharedMemoryFuchsiaHandle(zx::unowned_vmo handle)73 bool CheckReadOnlySharedMemoryFuchsiaHandle(zx::unowned_vmo handle) {
74 const uint32_t flags = ZX_VM_PERM_READ | ZX_VM_PERM_WRITE;
75 uintptr_t addr;
76 const zx_status_t status =
77 zx::vmar::root_self()->map(flags, 0, *handle, 0U, kDataSize, &addr);
78 if (status == ZX_OK) {
79 LOG(ERROR) << "zx_vmar_map() should have failed!";
80 zx::vmar::root_self()->unmap(addr, kDataSize);
81 return false;
82 }
83 if (status != ZX_ERR_ACCESS_DENIED) {
84 LOG(ERROR) << "Expected zx_vmar_map() to return " << ZX_ERR_ACCESS_DENIED
85 << " (ZX_ERR_ACCESS_DENIED) but returned " << status << "\n";
86 return false;
87 }
88 return true;
89 }
90
91 #elif BUILDFLAG(IS_APPLE)
CheckReadOnlySharedMemoryMachPort(mach_port_t memory_object)92 bool CheckReadOnlySharedMemoryMachPort(mach_port_t memory_object) {
93 vm_address_t memory;
94 const kern_return_t kr =
95 vm_map(mach_task_self(), &memory, kDataSize, 0, VM_FLAGS_ANYWHERE,
96 memory_object, 0, FALSE, VM_PROT_READ | VM_PROT_WRITE,
97 VM_PROT_READ | VM_PROT_WRITE | VM_PROT_IS_MASK, VM_INHERIT_NONE);
98 if (kr == KERN_SUCCESS) {
99 LOG(ERROR) << "vm_map() should have failed!";
100 vm_deallocate(mach_task_self(), memory, kDataSize); // Cleanup.
101 return false;
102 }
103 return true;
104 }
105
106 #elif BUILDFLAG(IS_WIN)
CheckReadOnlySharedMemoryWindowsHandle(HANDLE handle)107 bool CheckReadOnlySharedMemoryWindowsHandle(HANDLE handle) {
108 void* memory =
109 MapViewOfFile(handle, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, kDataSize);
110 if (memory != nullptr) {
111 LOG(ERROR) << "MapViewOfFile() should have failed!";
112 UnmapViewOfFile(memory);
113 return false;
114 }
115 return true;
116 }
117 #endif
118
CheckReadOnlyPlatformSharedMemoryRegionForTesting(subtle::PlatformSharedMemoryRegion region)119 bool CheckReadOnlyPlatformSharedMemoryRegionForTesting(
120 subtle::PlatformSharedMemoryRegion region) {
121 if (region.GetMode() != subtle::PlatformSharedMemoryRegion::Mode::kReadOnly) {
122 LOG(ERROR) << "Expected region mode is "
123 << static_cast<int>(
124 subtle::PlatformSharedMemoryRegion::Mode::kReadOnly)
125 << " but actual is " << static_cast<int>(region.GetMode());
126 return false;
127 }
128
129 #if BUILDFLAG(IS_APPLE)
130 return CheckReadOnlySharedMemoryMachPort(region.GetPlatformHandle());
131 #elif BUILDFLAG(IS_FUCHSIA)
132 return CheckReadOnlySharedMemoryFuchsiaHandle(region.GetPlatformHandle());
133 #elif BUILDFLAG(IS_WIN)
134 return CheckReadOnlySharedMemoryWindowsHandle(region.GetPlatformHandle());
135 #elif BUILDFLAG(IS_ANDROID)
136 return CheckReadOnlySharedMemoryFdPosix(region.GetPlatformHandle());
137 #else
138 return CheckReadOnlySharedMemoryFdPosix(region.GetPlatformHandle().fd);
139 #endif
140 }
141
142 #endif // !BUILDFLAG(IS_NACL)
143
MapForTesting(subtle::PlatformSharedMemoryRegion * region)144 WritableSharedMemoryMapping MapForTesting(
145 subtle::PlatformSharedMemoryRegion* region) {
146 return MapAtForTesting(region, 0, region->GetSize());
147 }
148
MapAtForTesting(subtle::PlatformSharedMemoryRegion * region,uint64_t offset,size_t size)149 WritableSharedMemoryMapping MapAtForTesting(
150 subtle::PlatformSharedMemoryRegion* region,
151 uint64_t offset,
152 size_t size) {
153 SharedMemoryMapper* mapper = SharedMemoryMapper::GetDefaultInstance();
154 auto result = region->MapAt(offset, size, mapper);
155 if (!result.has_value())
156 return {};
157
158 return WritableSharedMemoryMapping(result.value(), size, region->GetGUID(),
159 mapper);
160 }
161
162 template <>
163 std::pair<ReadOnlySharedMemoryRegion, WritableSharedMemoryMapping>
CreateMappedRegion(size_t size)164 CreateMappedRegion(size_t size) {
165 MappedReadOnlyRegion mapped_region = ReadOnlySharedMemoryRegion::Create(size);
166 return {std::move(mapped_region.region), std::move(mapped_region.mapping)};
167 }
168
169 } // namespace base
170