• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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