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