|  | // Copyright 2016 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "base/memory/shared_memory_helper.h" | 
|  |  | 
|  | #if defined(OS_CHROMEOS) | 
|  | #include <sys/resource.h> | 
|  | #include <sys/time.h> | 
|  |  | 
|  | #include "base/debug/alias.h" | 
|  | #endif  // defined(OS_CHROMEOS) | 
|  |  | 
|  | #include "base/threading/thread_restrictions.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | struct ScopedPathUnlinkerTraits { | 
|  | static const FilePath* InvalidValue() { return nullptr; } | 
|  |  | 
|  | static void Free(const FilePath* path) { | 
|  | if (unlink(path->value().c_str())) | 
|  | PLOG(WARNING) << "unlink"; | 
|  | } | 
|  | }; | 
|  |  | 
|  | // Unlinks the FilePath when the object is destroyed. | 
|  | using ScopedPathUnlinker = | 
|  | ScopedGeneric<const FilePath*, ScopedPathUnlinkerTraits>; | 
|  |  | 
|  | #if !defined(OS_ANDROID) | 
|  | bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options, | 
|  | ScopedFD* fd, | 
|  | ScopedFD* readonly_fd, | 
|  | FilePath* path) { | 
|  | #if defined(OS_LINUX) | 
|  | // It doesn't make sense to have a open-existing private piece of shmem | 
|  | DCHECK(!options.open_existing_deprecated); | 
|  | #endif  // defined(OS_LINUX) | 
|  | // Q: Why not use the shm_open() etc. APIs? | 
|  | // A: Because they're limited to 4mb on OS X.  FFFFFFFUUUUUUUUUUU | 
|  | FilePath directory; | 
|  | ScopedPathUnlinker path_unlinker; | 
|  | if (!GetShmemTempDir(options.executable, &directory)) | 
|  | return false; | 
|  |  | 
|  | fd->reset(base::CreateAndOpenFdForTemporaryFileInDir(directory, path)); | 
|  |  | 
|  | if (!fd->is_valid()) | 
|  | return false; | 
|  |  | 
|  | // Deleting the file prevents anyone else from mapping it in (making it | 
|  | // private), and prevents the need for cleanup (once the last fd is | 
|  | // closed, it is truly freed). | 
|  | path_unlinker.reset(path); | 
|  |  | 
|  | if (options.share_read_only) { | 
|  | // Also open as readonly so that we can GetReadOnlyHandle. | 
|  | readonly_fd->reset(HANDLE_EINTR(open(path->value().c_str(), O_RDONLY))); | 
|  | if (!readonly_fd->is_valid()) { | 
|  | DPLOG(ERROR) << "open(\"" << path->value() << "\", O_RDONLY) failed"; | 
|  | fd->reset(); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool PrepareMapFile(ScopedFD fd, | 
|  | ScopedFD readonly_fd, | 
|  | int* mapped_file, | 
|  | int* readonly_mapped_file) { | 
|  | DCHECK_EQ(-1, *mapped_file); | 
|  | DCHECK_EQ(-1, *readonly_mapped_file); | 
|  | if (!fd.is_valid()) | 
|  | return false; | 
|  |  | 
|  | // This function theoretically can block on the disk, but realistically | 
|  | // the temporary files we create will just go into the buffer cache | 
|  | // and be deleted before they ever make it out to disk. | 
|  | base::ThreadRestrictions::ScopedAllowIO allow_io; | 
|  |  | 
|  | if (readonly_fd.is_valid()) { | 
|  | struct stat st = {}; | 
|  | if (fstat(fd.get(), &st)) | 
|  | NOTREACHED(); | 
|  |  | 
|  | struct stat readonly_st = {}; | 
|  | if (fstat(readonly_fd.get(), &readonly_st)) | 
|  | NOTREACHED(); | 
|  | if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) { | 
|  | LOG(ERROR) << "writable and read-only inodes don't match; bailing"; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | *mapped_file = HANDLE_EINTR(dup(fd.get())); | 
|  | if (*mapped_file == -1) { | 
|  | NOTREACHED() << "Call to dup failed, errno=" << errno; | 
|  |  | 
|  | #if defined(OS_CHROMEOS) | 
|  | if (errno == EMFILE) { | 
|  | // We're out of file descriptors and are probably about to crash somewhere | 
|  | // else in Chrome anyway. Let's collect what FD information we can and | 
|  | // crash. | 
|  | // Added for debugging crbug.com/733718 | 
|  | int original_fd_limit = 16384; | 
|  | struct rlimit rlim; | 
|  | if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { | 
|  | original_fd_limit = rlim.rlim_cur; | 
|  | if (rlim.rlim_max > rlim.rlim_cur) { | 
|  | // Increase fd limit so breakpad has a chance to write a minidump. | 
|  | rlim.rlim_cur = rlim.rlim_max; | 
|  | if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) { | 
|  | PLOG(ERROR) << "setrlimit() failed"; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | PLOG(ERROR) << "getrlimit() failed"; | 
|  | } | 
|  |  | 
|  | const char kFileDataMarker[] = "FDATA"; | 
|  | char buf[PATH_MAX]; | 
|  | char fd_path[PATH_MAX]; | 
|  | char crash_buffer[32 * 1024] = {0}; | 
|  | char* crash_ptr = crash_buffer; | 
|  | base::debug::Alias(crash_buffer); | 
|  |  | 
|  | // Put a marker at the start of our data so we can confirm where it | 
|  | // begins. | 
|  | crash_ptr = strncpy(crash_ptr, kFileDataMarker, strlen(kFileDataMarker)); | 
|  | for (int i = original_fd_limit; i >= 0; --i) { | 
|  | memset(buf, 0, arraysize(buf)); | 
|  | memset(fd_path, 0, arraysize(fd_path)); | 
|  | snprintf(fd_path, arraysize(fd_path) - 1, "/proc/self/fd/%d", i); | 
|  | ssize_t count = readlink(fd_path, buf, arraysize(buf) - 1); | 
|  | if (count < 0) { | 
|  | PLOG(ERROR) << "readlink failed for: " << fd_path; | 
|  | continue; | 
|  | } | 
|  |  | 
|  | if (crash_ptr + count + 1 < crash_buffer + arraysize(crash_buffer)) { | 
|  | crash_ptr = strncpy(crash_ptr, buf, count + 1); | 
|  | } | 
|  | LOG(ERROR) << i << ": " << buf; | 
|  | } | 
|  | LOG(FATAL) << "Logged for file descriptor exhaustion, crashing now"; | 
|  | } | 
|  | #endif  // defined(OS_CHROMEOS) | 
|  | } | 
|  | *readonly_mapped_file = readonly_fd.release(); | 
|  |  | 
|  | return true; | 
|  | } | 
|  | #endif  // !defined(OS_ANDROID) | 
|  |  | 
|  | }  // namespace base |