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 #ifndef BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_ 6 #define BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_ 7 8 #include "base/base_export.h" 9 #include "base/containers/span.h" 10 #include "base/gtest_prod_util.h" 11 #include "base/memory/platform_shared_memory_handle.h" 12 #include "base/memory/shared_memory_mapper.h" 13 #include "base/unguessable_token.h" 14 #include "build/build_config.h" 15 #include "third_party/abseil-cpp/absl/types/optional.h" 16 17 #include <stdint.h> 18 19 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) 20 namespace content { 21 class SandboxIPCHandler; 22 } 23 #endif 24 25 namespace base { 26 namespace subtle { 27 28 // Implementation class for shared memory regions. 29 // 30 // This class does the following: 31 // 32 // - Wraps and owns a shared memory region platform handle. 33 // - Provides a way to allocate a new region of platform shared memory of given 34 // size. 35 // - Provides a way to create mapping of the region in the current process' 36 // address space, under special access-control constraints (see Mode). 37 // - Provides methods to help transferring the handle across process boundaries. 38 // - Holds a 128-bit unique identifier used to uniquely identify the same 39 // kernel region resource across processes (used for memory tracking). 40 // - Has a method to retrieve the region's size in bytes. 41 // 42 // IMPORTANT NOTE: Users should never use this directly, but 43 // ReadOnlySharedMemoryRegion, WritableSharedMemoryRegion or 44 // UnsafeSharedMemoryRegion since this is an implementation class. 45 class BASE_EXPORT PlatformSharedMemoryRegion { 46 public: 47 // Permission mode of the platform handle. Each mode corresponds to one of the 48 // typed shared memory classes: 49 // 50 // * ReadOnlySharedMemoryRegion: A region that can only create read-only 51 // mappings. 52 // 53 // * WritableSharedMemoryRegion: A region that can only create writable 54 // mappings. The region can be demoted to ReadOnlySharedMemoryRegion without 55 // the possibility of promoting back to writable. 56 // 57 // * UnsafeSharedMemoryRegion: A region that can only create writable 58 // mappings. The region cannot be demoted to ReadOnlySharedMemoryRegion. 59 enum class Mode { 60 kReadOnly, // ReadOnlySharedMemoryRegion 61 kWritable, // WritableSharedMemoryRegion 62 kUnsafe, // UnsafeSharedMemoryRegion 63 kMaxValue = kUnsafe 64 }; 65 66 // Errors that can occur during Shared Memory construction. 67 // These match tools/metrics/histograms/enums.xml. 68 // This enum is append-only. 69 enum class CreateError { 70 SUCCESS = 0, 71 SIZE_ZERO = 1, 72 SIZE_TOO_LARGE = 2, 73 INITIALIZE_ACL_FAILURE = 3, 74 INITIALIZE_SECURITY_DESC_FAILURE = 4, 75 SET_SECURITY_DESC_FAILURE = 5, 76 CREATE_FILE_MAPPING_FAILURE = 6, 77 REDUCE_PERMISSIONS_FAILURE = 7, 78 ALREADY_EXISTS = 8, 79 ALLOCATE_FILE_REGION_FAILURE = 9, 80 FSTAT_FAILURE = 10, 81 INODES_MISMATCH = 11, 82 GET_SHMEM_TEMP_DIR_FAILURE = 12, 83 kMaxValue = GET_SHMEM_TEMP_DIR_FAILURE 84 }; 85 86 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) 87 // Structure to limit access to executable region creation. 88 struct ExecutableRegion { 89 private: 90 // Creates a new shared memory region the unsafe mode (writable and not and 91 // convertible to read-only), and in addition marked executable. A ScopedFD 92 // to this region is returned. Any any mapping will have to be done 93 // manually, including setting executable permissions if necessary 94 // 95 // This is only used to support sandbox_ipc_linux.cc, and should not be used 96 // anywhere else in chrome. This is restricted via AllowCreateExecutable. 97 // TODO(crbug.com/982879): remove this when NaCl is unshipped. 98 // 99 // Returns an invalid ScopedFD if the call fails. 100 static ScopedFD CreateFD(size_t size); 101 102 friend class content::SandboxIPCHandler; 103 }; 104 #endif 105 106 // The minimum alignment in bytes that any mapped address produced by Map() 107 // and MapAt() is guaranteed to have. 108 enum { kMapMinimumAlignment = 32 }; 109 110 // Creates a new PlatformSharedMemoryRegion with corresponding mode and size. 111 // Creating in kReadOnly mode isn't supported because then there will be no 112 // way to modify memory content. 113 static PlatformSharedMemoryRegion CreateWritable(size_t size); 114 static PlatformSharedMemoryRegion CreateUnsafe(size_t size); 115 116 // Returns a new PlatformSharedMemoryRegion that takes ownership of the 117 // |handle|. All parameters must be taken from another valid 118 // PlatformSharedMemoryRegion instance, e.g. |size| must be equal to the 119 // actual region size as allocated by the kernel. 120 // Closes the |handle| and returns an invalid instance if passed parameters 121 // are invalid. 122 static PlatformSharedMemoryRegion Take( 123 ScopedPlatformSharedMemoryHandle handle, 124 Mode mode, 125 size_t size, 126 const UnguessableToken& guid); 127 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_APPLE) 128 // Specialized version of Take() for POSIX that takes only one file descriptor 129 // instead of pair. Cannot be used with kWritable |mode|. 130 static PlatformSharedMemoryRegion Take(ScopedFD handle, 131 Mode mode, 132 size_t size, 133 const UnguessableToken& guid); 134 #endif 135 136 // Default constructor initializes an invalid instance, i.e. an instance that 137 // doesn't wrap any valid platform handle. 138 PlatformSharedMemoryRegion(); 139 140 // Move operations are allowed. 141 PlatformSharedMemoryRegion(PlatformSharedMemoryRegion&&); 142 PlatformSharedMemoryRegion& operator=(PlatformSharedMemoryRegion&&); 143 PlatformSharedMemoryRegion(const PlatformSharedMemoryRegion&) = delete; 144 PlatformSharedMemoryRegion& operator=(const PlatformSharedMemoryRegion&) = 145 delete; 146 147 // Destructor closes the platform handle. Does nothing if the handle is 148 // invalid. 149 ~PlatformSharedMemoryRegion(); 150 151 // Passes ownership of the platform handle to the caller. The current instance 152 // becomes invalid. It's the responsibility of the caller to close the 153 // handle. If the current instance is invalid, ScopedPlatformHandle will also 154 // be invalid. 155 [[nodiscard]] ScopedPlatformSharedMemoryHandle PassPlatformHandle(); 156 157 // Returns the platform handle. The current instance keeps ownership of this 158 // handle. 159 PlatformSharedMemoryHandle GetPlatformHandle() const; 160 161 // Whether the platform handle is valid. 162 bool IsValid() const; 163 164 // Duplicates the platform handle and creates a new PlatformSharedMemoryRegion 165 // with the same |mode_|, |size_| and |guid_| that owns this handle. Returns 166 // invalid region on failure, the current instance remains valid. 167 // Can be called only in kReadOnly and kUnsafe modes, CHECK-fails if is 168 // called in kWritable mode. 169 PlatformSharedMemoryRegion Duplicate() const; 170 171 // Converts the region to read-only. Returns whether the operation succeeded. 172 // Makes the current instance invalid on failure. Can be called only in 173 // kWritable mode, all other modes will CHECK-fail. The object will have 174 // kReadOnly mode after this call on success. 175 bool ConvertToReadOnly(); 176 #if BUILDFLAG(IS_APPLE) 177 // Same as above, but |mapped_addr| is used as a hint to avoid additional 178 // mapping of the memory object. 179 // |mapped_addr| must be mapped location of |memory_object_|. If the location 180 // is unknown, |mapped_addr| should be |nullptr|. 181 bool ConvertToReadOnly(void* mapped_addr); 182 #endif // BUILDFLAG(IS_APPLE) 183 184 // Converts the region to unsafe. Returns whether the operation succeeded. 185 // Makes the current instance invalid on failure. Can be called only in 186 // kWritable mode, all other modes will CHECK-fail. The object will have 187 // kUnsafe mode after this call on success. 188 bool ConvertToUnsafe(); 189 190 // Maps |size| bytes of the shared memory region starting with the given 191 // |offset| into the caller's address space using the provided 192 // |SharedMemoryMapper|. |offset| must be aligned to value of 193 // |SysInfo::VMAllocationGranularity()|. Fails if requested bytes are out of 194 // the region limits. Returns the mapping as span on success, or absl::nullopt 195 // on failure. The mapped address is guaranteed to have an alignment of at 196 // least |kMapMinimumAlignment|. 197 absl::optional<span<uint8_t>> MapAt(uint64_t offset, 198 size_t size, 199 SharedMemoryMapper* mapper) const; 200 GetGUID()201 const UnguessableToken& GetGUID() const { return guid_; } 202 GetSize()203 size_t GetSize() const { return size_; } 204 GetMode()205 Mode GetMode() const { return mode_; } 206 207 private: 208 FRIEND_TEST_ALL_PREFIXES(PlatformSharedMemoryRegionTest, 209 CreateReadOnlyRegionDeathTest); 210 FRIEND_TEST_ALL_PREFIXES(PlatformSharedMemoryRegionTest, 211 CheckPlatformHandlePermissionsCorrespondToMode); 212 static PlatformSharedMemoryRegion Create(Mode mode, 213 size_t size 214 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) 215 , 216 bool executable = false 217 #endif 218 ); 219 220 static bool CheckPlatformHandlePermissionsCorrespondToMode( 221 PlatformSharedMemoryHandle handle, 222 Mode mode, 223 size_t size); 224 225 PlatformSharedMemoryRegion(ScopedPlatformSharedMemoryHandle handle, 226 Mode mode, 227 size_t size, 228 const UnguessableToken& guid); 229 230 ScopedPlatformSharedMemoryHandle handle_; 231 Mode mode_ = Mode::kReadOnly; 232 size_t size_ = 0; 233 UnguessableToken guid_; 234 }; 235 236 } // namespace subtle 237 } // namespace base 238 239 #endif // BASE_MEMORY_PLATFORM_SHARED_MEMORY_REGION_H_ 240