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