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.
5 #include "base/memory/shared_memory.h"
11 #include <sys/types.h>
15 #include "base/files/file_util.h"
16 #include "base/files/scoped_file.h"
17 #include "base/lazy_instance.h"
18 #include "base/logging.h"
19 #include "base/metrics/field_trial.h"
20 #include "base/metrics/histogram.h"
21 #include "base/posix/eintr_wrapper.h"
22 #include "base/process/process_metrics.h"
23 #include "base/profiler/scoped_tracker.h"
24 #include "base/safe_strerror_posix.h"
25 #include "base/scoped_generic.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/synchronization/lock.h"
28 #include "base/threading/platform_thread.h"
29 #include "base/threading/thread_restrictions.h"
30 #include "base/time/time.h"
32 #if defined(OS_MACOSX)
33 #include "base/mac/foundation_util.h"
36 #if defined(OS_ANDROID)
37 #include "base/os_compat_android.h"
38 #include "third_party/ashmem/ashmem.h"
45 LazyInstance
<Lock
>::Leaky g_thread_lock_
= LAZY_INSTANCE_INITIALIZER
;
47 #if !defined(OS_ANDROID)
48 struct ScopedPathUnlinkerTraits
{
49 static FilePath
* InvalidValue() { return nullptr; }
51 static void Free(FilePath
* path
) {
52 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
54 tracked_objects::ScopedTracker
tracking_profile(
55 FROM_HERE_WITH_EXPLICIT_FUNCTION(
56 "466437 SharedMemory::Create::Unlink"));
57 if (unlink(path
->value().c_str()))
58 PLOG(WARNING
) << "unlink";
62 // Unlinks the FilePath when the object is destroyed.
63 typedef ScopedGeneric
<FilePath
*, ScopedPathUnlinkerTraits
> ScopedPathUnlinker
;
65 const char kSharedMemoryBatchCreate
[] = "kSharedMemoryBatchCreate";
66 const char kSharedMemoryCreateStrategy
[] = "SharedMemoryCreateStrategy";
68 #if defined(OS_MACOSX) && !defined(OS_IOS)
69 const int kBatchSize
= 5;
71 // This variable must only be accessed if |g_thread_lock_| is held.
72 LazyInstance
<std::vector
<FILE*>>::Leaky g_file_pool_
=
73 LAZY_INSTANCE_INITIALIZER
;
74 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
76 // Whether to generate more than 1 shared memory handle at a time, and store
77 // the results in a pool.
78 bool ShouldBatchCreateSharedMemory() {
79 #if !defined(OS_MACOSX) || defined(OS_IOS)
81 #endif // !defined(OS_MACOSX) || defined(OS_IOS)
83 g_thread_lock_
.Get().AssertAcquired();
85 static bool has_determined_group
= false;
86 static bool batch_create_shared_memory
= false;
88 if (has_determined_group
)
89 return batch_create_shared_memory
;
91 const std::string group_name
=
92 base::FieldTrialList::FindFullName(kSharedMemoryCreateStrategy
);
93 batch_create_shared_memory
= group_name
== kSharedMemoryBatchCreate
;
94 has_determined_group
= true;
95 return batch_create_shared_memory
;
98 // Makes a temporary file, fdopens it, and then unlinks it. |fp| is populated
99 // with the fdopened FILE. |readonly_fd| is populated with the opened fd if
100 // options.share_read_only is true. |path| is populated with the location of
101 // the file before it was unlinked.
102 // Returns false if there's an unhandled failure.
103 bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions
& options
,
105 ScopedFD
* readonly_fd
,
107 // It doesn't make sense to have a open-existing private piece of shmem
108 DCHECK(!options
.open_existing_deprecated
);
109 // Q: Why not use the shm_open() etc. APIs?
110 // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU
112 ScopedPathUnlinker path_unlinker
;
113 if (GetShmemTempDir(options
.executable
, &directory
)) {
114 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
116 tracked_objects::ScopedTracker
tracking_profile(
117 FROM_HERE_WITH_EXPLICIT_FUNCTION(
118 "466437 SharedMemory::Create::OpenTemporaryFile"));
119 fp
->reset(base::CreateAndOpenTemporaryFileInDir(directory
, path
));
121 // Deleting the file prevents anyone else from mapping it in (making it
122 // private), and prevents the need for cleanup (once the last fd is
123 // closed, it is truly freed).
125 path_unlinker
.reset(path
);
129 if (options
.share_read_only
) {
130 // TODO(erikchen): Remove ScopedTracker below once
131 // http://crbug.com/466437 is fixed.
132 tracked_objects::ScopedTracker
tracking_profile(
133 FROM_HERE_WITH_EXPLICIT_FUNCTION(
134 "466437 SharedMemory::Create::OpenReadonly"));
135 // Also open as readonly so that we can ShareReadOnlyToProcess.
136 readonly_fd
->reset(HANDLE_EINTR(open(path
->value().c_str(), O_RDONLY
)));
137 if (!readonly_fd
->is_valid()) {
138 DPLOG(ERROR
) << "open(\"" << path
->value() << "\", O_RDONLY) failed";
147 #if defined(OS_MACOSX) && !defined(OS_IOS)
148 // This method must only be called on OSX, since it assumes that
149 // |options.executable| has no effect. It also doesn't fill in |path|, which is
150 // only used for error logging when |options.share_read_only| is false.
151 bool CreateAnonymousSharedMemoryFromBatch(
152 const SharedMemoryCreateOptions
& options
,
155 DCHECK(!options
.share_read_only
);
156 g_thread_lock_
.Get().AssertAcquired();
157 std::vector
<FILE*>& file_pool
= g_file_pool_
.Get();
159 if (file_pool
.empty()) {
160 for (int i
= 0; i
< kBatchSize
; ++i
) {
164 CreateAnonymousSharedMemory(options
, &temp_fp
, NULL
, &temp_path
);
166 file_pool
.push_back(temp_fp
.release());
170 if (file_pool
.empty())
173 FILE* file
= file_pool
.back();
174 file_pool
.pop_back();
178 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
179 #endif // !defined(OS_ANDROID)
182 SharedMemory::SharedMemory()
184 readonly_mapped_file_(-1),
192 SharedMemory::SharedMemory(SharedMemoryHandle handle
, bool read_only
)
193 : mapped_file_(handle
.fd
),
194 readonly_mapped_file_(-1),
198 read_only_(read_only
),
201 if (fstat(handle
.fd
, &st
) == 0) {
202 // If fstat fails, then the file descriptor is invalid and we'll learn this
203 // fact when Map() fails.
208 SharedMemory::SharedMemory(SharedMemoryHandle handle
, bool read_only
,
209 ProcessHandle process
)
210 : mapped_file_(handle
.fd
),
211 readonly_mapped_file_(-1),
215 read_only_(read_only
),
217 // We don't handle this case yet (note the ignored parameter); let's die if
218 // someone comes calling.
222 SharedMemory::~SharedMemory() {
228 bool SharedMemory::IsHandleValid(const SharedMemoryHandle
& handle
) {
229 return handle
.fd
>= 0;
233 SharedMemoryHandle
SharedMemory::NULLHandle() {
234 return SharedMemoryHandle();
238 void SharedMemory::CloseHandle(const SharedMemoryHandle
& handle
) {
239 DCHECK_GE(handle
.fd
, 0);
240 if (close(handle
.fd
) < 0)
241 DPLOG(ERROR
) << "close";
245 size_t SharedMemory::GetHandleLimit() {
246 return base::GetMaxFds();
249 bool SharedMemory::CreateAndMapAnonymous(size_t size
) {
250 return CreateAnonymous(size
) && Map(size
);
253 #if !defined(OS_ANDROID)
254 // Chromium mostly only uses the unique/private shmem as specified by
255 // "name == L"". The exception is in the StatsTable.
256 // TODO(jrg): there is no way to "clean up" all unused named shmem if
257 // we restart from a crash. (That isn't a new problem, but it is a problem.)
258 // In case we want to delete it later, it may be useful to save the value
259 // of mem_filename after FilePathForMemoryName().
260 bool SharedMemory::Create(const SharedMemoryCreateOptions
& options
) {
261 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
263 tracked_objects::ScopedTracker
tracking_profile1(
264 FROM_HERE_WITH_EXPLICIT_FUNCTION(
265 "466437 SharedMemory::Create::Start"));
266 DCHECK_EQ(-1, mapped_file_
);
267 if (options
.size
== 0) return false;
269 if (options
.size
> static_cast<size_t>(std::numeric_limits
<int>::max()))
272 // This function theoretically can block on the disk, but realistically
273 // the temporary files we create will just go into the buffer cache
274 // and be deleted before they ever make it out to disk.
275 base::ThreadRestrictions::ScopedAllowIO allow_io
;
278 bool fix_size
= true;
279 ScopedFD readonly_fd
;
282 if (options
.name_deprecated
== NULL
|| options
.name_deprecated
->empty()) {
283 AutoLock
a(g_thread_lock_
.Get());
285 Time start_time
= base::Time::Now();
286 if (options
.share_read_only
|| !ShouldBatchCreateSharedMemory()) {
288 CreateAnonymousSharedMemory(options
, &fp
, &readonly_fd
, &path
);
292 #if defined(OS_MACOSX) && !defined(OS_IOS)
293 bool result
= CreateAnonymousSharedMemoryFromBatch(options
, &fp
, &path
);
298 #endif // defined(OS_MACOSX) && !defined(OS_IOS)
300 if (!options
.share_read_only
) {
301 UMA_HISTOGRAM_TIMES("SharedMemory.TimeSpentMakingAnonymousMemory",
302 Time::Now() - start_time
);
305 if (!FilePathForMemoryName(*options
.name_deprecated
, &path
))
308 // Make sure that the file is opened without any permission
309 // to other users on the system.
310 const mode_t kOwnerOnly
= S_IRUSR
| S_IWUSR
;
312 // First, try to create the file.
313 int fd
= HANDLE_EINTR(
314 open(path
.value().c_str(), O_RDWR
| O_CREAT
| O_EXCL
, kOwnerOnly
));
315 if (fd
== -1 && options
.open_existing_deprecated
) {
316 // If this doesn't work, try and open an existing file in append mode.
317 // Opening an existing file in a world writable directory has two main
318 // security implications:
319 // - Attackers could plant a file under their control, so ownership of
320 // the file is checked below.
321 // - Attackers could plant a symbolic link so that an unexpected file
322 // is opened, so O_NOFOLLOW is passed to open().
324 open(path
.value().c_str(), O_RDWR
| O_APPEND
| O_NOFOLLOW
));
326 // Check that the current user owns the file.
327 // If uid != euid, then a more complex permission model is used and this
328 // API is not appropriate.
329 const uid_t real_uid
= getuid();
330 const uid_t effective_uid
= geteuid();
333 (fstat(fd
, &sb
) != 0 || sb
.st_uid
!= real_uid
||
334 sb
.st_uid
!= effective_uid
)) {
336 "Invalid owner when opening existing shared memory file.";
341 // An existing file was opened, so its size should not be fixed.
345 if (options
.share_read_only
) {
346 // Also open as readonly so that we can ShareReadOnlyToProcess.
347 readonly_fd
.reset(HANDLE_EINTR(open(path
.value().c_str(), O_RDONLY
)));
348 if (!readonly_fd
.is_valid()) {
349 DPLOG(ERROR
) << "open(\"" << path
.value() << "\", O_RDONLY) failed";
356 // "a+" is always appropriate: if it's a new file, a+ is similar to w+.
357 fp
.reset(fdopen(fd
, "a+"));
360 if (fp
&& fix_size
) {
363 if (fstat(fileno(fp
.get()), &stat
) != 0)
365 const size_t current_size
= stat
.st_size
;
366 if (current_size
!= options
.size
) {
367 if (HANDLE_EINTR(ftruncate(fileno(fp
.get()), options
.size
)) != 0)
370 requested_size_
= options
.size
;
373 #if !defined(OS_MACOSX)
374 PLOG(ERROR
) << "Creating shared memory in " << path
.value() << " failed";
375 FilePath dir
= path
.DirName();
376 if (access(dir
.value().c_str(), W_OK
| X_OK
) < 0) {
377 PLOG(ERROR
) << "Unable to access(W_OK|X_OK) " << dir
.value();
378 if (dir
.value() == "/dev/shm") {
379 LOG(FATAL
) << "This is frequently caused by incorrect permissions on "
380 << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix.";
384 PLOG(ERROR
) << "Creating shared memory in " << path
.value() << " failed";
389 return PrepareMapFile(fp
.Pass(), readonly_fd
.Pass());
392 // Our current implementation of shmem is with mmap()ing of files.
393 // These files need to be deleted explicitly.
394 // In practice this call is only needed for unit tests.
395 bool SharedMemory::Delete(const std::string
& name
) {
397 if (!FilePathForMemoryName(name
, &path
))
400 if (PathExists(path
))
401 return base::DeleteFile(path
, false);
403 // Doesn't exist, so success.
407 bool SharedMemory::Open(const std::string
& name
, bool read_only
) {
409 if (!FilePathForMemoryName(name
, &path
))
412 read_only_
= read_only
;
414 const char *mode
= read_only
? "r" : "r+";
415 ScopedFILE
fp(base::OpenFile(path
, mode
));
416 ScopedFD
readonly_fd(HANDLE_EINTR(open(path
.value().c_str(), O_RDONLY
)));
417 if (!readonly_fd
.is_valid()) {
418 DPLOG(ERROR
) << "open(\"" << path
.value() << "\", O_RDONLY) failed";
421 return PrepareMapFile(fp
.Pass(), readonly_fd
.Pass());
423 #endif // !defined(OS_ANDROID)
425 bool SharedMemory::MapAt(off_t offset
, size_t bytes
) {
426 if (mapped_file_
== -1)
429 if (bytes
> static_cast<size_t>(std::numeric_limits
<int>::max()))
435 #if defined(OS_ANDROID)
436 // On Android, Map can be called with a size and offset of zero to use the
437 // ashmem-determined size.
439 DCHECK_EQ(0, offset
);
440 int ashmem_bytes
= ashmem_get_size_region(mapped_file_
);
441 if (ashmem_bytes
< 0)
443 bytes
= ashmem_bytes
;
447 memory_
= mmap(NULL
, bytes
, PROT_READ
| (read_only_
? 0 : PROT_WRITE
),
448 MAP_SHARED
, mapped_file_
, offset
);
450 bool mmap_succeeded
= memory_
!= (void*)-1 && memory_
!= NULL
;
451 if (mmap_succeeded
) {
452 mapped_size_
= bytes
;
453 DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_
) &
454 (SharedMemory::MAP_MINIMUM_ALIGNMENT
- 1));
459 return mmap_succeeded
;
462 bool SharedMemory::Unmap() {
466 munmap(memory_
, mapped_size_
);
472 SharedMemoryHandle
SharedMemory::handle() const {
473 return FileDescriptor(mapped_file_
, false);
476 void SharedMemory::Close() {
477 if (mapped_file_
> 0) {
478 if (close(mapped_file_
) < 0)
479 PLOG(ERROR
) << "close";
482 if (readonly_mapped_file_
> 0) {
483 if (close(readonly_mapped_file_
) < 0)
484 PLOG(ERROR
) << "close";
485 readonly_mapped_file_
= -1;
489 void SharedMemory::LockDeprecated() {
490 g_thread_lock_
.Get().Acquire();
491 LockOrUnlockCommon(F_LOCK
);
494 void SharedMemory::UnlockDeprecated() {
495 LockOrUnlockCommon(F_ULOCK
);
496 g_thread_lock_
.Get().Release();
499 #if !defined(OS_ANDROID)
500 bool SharedMemory::PrepareMapFile(ScopedFILE fp
, ScopedFD readonly_fd
) {
501 DCHECK_EQ(-1, mapped_file_
);
502 DCHECK_EQ(-1, readonly_mapped_file_
);
506 // This function theoretically can block on the disk, but realistically
507 // the temporary files we create will just go into the buffer cache
508 // and be deleted before they ever make it out to disk.
509 base::ThreadRestrictions::ScopedAllowIO allow_io
;
512 if (fstat(fileno(fp
.get()), &st
))
514 if (readonly_fd
.is_valid()) {
515 struct stat readonly_st
= {};
516 if (fstat(readonly_fd
.get(), &readonly_st
))
518 if (st
.st_dev
!= readonly_st
.st_dev
|| st
.st_ino
!= readonly_st
.st_ino
) {
519 LOG(ERROR
) << "writable and read-only inodes don't match; bailing";
524 mapped_file_
= HANDLE_EINTR(dup(fileno(fp
.get())));
525 if (mapped_file_
== -1) {
526 if (errno
== EMFILE
) {
527 LOG(WARNING
) << "Shared memory creation failed; out of file descriptors";
530 NOTREACHED() << "Call to dup failed, errno=" << errno
;
534 readonly_mapped_file_
= readonly_fd
.release();
539 // For the given shmem named |mem_name|, return a filename to mmap()
540 // (and possibly create). Modifies |filename|. Return false on
541 // error, or true of we are happy.
542 bool SharedMemory::FilePathForMemoryName(const std::string
& mem_name
,
544 // mem_name will be used for a filename; make sure it doesn't
545 // contain anything which will confuse us.
546 DCHECK_EQ(std::string::npos
, mem_name
.find('/'));
547 DCHECK_EQ(std::string::npos
, mem_name
.find('\0'));
550 if (!GetShmemTempDir(false, &temp_dir
))
553 #if !defined(OS_MACOSX)
554 #if defined(GOOGLE_CHROME_BUILD)
555 std::string name_base
= std::string("com.google.Chrome");
557 std::string name_base
= std::string("org.chromium.Chromium");
560 std::string name_base
= std::string(base::mac::BaseBundleID());
562 *path
= temp_dir
.AppendASCII(name_base
+ ".shmem." + mem_name
);
565 #endif // !defined(OS_ANDROID)
567 void SharedMemory::LockOrUnlockCommon(int function
) {
568 DCHECK_GE(mapped_file_
, 0);
569 while (lockf(mapped_file_
, function
, 0) < 0) {
570 if (errno
== EINTR
) {
572 } else if (errno
== ENOLCK
) {
573 // temporary kernel resource exaustion
574 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(500));
577 NOTREACHED() << "lockf() failed."
578 << " function:" << function
579 << " fd:" << mapped_file_
580 << " errno:" << errno
581 << " msg:" << safe_strerror(errno
);
586 bool SharedMemory::ShareToProcessCommon(ProcessHandle process
,
587 SharedMemoryHandle
* new_handle
,
589 ShareMode share_mode
) {
590 int handle_to_dup
= -1;
592 case SHARE_CURRENT_MODE
:
593 handle_to_dup
= mapped_file_
;
596 // We could imagine re-opening the file from /dev/fd, but that can't make
597 // it readonly on Mac: https://codereview.chromium.org/27265002/#msg10
598 CHECK_GE(readonly_mapped_file_
, 0);
599 handle_to_dup
= readonly_mapped_file_
;
603 const int new_fd
= HANDLE_EINTR(dup(handle_to_dup
));
605 DPLOG(ERROR
) << "dup() failed.";
609 new_handle
->fd
= new_fd
;
610 new_handle
->auto_close
= true;