1 // Copyright 2016 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_helper.h"
6
7 #if defined(OS_CHROMEOS)
8 #include <sys/resource.h>
9 #include <sys/time.h>
10
11 #include "base/debug/alias.h"
12 #endif // defined(OS_CHROMEOS)
13
14 #include "base/threading/thread_restrictions.h"
15
16 namespace base {
17
18 struct ScopedPathUnlinkerTraits {
InvalidValuebase::ScopedPathUnlinkerTraits19 static const FilePath* InvalidValue() { return nullptr; }
20
Freebase::ScopedPathUnlinkerTraits21 static void Free(const FilePath* path) {
22 if (unlink(path->value().c_str()))
23 PLOG(WARNING) << "unlink";
24 }
25 };
26
27 // Unlinks the FilePath when the object is destroyed.
28 using ScopedPathUnlinker =
29 ScopedGeneric<const FilePath*, ScopedPathUnlinkerTraits>;
30
31 #if !defined(OS_ANDROID)
CreateAnonymousSharedMemory(const SharedMemoryCreateOptions & options,ScopedFD * fd,ScopedFD * readonly_fd,FilePath * path)32 bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options,
33 ScopedFD* fd,
34 ScopedFD* readonly_fd,
35 FilePath* path) {
36 #if defined(OS_LINUX)
37 // It doesn't make sense to have a open-existing private piece of shmem
38 DCHECK(!options.open_existing_deprecated);
39 #endif // defined(OS_LINUX)
40 // Q: Why not use the shm_open() etc. APIs?
41 // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU
42 FilePath directory;
43 ScopedPathUnlinker path_unlinker;
44 if (!GetShmemTempDir(options.executable, &directory))
45 return false;
46
47 fd->reset(base::CreateAndOpenFdForTemporaryFileInDir(directory, path));
48
49 if (!fd->is_valid())
50 return false;
51
52 // Deleting the file prevents anyone else from mapping it in (making it
53 // private), and prevents the need for cleanup (once the last fd is
54 // closed, it is truly freed).
55 path_unlinker.reset(path);
56
57 if (options.share_read_only) {
58 // Also open as readonly so that we can GetReadOnlyHandle.
59 readonly_fd->reset(HANDLE_EINTR(open(path->value().c_str(), O_RDONLY)));
60 if (!readonly_fd->is_valid()) {
61 DPLOG(ERROR) << "open(\"" << path->value() << "\", O_RDONLY) failed";
62 fd->reset();
63 return false;
64 }
65 }
66 return true;
67 }
68
PrepareMapFile(ScopedFD fd,ScopedFD readonly_fd,int * mapped_file,int * readonly_mapped_file)69 bool PrepareMapFile(ScopedFD fd,
70 ScopedFD readonly_fd,
71 int* mapped_file,
72 int* readonly_mapped_file) {
73 DCHECK_EQ(-1, *mapped_file);
74 DCHECK_EQ(-1, *readonly_mapped_file);
75 if (!fd.is_valid())
76 return false;
77
78 // This function theoretically can block on the disk, but realistically
79 // the temporary files we create will just go into the buffer cache
80 // and be deleted before they ever make it out to disk.
81 base::ThreadRestrictions::ScopedAllowIO allow_io;
82
83 if (readonly_fd.is_valid()) {
84 struct stat st = {};
85 if (fstat(fd.get(), &st))
86 NOTREACHED();
87
88 struct stat readonly_st = {};
89 if (fstat(readonly_fd.get(), &readonly_st))
90 NOTREACHED();
91 if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) {
92 LOG(ERROR) << "writable and read-only inodes don't match; bailing";
93 return false;
94 }
95 }
96
97 *mapped_file = HANDLE_EINTR(dup(fd.get()));
98 if (*mapped_file == -1) {
99 NOTREACHED() << "Call to dup failed, errno=" << errno;
100
101 #if defined(OS_CHROMEOS)
102 if (errno == EMFILE) {
103 // We're out of file descriptors and are probably about to crash somewhere
104 // else in Chrome anyway. Let's collect what FD information we can and
105 // crash.
106 // Added for debugging crbug.com/733718
107 int original_fd_limit = 16384;
108 struct rlimit rlim;
109 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) {
110 original_fd_limit = rlim.rlim_cur;
111 if (rlim.rlim_max > rlim.rlim_cur) {
112 // Increase fd limit so breakpad has a chance to write a minidump.
113 rlim.rlim_cur = rlim.rlim_max;
114 if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
115 PLOG(ERROR) << "setrlimit() failed";
116 }
117 }
118 } else {
119 PLOG(ERROR) << "getrlimit() failed";
120 }
121
122 const char kFileDataMarker[] = "FDATA";
123 char buf[PATH_MAX];
124 char fd_path[PATH_MAX];
125 char crash_buffer[32 * 1024] = {0};
126 char* crash_ptr = crash_buffer;
127 base::debug::Alias(crash_buffer);
128
129 // Put a marker at the start of our data so we can confirm where it
130 // begins.
131 crash_ptr = strncpy(crash_ptr, kFileDataMarker, strlen(kFileDataMarker));
132 for (int i = original_fd_limit; i >= 0; --i) {
133 memset(buf, 0, arraysize(buf));
134 memset(fd_path, 0, arraysize(fd_path));
135 snprintf(fd_path, arraysize(fd_path) - 1, "/proc/self/fd/%d", i);
136 ssize_t count = readlink(fd_path, buf, arraysize(buf) - 1);
137 if (count < 0) {
138 PLOG(ERROR) << "readlink failed for: " << fd_path;
139 continue;
140 }
141
142 if (crash_ptr + count + 1 < crash_buffer + arraysize(crash_buffer)) {
143 crash_ptr = strncpy(crash_ptr, buf, count + 1);
144 }
145 LOG(ERROR) << i << ": " << buf;
146 }
147 LOG(FATAL) << "Logged for file descriptor exhaustion, crashing now";
148 }
149 #endif // defined(OS_CHROMEOS)
150 }
151 *readonly_mapped_file = readonly_fd.release();
152
153 return true;
154 }
155 #endif // !defined(OS_ANDROID)
156
157 } // namespace base
158