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