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_MAC) 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 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD) 31 namespace content { 32 class SandboxIPCHandler; 33 } 34 #endif 35 36 namespace base { 37 namespace subtle { 38 39 #if defined(OS_POSIX) && !defined(OS_MAC) && !defined(OS_ANDROID) 40 // Helper structs to keep two descriptors on POSIX. It's needed to support 41 // ConvertToReadOnly(). 42 struct BASE_EXPORT FDPair { 43 // The main shared memory descriptor that is used for mapping. May be either 44 // writable or read-only, depending on region's mode. 45 int fd; 46 // The read-only descriptor, valid only in kWritable mode. Replaces |fd| when 47 // a region is converted to read-only. 48 int readonly_fd; 49 }; 50 51 struct BASE_EXPORT ScopedFDPair { 52 ScopedFDPair(); 53 ScopedFDPair(ScopedFD in_fd, ScopedFD in_readonly_fd); 54 ScopedFDPair(ScopedFDPair&&); 55 ScopedFDPair& operator=(ScopedFDPair&&); 56 ~ScopedFDPair(); 57 58 FDPair get() const; 59 60 ScopedFD fd; 61 ScopedFD readonly_fd; 62 }; 63 #endif 64 65 // Implementation class for shared memory regions. 66 // 67 // This class does the following: 68 // 69 // - Wraps and owns a shared memory region platform handle. 70 // - Provides a way to allocate a new region of platform shared memory of given 71 // size. 72 // - Provides a way to create mapping of the region in the current process' 73 // address space, under special access-control constraints (see Mode). 74 // - Provides methods to help transferring the handle across process boundaries. 75 // - Holds a 128-bit unique identifier used to uniquely identify the same 76 // kernel region resource across processes (used for memory tracking). 77 // - Has a method to retrieve the region's size in bytes. 78 // 79 // IMPORTANT NOTE: Users should never use this directly, but 80 // ReadOnlySharedMemoryRegion, WritableSharedMemoryRegion or 81 // UnsafeSharedMemoryRegion since this is an implementation class. 82 class BASE_EXPORT PlatformSharedMemoryRegion { 83 public: 84 // Permission mode of the platform handle. Each mode corresponds to one of the 85 // typed shared memory classes: 86 // 87 // * ReadOnlySharedMemoryRegion: A region that can only create read-only 88 // mappings. 89 // 90 // * WritableSharedMemoryRegion: A region that can only create writable 91 // mappings. The region can be demoted to ReadOnlySharedMemoryRegion without 92 // the possibility of promoting back to writable. 93 // 94 // * UnsafeSharedMemoryRegion: A region that can only create writable 95 // mappings. The region cannot be demoted to ReadOnlySharedMemoryRegion. 96 enum class Mode { 97 kReadOnly, // ReadOnlySharedMemoryRegion 98 kWritable, // WritableSharedMemoryRegion 99 kUnsafe, // UnsafeSharedMemoryRegion 100 kMaxValue = kUnsafe 101 }; 102 103 // Errors that can occur during Shared Memory construction. 104 // These match tools/metrics/histograms/enums.xml. 105 // This enum is append-only. 106 enum class CreateError { 107 SUCCESS = 0, 108 SIZE_ZERO = 1, 109 SIZE_TOO_LARGE = 2, 110 INITIALIZE_ACL_FAILURE = 3, 111 INITIALIZE_SECURITY_DESC_FAILURE = 4, 112 SET_SECURITY_DESC_FAILURE = 5, 113 CREATE_FILE_MAPPING_FAILURE = 6, 114 REDUCE_PERMISSIONS_FAILURE = 7, 115 ALREADY_EXISTS = 8, 116 ALLOCATE_FILE_REGION_FAILURE = 9, 117 FSTAT_FAILURE = 10, 118 INODES_MISMATCH = 11, 119 GET_SHMEM_TEMP_DIR_FAILURE = 12, 120 kMaxValue = GET_SHMEM_TEMP_DIR_FAILURE 121 }; 122 123 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD) 124 // Structure to limit access to executable region creation. 125 struct ExecutableRegion { 126 private: 127 // Creates a new shared memory region the unsafe mode (writable and not and 128 // convertible to read-only), and in addition marked executable. A ScopedFD 129 // to this region is returned. Any any mapping will have to be done 130 // manually, including setting executable permissions if necessary 131 // 132 // This is only used to support sandbox_ipc_linux.cc, and should not be used 133 // anywhere else in chrome. This is restricted via AllowCreateExecutable. 134 // TODO(crbug.com/982879): remove this when NaCl is unshipped. 135 // 136 // Returns an invalid ScopedFD if the call fails. 137 static ScopedFD CreateFD(size_t size); 138 139 friend class content::SandboxIPCHandler; 140 }; 141 #endif 142 143 // Platform-specific shared memory type used by this class. 144 #if defined(OS_MAC) 145 using PlatformHandle = mach_port_t; 146 using ScopedPlatformHandle = mac::ScopedMachSendRight; 147 #elif defined(OS_FUCHSIA) 148 using PlatformHandle = zx::unowned_vmo; 149 using ScopedPlatformHandle = zx::vmo; 150 #elif defined(OS_WIN) 151 using PlatformHandle = HANDLE; 152 using ScopedPlatformHandle = win::ScopedHandle; 153 #elif defined(OS_ANDROID) 154 using PlatformHandle = int; 155 using ScopedPlatformHandle = ScopedFD; 156 #else 157 using PlatformHandle = FDPair; 158 using ScopedPlatformHandle = ScopedFDPair; 159 #endif 160 161 // The minimum alignment in bytes that any mapped address produced by Map() 162 // and MapAt() is guaranteed to have. 163 enum { kMapMinimumAlignment = 32 }; 164 165 // Creates a new PlatformSharedMemoryRegion with corresponding mode and size. 166 // Creating in kReadOnly mode isn't supported because then there will be no 167 // way to modify memory content. 168 static PlatformSharedMemoryRegion CreateWritable(size_t size); 169 static PlatformSharedMemoryRegion CreateUnsafe(size_t size); 170 171 // Returns a new PlatformSharedMemoryRegion that takes ownership of the 172 // |handle|. All parameters must be taken from another valid 173 // PlatformSharedMemoryRegion instance, e.g. |size| must be equal to the 174 // actual region size as allocated by the kernel. 175 // Closes the |handle| and returns an invalid instance if passed parameters 176 // are invalid. 177 static PlatformSharedMemoryRegion Take(ScopedPlatformHandle handle, 178 Mode mode, 179 size_t size, 180 const UnguessableToken& guid); 181 #if defined(OS_POSIX) && !defined(OS_ANDROID) && !defined(OS_MAC) 182 // Specialized version of Take() for POSIX that takes only one file descriptor 183 // instead of pair. Cannot be used with kWritable |mode|. 184 static PlatformSharedMemoryRegion Take(ScopedFD handle, 185 Mode mode, 186 size_t size, 187 const UnguessableToken& guid); 188 #endif 189 190 // Default constructor initializes an invalid instance, i.e. an instance that 191 // doesn't wrap any valid platform handle. 192 PlatformSharedMemoryRegion(); 193 194 // Move operations are allowed. 195 PlatformSharedMemoryRegion(PlatformSharedMemoryRegion&&); 196 PlatformSharedMemoryRegion& operator=(PlatformSharedMemoryRegion&&); 197 198 // Destructor closes the platform handle. Does nothing if the handle is 199 // invalid. 200 ~PlatformSharedMemoryRegion(); 201 202 // Passes ownership of the platform handle to the caller. The current instance 203 // becomes invalid. It's the responsibility of the caller to close the 204 // handle. If the current instance is invalid, ScopedPlatformHandle will also 205 // be invalid. 206 ScopedPlatformHandle PassPlatformHandle() WARN_UNUSED_RESULT; 207 208 // Returns the platform handle. The current instance keeps ownership of this 209 // handle. 210 PlatformHandle GetPlatformHandle() const; 211 212 // Whether the platform handle is valid. 213 bool IsValid() const; 214 215 // Duplicates the platform handle and creates a new PlatformSharedMemoryRegion 216 // with the same |mode_|, |size_| and |guid_| that owns this handle. Returns 217 // invalid region on failure, the current instance remains valid. 218 // Can be called only in kReadOnly and kUnsafe modes, CHECK-fails if is 219 // called in kWritable mode. 220 PlatformSharedMemoryRegion Duplicate() const; 221 222 // Converts the region to read-only. Returns whether the operation succeeded. 223 // Makes the current instance invalid on failure. Can be called only in 224 // kWritable mode, all other modes will CHECK-fail. The object will have 225 // kReadOnly mode after this call on success. 226 bool ConvertToReadOnly(); 227 #if defined(OS_MAC) 228 // Same as above, but |mapped_addr| is used as a hint to avoid additional 229 // mapping of the memory object. 230 // |mapped_addr| must be mapped location of |memory_object_|. If the location 231 // is unknown, |mapped_addr| should be |nullptr|. 232 bool ConvertToReadOnly(void* mapped_addr); 233 #endif // defined(OS_MAC) 234 235 // Converts the region to unsafe. Returns whether the operation succeeded. 236 // Makes the current instance invalid on failure. Can be called only in 237 // kWritable mode, all other modes will CHECK-fail. The object will have 238 // kUnsafe mode after this call on success. 239 bool ConvertToUnsafe(); 240 241 // Maps |size| bytes of the shared memory region starting with the given 242 // |offset| into the caller's address space. |offset| must be aligned to value 243 // of |SysInfo::VMAllocationGranularity()|. Fails if requested bytes are out 244 // of the region limits. 245 // Returns true and sets |memory| and |mapped_size| on success, returns false 246 // and leaves output parameters in unspecified state otherwise. The mapped 247 // address is guaranteed to have an alignment of at least 248 // |kMapMinimumAlignment|. 249 bool MapAt(off_t offset, 250 size_t size, 251 void** memory, 252 size_t* mapped_size) const; 253 GetGUID()254 const UnguessableToken& GetGUID() const { return guid_; } 255 GetSize()256 size_t GetSize() const { return size_; } 257 GetMode()258 Mode GetMode() const { return mode_; } 259 260 private: 261 FRIEND_TEST_ALL_PREFIXES(PlatformSharedMemoryRegionTest, 262 CreateReadOnlyRegionDeathTest); 263 FRIEND_TEST_ALL_PREFIXES(PlatformSharedMemoryRegionTest, 264 CheckPlatformHandlePermissionsCorrespondToMode); 265 static PlatformSharedMemoryRegion Create(Mode mode, 266 size_t size 267 #if defined(OS_LINUX) || defined(OS_CHROMEOS) || defined(OS_BSD) 268 , 269 bool executable = false 270 #endif 271 ); 272 273 static bool CheckPlatformHandlePermissionsCorrespondToMode( 274 PlatformHandle handle, 275 Mode mode, 276 size_t size); 277 278 PlatformSharedMemoryRegion(ScopedPlatformHandle handle, 279 Mode mode, 280 size_t size, 281 const UnguessableToken& guid); 282 283 bool MapAtInternal(off_t offset, 284 size_t size, 285 void** memory, 286 size_t* mapped_size) const; 287 288 ScopedPlatformHandle handle_; 289 Mode mode_ = Mode::kReadOnly; 290 size_t size_ = 0; 291 UnguessableToken guid_; 292 293 DISALLOW_COPY_AND_ASSIGN(PlatformSharedMemoryRegion); 294 }; 295 296 } // namespace subtle 297 } // namespace base 298 299 #endif // BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_ 300