• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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 #include "base/memory/shared_memory.h"
6 
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <stddef.h>
10 #include <sys/mman.h>
11 #include <sys/stat.h>
12 #include <unistd.h>
13 
14 #include "base/files/file_util.h"
15 #include "base/files/scoped_file.h"
16 #include "base/logging.h"
17 #include "base/macros.h"
18 #include "base/memory/shared_memory_helper.h"
19 // Unsupported in libchrome.
20 // #include "base/memory/shared_memory_tracker.h"
21 #include "base/posix/eintr_wrapper.h"
22 #include "base/posix/safe_strerror.h"
23 #include "base/process/process_metrics.h"
24 #include "base/scoped_generic.h"
25 #include "base/strings/utf_string_conversions.h"
26 #include "base/threading/thread_restrictions.h"
27 #include "base/trace_event/trace_event.h"
28 #include "base/unguessable_token.h"
29 #include "build/build_config.h"
30 
31 #if defined(OS_ANDROID)
32 #include "base/os_compat_android.h"
33 #endif
34 #if defined(OS_ANDROID) || defined(__ANDROID__)
35 #include "third_party/ashmem/ashmem.h"
36 #endif
37 
38 #if defined(OS_MACOSX) && !defined(OS_IOS)
39 #error "MacOS uses shared_memory_mac.cc"
40 #endif
41 
42 namespace base {
43 
44 SharedMemory::SharedMemory() = default;
45 
SharedMemory(const SharedMemoryHandle & handle,bool read_only)46 SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only)
47     : shm_(handle), read_only_(read_only) {}
48 
~SharedMemory()49 SharedMemory::~SharedMemory() {
50   Unmap();
51   Close();
52 }
53 
54 // static
IsHandleValid(const SharedMemoryHandle & handle)55 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
56   return handle.IsValid();
57 }
58 
59 // static
CloseHandle(const SharedMemoryHandle & handle)60 void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
61   DCHECK(handle.IsValid());
62   handle.Close();
63 }
64 
65 // static
GetHandleLimit()66 size_t SharedMemory::GetHandleLimit() {
67   return GetMaxFds();
68 }
69 
70 // static
DuplicateHandle(const SharedMemoryHandle & handle)71 SharedMemoryHandle SharedMemory::DuplicateHandle(
72     const SharedMemoryHandle& handle) {
73   return handle.Duplicate();
74 }
75 
76 // static
GetFdFromSharedMemoryHandle(const SharedMemoryHandle & handle)77 int SharedMemory::GetFdFromSharedMemoryHandle(
78     const SharedMemoryHandle& handle) {
79   return handle.GetHandle();
80 }
81 
CreateAndMapAnonymous(size_t size)82 bool SharedMemory::CreateAndMapAnonymous(size_t size) {
83   return CreateAnonymous(size) && Map(size);
84 }
85 
86 #if !defined(OS_ANDROID) && !defined(__ANDROID__)
87 
88 // Chromium mostly only uses the unique/private shmem as specified by
89 // "name == L"". The exception is in the StatsTable.
90 // TODO(jrg): there is no way to "clean up" all unused named shmem if
91 // we restart from a crash.  (That isn't a new problem, but it is a problem.)
92 // In case we want to delete it later, it may be useful to save the value
93 // of mem_filename after FilePathForMemoryName().
Create(const SharedMemoryCreateOptions & options)94 bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
95   DCHECK(!shm_.IsValid());
96   if (options.size == 0) return false;
97 
98   if (options.size > static_cast<size_t>(std::numeric_limits<int>::max()))
99     return false;
100 
101   // This function theoretically can block on the disk, but realistically
102   // the temporary files we create will just go into the buffer cache
103   // and be deleted before they ever make it out to disk.
104   ThreadRestrictions::ScopedAllowIO allow_io;
105 
106   bool fix_size = true;
107   ScopedFD fd;
108   ScopedFD readonly_fd;
109   FilePath path;
110   if (!options.name_deprecated || options.name_deprecated->empty()) {
111     bool result =
112         CreateAnonymousSharedMemory(options, &fd, &readonly_fd, &path);
113     if (!result)
114       return false;
115   } else {
116     if (!FilePathForMemoryName(*options.name_deprecated, &path))
117       return false;
118 
119     // Make sure that the file is opened without any permission
120     // to other users on the system.
121     const mode_t kOwnerOnly = S_IRUSR | S_IWUSR;
122 
123     // First, try to create the file.
124     fd.reset(HANDLE_EINTR(
125         open(path.value().c_str(), O_RDWR | O_CREAT | O_EXCL, kOwnerOnly)));
126     if (!fd.is_valid() && options.open_existing_deprecated) {
127       // If this doesn't work, try and open an existing file in append mode.
128       // Opening an existing file in a world writable directory has two main
129       // security implications:
130       // - Attackers could plant a file under their control, so ownership of
131       //   the file is checked below.
132       // - Attackers could plant a symbolic link so that an unexpected file
133       //   is opened, so O_NOFOLLOW is passed to open().
134 #if !defined(OS_AIX)
135       fd.reset(HANDLE_EINTR(
136           open(path.value().c_str(), O_RDWR | O_APPEND | O_NOFOLLOW)));
137 #else
138       // AIX has no 64-bit support for open flags such as -
139       //  O_CLOEXEC, O_NOFOLLOW and O_TTY_INIT.
140       fd.reset(HANDLE_EINTR(open(path.value().c_str(), O_RDWR | O_APPEND)));
141 #endif
142       // Check that the current user owns the file.
143       // If uid != euid, then a more complex permission model is used and this
144       // API is not appropriate.
145       const uid_t real_uid = getuid();
146       const uid_t effective_uid = geteuid();
147       struct stat sb;
148       if (fd.is_valid() &&
149           (fstat(fd.get(), &sb) != 0 || sb.st_uid != real_uid ||
150            sb.st_uid != effective_uid)) {
151         LOG(ERROR) <<
152             "Invalid owner when opening existing shared memory file.";
153         close(fd.get());
154         return false;
155       }
156 
157       // An existing file was opened, so its size should not be fixed.
158       fix_size = false;
159     }
160 
161     if (options.share_read_only) {
162       // Also open as readonly so that we can GetReadOnlyHandle.
163       readonly_fd.reset(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)));
164       if (!readonly_fd.is_valid()) {
165         DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
166         close(fd.get());
167         return false;
168       }
169     }
170   }
171   if (fd.is_valid() && fix_size) {
172     // Get current size.
173     struct stat stat;
174     if (fstat(fd.get(), &stat) != 0)
175       return false;
176     const size_t current_size = stat.st_size;
177     if (current_size != options.size) {
178       if (HANDLE_EINTR(ftruncate(fd.get(), options.size)) != 0)
179         return false;
180     }
181     requested_size_ = options.size;
182   }
183   if (!fd.is_valid()) {
184     PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
185     FilePath dir = path.DirName();
186     if (access(dir.value().c_str(), W_OK | X_OK) < 0) {
187       PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value();
188       if (dir.value() == "/dev/shm") {
189         LOG(FATAL) << "This is frequently caused by incorrect permissions on "
190                    << "/dev/shm.  Try 'sudo chmod 1777 /dev/shm' to fix.";
191       }
192     }
193     return false;
194   }
195 
196   int mapped_file = -1;
197   int readonly_mapped_file = -1;
198 
199   bool result = PrepareMapFile(std::move(fd), std::move(readonly_fd),
200                                &mapped_file, &readonly_mapped_file);
201   shm_ = SharedMemoryHandle(FileDescriptor(mapped_file, false), options.size,
202                             UnguessableToken::Create());
203   readonly_shm_ =
204       SharedMemoryHandle(FileDescriptor(readonly_mapped_file, false),
205                          options.size, shm_.GetGUID());
206   return result;
207 }
208 
209 // Our current implementation of shmem is with mmap()ing of files.
210 // These files need to be deleted explicitly.
211 // In practice this call is only needed for unit tests.
Delete(const std::string & name)212 bool SharedMemory::Delete(const std::string& name) {
213   FilePath path;
214   if (!FilePathForMemoryName(name, &path))
215     return false;
216 
217   if (PathExists(path))
218     return DeleteFile(path, false);
219 
220   // Doesn't exist, so success.
221   return true;
222 }
223 
Open(const std::string & name,bool read_only)224 bool SharedMemory::Open(const std::string& name, bool read_only) {
225   FilePath path;
226   if (!FilePathForMemoryName(name, &path))
227     return false;
228 
229   read_only_ = read_only;
230 
231   int mode = read_only ? O_RDONLY : O_RDWR;
232   ScopedFD fd(HANDLE_EINTR(open(path.value().c_str(), mode)));
233   ScopedFD readonly_fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)));
234   if (!readonly_fd.is_valid()) {
235     DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
236     return false;
237   }
238   int mapped_file = -1;
239   int readonly_mapped_file = -1;
240   bool result = PrepareMapFile(std::move(fd), std::move(readonly_fd),
241                                &mapped_file, &readonly_mapped_file);
242   // This form of sharing shared memory is deprecated. https://crbug.com/345734.
243   // However, we can't get rid of it without a significant refactor because its
244   // used to communicate between two versions of the same service process, very
245   // early in the life cycle.
246   // Technically, we should also pass the GUID from the original shared memory
247   // region. We don't do that - this means that we will overcount this memory,
248   // which thankfully isn't relevant since Chrome only communicates with a
249   // single version of the service process.
250   // We pass the size |0|, which is a dummy size and wrong, but otherwise
251   // harmless.
252   shm_ = SharedMemoryHandle(FileDescriptor(mapped_file, false), 0u,
253                             UnguessableToken::Create());
254   readonly_shm_ = SharedMemoryHandle(
255       FileDescriptor(readonly_mapped_file, false), 0, shm_.GetGUID());
256   return result;
257 }
258 #endif  // !defined(OS_ANDROID) && !defined(__ANDROID__)
259 
MapAt(off_t offset,size_t bytes)260 bool SharedMemory::MapAt(off_t offset, size_t bytes) {
261   if (!shm_.IsValid())
262     return false;
263 
264   if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
265     return false;
266 
267   if (memory_)
268     return false;
269 
270 #if defined(OS_ANDROID) || defined(__ANDROID__)
271   // On Android, Map can be called with a size and offset of zero to use the
272   // ashmem-determined size.
273   if (bytes == 0) {
274     DCHECK_EQ(0, offset);
275     int ashmem_bytes = ashmem_get_size_region(shm_.GetHandle());
276     if (ashmem_bytes < 0)
277       return false;
278     bytes = ashmem_bytes;
279   }
280 
281   // Sanity check. This shall catch invalid uses of the SharedMemory APIs
282   // but will not protect against direct mmap() attempts.
283   // if (shm_.IsReadOnly()) {
284   //   // Use a DCHECK() to call writable mappings with read-only descriptors
285   //   // in debug builds immediately. Return an error for release builds
286   //   // or during unit-testing (assuming a ScopedLogAssertHandler was installed).
287   //   DCHECK(read_only_)
288   //       << "Trying to map a region writable with a read-only descriptor.";
289   //   if (!read_only_) {
290   //     return false;
291   //   }
292   //   if (!shm_.SetRegionReadOnly()) {  // Ensure the region is read-only.
293   //     return false;
294   //   }
295   // }
296 #endif
297 
298   memory_ = mmap(nullptr, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
299                  MAP_SHARED, shm_.GetHandle(), offset);
300 
301   bool mmap_succeeded = memory_ && memory_ != reinterpret_cast<void*>(-1);
302   if (mmap_succeeded) {
303     mapped_size_ = bytes;
304     mapped_id_ = shm_.GetGUID();
305     DCHECK_EQ(0U,
306               reinterpret_cast<uintptr_t>(memory_) &
307                   (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
308     // Unsupported in libchrome.
309     // SharedMemoryTracker::GetInstance()->IncrementMemoryUsage(*this);
310   } else {
311     memory_ = nullptr;
312   }
313 
314   return mmap_succeeded;
315 }
316 
Unmap()317 bool SharedMemory::Unmap() {
318   if (!memory_)
319     return false;
320 
321   // Unsupported in libchrome.
322   // SharedMemoryTracker::GetInstance()->DecrementMemoryUsage(*this);
323   munmap(memory_, mapped_size_);
324   memory_ = nullptr;
325   mapped_size_ = 0;
326   mapped_id_ = UnguessableToken();
327   return true;
328 }
329 
handle() const330 SharedMemoryHandle SharedMemory::handle() const {
331   return shm_;
332 }
333 
TakeHandle()334 SharedMemoryHandle SharedMemory::TakeHandle() {
335   SharedMemoryHandle handle_copy = shm_;
336   handle_copy.SetOwnershipPassesToIPC(true);
337   Unmap();
338   shm_ = SharedMemoryHandle();
339   return handle_copy;
340 }
341 
342 #if !defined(OS_ANDROID) && !defined(__ANDROID__)
Close()343 void SharedMemory::Close() {
344   if (shm_.IsValid()) {
345     shm_.Close();
346     shm_ = SharedMemoryHandle();
347   }
348   if (readonly_shm_.IsValid()) {
349     readonly_shm_.Close();
350     readonly_shm_ = SharedMemoryHandle();
351   }
352 }
353 
354 // For the given shmem named |mem_name|, return a filename to mmap()
355 // (and possibly create).  Modifies |filename|.  Return false on
356 // error, or true of we are happy.
FilePathForMemoryName(const std::string & mem_name,FilePath * path)357 bool SharedMemory::FilePathForMemoryName(const std::string& mem_name,
358                                          FilePath* path) {
359   // mem_name will be used for a filename; make sure it doesn't
360   // contain anything which will confuse us.
361   DCHECK_EQ(std::string::npos, mem_name.find('/'));
362   DCHECK_EQ(std::string::npos, mem_name.find('\0'));
363 
364   FilePath temp_dir;
365   if (!GetShmemTempDir(false, &temp_dir))
366     return false;
367 
368 #if defined(GOOGLE_CHROME_BUILD)
369   static const char kShmem[] = "com.google.Chrome.shmem.";
370 #else
371   static const char kShmem[] = "org.chromium.Chromium.shmem.";
372 #endif
373   CR_DEFINE_STATIC_LOCAL(const std::string, name_base, (kShmem));
374   *path = temp_dir.AppendASCII(name_base + mem_name);
375   return true;
376 }
377 
GetReadOnlyHandle() const378 SharedMemoryHandle SharedMemory::GetReadOnlyHandle() const {
379   CHECK(readonly_shm_.IsValid());
380   return readonly_shm_.Duplicate();
381 }
382 #endif  // !defined(OS_ANDROID) && !defined(__ANDROID__)
383 
384 }  // namespace base
385