1 // Copyright 2018 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_ 6 #define BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_ 7 8 #include <utility> 9 10 #include "base/compiler_specific.h" 11 #include "base/gtest_prod_util.h" 12 #include "base/macros.h" 13 #include "base/unguessable_token.h" 14 #include "build/build_config.h" 15 16 #if defined(OS_MACOSX) && !defined(OS_IOS) 17 #include <mach/mach.h> 18 #include "base/mac/scoped_mach_port.h" 19 #elif defined(OS_FUCHSIA) 20 #include <lib/zx/vmo.h> 21 #elif defined(OS_WIN) 22 #include "base/win/scoped_handle.h" 23 #include "base/win/windows_types.h" 24 #elif defined(OS_POSIX) 25 #include <sys/types.h> 26 #include "base/file_descriptor_posix.h" 27 #include "base/files/scoped_file.h" 28 #endif 29 30 namespace base { 31 namespace subtle { 32 33 #if defined(OS_POSIX) && (!defined(OS_MACOSX) || defined(OS_IOS)) && \ 34 !defined(OS_ANDROID) 35 // Helper structs to keep two descriptors on POSIX. It's needed to support 36 // ConvertToReadOnly(). 37 struct BASE_EXPORT FDPair { 38 int fd; 39 int readonly_fd; 40 }; 41 42 struct BASE_EXPORT ScopedFDPair { 43 ScopedFDPair(); 44 ScopedFDPair(ScopedFD in_fd, ScopedFD in_readonly_fd); 45 ScopedFDPair(ScopedFDPair&&); 46 ScopedFDPair& operator=(ScopedFDPair&&); 47 ~ScopedFDPair(); 48 49 FDPair get() const; 50 51 ScopedFD fd; 52 ScopedFD readonly_fd; 53 }; 54 #endif 55 56 // Implementation class for shared memory regions. 57 // 58 // This class does the following: 59 // 60 // - Wraps and owns a shared memory region platform handle. 61 // - Provides a way to allocate a new region of platform shared memory of given 62 // size. 63 // - Provides a way to create mapping of the region in the current process' 64 // address space, under special access-control constraints (see Mode). 65 // - Provides methods to help transferring the handle across process boundaries. 66 // - Holds a 128-bit unique identifier used to uniquely identify the same 67 // kernel region resource across processes (used for memory tracking). 68 // - Has a method to retrieve the region's size in bytes. 69 // 70 // IMPORTANT NOTE: Users should never use this directly, but 71 // ReadOnlySharedMemoryRegion, WritableSharedMemoryRegion or 72 // UnsafeSharedMemoryRegion since this is an implementation class. 73 class BASE_EXPORT PlatformSharedMemoryRegion { 74 public: 75 // Permission mode of the platform handle. Each mode corresponds to one of the 76 // typed shared memory classes: 77 // 78 // * ReadOnlySharedMemoryRegion: A region that can only create read-only 79 // mappings. 80 // 81 // * WritableSharedMemoryRegion: A region that can only create writable 82 // mappings. The region can be demoted to ReadOnlySharedMemoryRegion without 83 // the possibility of promoting back to writable. 84 // 85 // * UnsafeSharedMemoryRegion: A region that can only create writable 86 // mappings. The region cannot be demoted to ReadOnlySharedMemoryRegion. 87 enum class Mode { 88 kReadOnly, // ReadOnlySharedMemoryRegion 89 kWritable, // WritableSharedMemoryRegion 90 kUnsafe, // UnsafeSharedMemoryRegion 91 kMaxValue = kUnsafe 92 }; 93 94 // Platform-specific shared memory type used by this class. 95 #if defined(OS_MACOSX) && !defined(OS_IOS) 96 using PlatformHandle = mach_port_t; 97 using ScopedPlatformHandle = mac::ScopedMachSendRight; 98 #elif defined(OS_FUCHSIA) 99 using PlatformHandle = zx_handle_t; 100 using ScopedPlatformHandle = zx::vmo; 101 #elif defined(OS_WIN) 102 using PlatformHandle = HANDLE; 103 using ScopedPlatformHandle = win::ScopedHandle; 104 #elif defined(OS_ANDROID) 105 using PlatformHandle = int; 106 using ScopedPlatformHandle = ScopedFD; 107 #else 108 using PlatformHandle = FDPair; 109 using ScopedPlatformHandle = ScopedFDPair; 110 #endif 111 112 // The minimum alignment in bytes that any mapped address produced by Map() 113 // and MapAt() is guaranteed to have. 114 enum { kMapMinimumAlignment = 32 }; 115 116 // Creates a new PlatformSharedMemoryRegion with corresponding mode and size. 117 // Creating in kReadOnly mode isn't supported because then there will be no 118 // way to modify memory content. 119 static PlatformSharedMemoryRegion CreateWritable(size_t size); 120 static PlatformSharedMemoryRegion CreateUnsafe(size_t size); 121 122 // Returns a new PlatformSharedMemoryRegion that takes ownership of the 123 // |handle|. All parameters must be taken from another valid 124 // PlatformSharedMemoryRegion instance, e.g. |size| must be equal to the 125 // actual region size as allocated by the kernel. 126 // Closes the |handle| and returns an invalid instance if passed parameters 127 // are invalid. 128 static PlatformSharedMemoryRegion Take(ScopedPlatformHandle handle, 129 Mode mode, 130 size_t size, 131 const UnguessableToken& guid); 132 133 // Default constructor initializes an invalid instance, i.e. an instance that 134 // doesn't wrap any valid platform handle. 135 PlatformSharedMemoryRegion(); 136 137 // Move operations are allowed. 138 PlatformSharedMemoryRegion(PlatformSharedMemoryRegion&&); 139 PlatformSharedMemoryRegion& operator=(PlatformSharedMemoryRegion&&); 140 141 // Destructor closes the platform handle. Does nothing if the handle is 142 // invalid. 143 ~PlatformSharedMemoryRegion(); 144 145 // Passes ownership of the platform handle to the caller. The current instance 146 // becomes invalid. It's the responsibility of the caller to close the handle. 147 ScopedPlatformHandle PassPlatformHandle() WARN_UNUSED_RESULT; 148 149 // Returns the platform handle. The current instance keeps ownership of this 150 // handle. 151 PlatformHandle GetPlatformHandle() const; 152 153 // Whether the platform handle is valid. 154 bool IsValid() const; 155 156 // Duplicates the platform handle and creates a new PlatformSharedMemoryRegion 157 // with the same |mode_|, |size_| and |guid_| that owns this handle. Returns 158 // invalid region on failure, the current instance remains valid. 159 // Can be called only in kReadOnly and kUnsafe modes, CHECK-fails if is 160 // called in kWritable mode. 161 PlatformSharedMemoryRegion Duplicate() const; 162 163 // Converts the region to read-only. Returns whether the operation succeeded. 164 // Makes the current instance invalid on failure. Can be called only in 165 // kWritable mode, all other modes will CHECK-fail. The object will have 166 // kReadOnly mode after this call on success. 167 bool ConvertToReadOnly(); 168 #if defined(OS_MACOSX) && !defined(OS_IOS) 169 // Same as above, but |mapped_addr| is used as a hint to avoid additional 170 // mapping of the memory object. 171 // |mapped_addr| must be mapped location of |memory_object_|. If the location 172 // is unknown, |mapped_addr| should be |nullptr|. 173 bool ConvertToReadOnly(void* mapped_addr); 174 #endif // defined(OS_MACOSX) && !defined(OS_IOS) 175 176 // Converts the region to unsafe. Returns whether the operation succeeded. 177 // Makes the current instance invalid on failure. Can be called only in 178 // kWritable mode, all other modes will CHECK-fail. The object will have 179 // kUnsafe mode after this call on success. 180 bool ConvertToUnsafe(); 181 182 // Maps |size| bytes of the shared memory region starting with the given 183 // |offset| into the caller's address space. |offset| must be aligned to value 184 // of |SysInfo::VMAllocationGranularity()|. Fails if requested bytes are out 185 // of the region limits. 186 // Returns true and sets |memory| and |mapped_size| on success, returns false 187 // and leaves output parameters in unspecified state otherwise. The mapped 188 // address is guaranteed to have an alignment of at least 189 // |kMapMinimumAlignment|. 190 bool MapAt(off_t offset, 191 size_t size, 192 void** memory, 193 size_t* mapped_size) const; 194 GetGUID()195 const UnguessableToken& GetGUID() const { return guid_; } 196 GetSize()197 size_t GetSize() const { return size_; } 198 GetMode()199 Mode GetMode() const { return mode_; } 200 201 private: 202 FRIEND_TEST_ALL_PREFIXES(PlatformSharedMemoryRegionTest, 203 CreateReadOnlyRegionDeathTest); 204 FRIEND_TEST_ALL_PREFIXES(PlatformSharedMemoryRegionTest, 205 CheckPlatformHandlePermissionsCorrespondToMode); 206 static PlatformSharedMemoryRegion Create(Mode mode, size_t size); 207 208 static bool CheckPlatformHandlePermissionsCorrespondToMode( 209 PlatformHandle handle, 210 Mode mode, 211 size_t size); 212 213 PlatformSharedMemoryRegion(ScopedPlatformHandle handle, 214 Mode mode, 215 size_t size, 216 const UnguessableToken& guid); 217 218 ScopedPlatformHandle handle_; 219 Mode mode_ = Mode::kReadOnly; 220 size_t size_ = 0; 221 UnguessableToken guid_; 222 223 DISALLOW_COPY_AND_ASSIGN(PlatformSharedMemoryRegion); 224 }; 225 226 } // namespace subtle 227 } // namespace base 228 229 #endif // BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_ 230