Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(29)

Side by Side Diff: base/memory/shared_memory_mac.cc

Issue 1647803004: Move base to DEPS (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « base/memory/shared_memory_handle_mac.cc ('k') | base/memory/shared_memory_nacl.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 <fcntl.h>
8 #include <sys/mman.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_file.h"
14 #include "base/logging.h"
15 #include "base/posix/eintr_wrapper.h"
16 #include "base/posix/safe_strerror.h"
17 #include "base/process/process_metrics.h"
18 #include "base/profiler/scoped_tracker.h"
19 #include "base/scoped_generic.h"
20 #include "base/strings/utf_string_conversions.h"
21
22 #if defined(OS_MACOSX)
23 #include "base/mac/foundation_util.h"
24 #endif // OS_MACOSX
25
26 namespace base {
27
28 namespace {
29
30 struct ScopedPathUnlinkerTraits {
31 static FilePath* InvalidValue() { return nullptr; }
32
33 static void Free(FilePath* path) {
34 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
35 // is fixed.
36 tracked_objects::ScopedTracker tracking_profile(
37 FROM_HERE_WITH_EXPLICIT_FUNCTION(
38 "466437 SharedMemory::Create::Unlink"));
39 if (unlink(path->value().c_str()))
40 PLOG(WARNING) << "unlink";
41 }
42 };
43
44 // Unlinks the FilePath when the object is destroyed.
45 typedef ScopedGeneric<FilePath*, ScopedPathUnlinkerTraits> ScopedPathUnlinker;
46
47 // Makes a temporary file, fdopens it, and then unlinks it. |fp| is populated
48 // with the fdopened FILE. |readonly_fd| is populated with the opened fd if
49 // options.share_read_only is true. |path| is populated with the location of
50 // the file before it was unlinked.
51 // Returns false if there's an unhandled failure.
52 bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options,
53 ScopedFILE* fp,
54 ScopedFD* readonly_fd,
55 FilePath* path) {
56 // It doesn't make sense to have a open-existing private piece of shmem
57 DCHECK(!options.open_existing_deprecated);
58 // Q: Why not use the shm_open() etc. APIs?
59 // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU
60 FilePath directory;
61 ScopedPathUnlinker path_unlinker;
62 if (GetShmemTempDir(options.executable, &directory)) {
63 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
64 // is fixed.
65 tracked_objects::ScopedTracker tracking_profile(
66 FROM_HERE_WITH_EXPLICIT_FUNCTION(
67 "466437 SharedMemory::Create::OpenTemporaryFile"));
68 fp->reset(base::CreateAndOpenTemporaryFileInDir(directory, path));
69
70 // Deleting the file prevents anyone else from mapping it in (making it
71 // private), and prevents the need for cleanup (once the last fd is
72 // closed, it is truly freed).
73 if (*fp)
74 path_unlinker.reset(path);
75 }
76
77 if (*fp) {
78 if (options.share_read_only) {
79 // TODO(erikchen): Remove ScopedTracker below once
80 // http://crbug.com/466437 is fixed.
81 tracked_objects::ScopedTracker tracking_profile(
82 FROM_HERE_WITH_EXPLICIT_FUNCTION(
83 "466437 SharedMemory::Create::OpenReadonly"));
84 // Also open as readonly so that we can ShareReadOnlyToProcess.
85 readonly_fd->reset(HANDLE_EINTR(open(path->value().c_str(), O_RDONLY)));
86 if (!readonly_fd->is_valid()) {
87 DPLOG(ERROR) << "open(\"" << path->value() << "\", O_RDONLY) failed";
88 fp->reset();
89 return false;
90 }
91 }
92 }
93 return true;
94 }
95 }
96
97 SharedMemory::SharedMemory()
98 : mapped_file_(-1),
99 readonly_mapped_file_(-1),
100 mapped_size_(0),
101 memory_(NULL),
102 read_only_(false),
103 requested_size_(0) {}
104
105 SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only)
106 : mapped_file_(GetFdFromSharedMemoryHandle(handle)),
107 readonly_mapped_file_(-1),
108 mapped_size_(0),
109 memory_(NULL),
110 read_only_(read_only),
111 requested_size_(0) {}
112
113 SharedMemory::SharedMemory(const SharedMemoryHandle& handle,
114 bool read_only,
115 ProcessHandle process)
116 : mapped_file_(GetFdFromSharedMemoryHandle(handle)),
117 readonly_mapped_file_(-1),
118 mapped_size_(0),
119 memory_(NULL),
120 read_only_(read_only),
121 requested_size_(0) {
122 // We don't handle this case yet (note the ignored parameter); let's die if
123 // someone comes calling.
124 NOTREACHED();
125 }
126
127 SharedMemory::~SharedMemory() {
128 Unmap();
129 Close();
130 }
131
132 // static
133 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
134 return handle.IsValid();
135 }
136
137 // static
138 SharedMemoryHandle SharedMemory::NULLHandle() {
139 return SharedMemoryHandle();
140 }
141
142 // static
143 void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
144 DCHECK_GE(GetFdFromSharedMemoryHandle(handle), 0);
145 if (close(GetFdFromSharedMemoryHandle(handle)) < 0)
146 DPLOG(ERROR) << "close";
147 }
148
149 // static
150 size_t SharedMemory::GetHandleLimit() {
151 return base::GetMaxFds();
152 }
153
154 // static
155 SharedMemoryHandle SharedMemory::DuplicateHandle(
156 const SharedMemoryHandle& handle) {
157 return handle.Duplicate();
158 }
159
160 // static
161 int SharedMemory::GetFdFromSharedMemoryHandle(
162 const SharedMemoryHandle& handle) {
163 return handle.GetFileDescriptor().fd;
164 }
165
166 bool SharedMemory::CreateAndMapAnonymous(size_t size) {
167 return CreateAnonymous(size) && Map(size);
168 }
169
170 // static
171 int SharedMemory::GetSizeFromSharedMemoryHandle(
172 const SharedMemoryHandle& handle) {
173 struct stat st;
174 if (fstat(GetFdFromSharedMemoryHandle(handle), &st) != 0)
175 return -1;
176 return st.st_size;
177 }
178
179 // Chromium mostly only uses the unique/private shmem as specified by
180 // "name == L"". The exception is in the StatsTable.
181 // TODO(jrg): there is no way to "clean up" all unused named shmem if
182 // we restart from a crash. (That isn't a new problem, but it is a problem.)
183 // In case we want to delete it later, it may be useful to save the value
184 // of mem_filename after FilePathForMemoryName().
185 bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
186 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437
187 // is fixed.
188 tracked_objects::ScopedTracker tracking_profile1(
189 FROM_HERE_WITH_EXPLICIT_FUNCTION("466437 SharedMemory::Create::Start"));
190 DCHECK_EQ(-1, mapped_file_);
191 if (options.size == 0)
192 return false;
193
194 if (options.size > static_cast<size_t>(std::numeric_limits<int>::max()))
195 return false;
196
197 // This function theoretically can block on the disk, but realistically
198 // the temporary files we create will just go into the buffer cache
199 // and be deleted before they ever make it out to disk.
200 base::ThreadRestrictions::ScopedAllowIO allow_io;
201
202 ScopedFILE fp;
203 bool fix_size = true;
204 ScopedFD readonly_fd;
205
206 FilePath path;
207 if (options.name_deprecated == NULL || options.name_deprecated->empty()) {
208 bool result =
209 CreateAnonymousSharedMemory(options, &fp, &readonly_fd, &path);
210 if (!result)
211 return false;
212 } else {
213 if (!FilePathForMemoryName(*options.name_deprecated, &path))
214 return false;
215
216 // Make sure that the file is opened without any permission
217 // to other users on the system.
218 const mode_t kOwnerOnly = S_IRUSR | S_IWUSR;
219
220 // First, try to create the file.
221 int fd = HANDLE_EINTR(
222 open(path.value().c_str(), O_RDWR | O_CREAT | O_EXCL, kOwnerOnly));
223 if (fd == -1 && options.open_existing_deprecated) {
224 // If this doesn't work, try and open an existing file in append mode.
225 // Opening an existing file in a world writable directory has two main
226 // security implications:
227 // - Attackers could plant a file under their control, so ownership of
228 // the file is checked below.
229 // - Attackers could plant a symbolic link so that an unexpected file
230 // is opened, so O_NOFOLLOW is passed to open().
231 fd = HANDLE_EINTR(
232 open(path.value().c_str(), O_RDWR | O_APPEND | O_NOFOLLOW));
233
234 // Check that the current user owns the file.
235 // If uid != euid, then a more complex permission model is used and this
236 // API is not appropriate.
237 const uid_t real_uid = getuid();
238 const uid_t effective_uid = geteuid();
239 struct stat sb;
240 if (fd >= 0 && (fstat(fd, &sb) != 0 || sb.st_uid != real_uid ||
241 sb.st_uid != effective_uid)) {
242 LOG(ERROR) << "Invalid owner when opening existing shared memory file.";
243 close(fd);
244 return false;
245 }
246
247 // An existing file was opened, so its size should not be fixed.
248 fix_size = false;
249 }
250
251 if (options.share_read_only) {
252 // Also open as readonly so that we can ShareReadOnlyToProcess.
253 readonly_fd.reset(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)));
254 if (!readonly_fd.is_valid()) {
255 DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
256 close(fd);
257 fd = -1;
258 return false;
259 }
260 }
261 if (fd >= 0) {
262 // "a+" is always appropriate: if it's a new file, a+ is similar to w+.
263 fp.reset(fdopen(fd, "a+"));
264 }
265 }
266 if (fp && fix_size) {
267 // Get current size.
268 struct stat stat;
269 if (fstat(fileno(fp.get()), &stat) != 0)
270 return false;
271 const size_t current_size = stat.st_size;
272 if (current_size != options.size) {
273 if (HANDLE_EINTR(ftruncate(fileno(fp.get()), options.size)) != 0)
274 return false;
275 }
276 requested_size_ = options.size;
277 }
278 if (fp == NULL) {
279 PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
280 return false;
281 }
282
283 return PrepareMapFile(fp.Pass(), readonly_fd.Pass());
284 }
285
286 // Our current implementation of shmem is with mmap()ing of files.
287 // These files need to be deleted explicitly.
288 // In practice this call is only needed for unit tests.
289 bool SharedMemory::Delete(const std::string& name) {
290 FilePath path;
291 if (!FilePathForMemoryName(name, &path))
292 return false;
293
294 if (PathExists(path))
295 return base::DeleteFile(path, false);
296
297 // Doesn't exist, so success.
298 return true;
299 }
300
301 bool SharedMemory::Open(const std::string& name, bool read_only) {
302 FilePath path;
303 if (!FilePathForMemoryName(name, &path))
304 return false;
305
306 read_only_ = read_only;
307
308 const char* mode = read_only ? "r" : "r+";
309 ScopedFILE fp(base::OpenFile(path, mode));
310 ScopedFD readonly_fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY)));
311 if (!readonly_fd.is_valid()) {
312 DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed";
313 return false;
314 }
315 return PrepareMapFile(fp.Pass(), readonly_fd.Pass());
316 }
317
318 bool SharedMemory::MapAt(off_t offset, size_t bytes) {
319 if (mapped_file_ == -1)
320 return false;
321
322 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
323 return false;
324
325 if (memory_)
326 return false;
327
328 memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
329 MAP_SHARED, mapped_file_, offset);
330
331 bool mmap_succeeded = memory_ != (void*)-1 && memory_ != NULL;
332 if (mmap_succeeded) {
333 mapped_size_ = bytes;
334 DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
335 (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
336 } else {
337 memory_ = NULL;
338 }
339
340 return mmap_succeeded;
341 }
342
343 bool SharedMemory::Unmap() {
344 if (memory_ == NULL)
345 return false;
346
347 munmap(memory_, mapped_size_);
348 memory_ = NULL;
349 mapped_size_ = 0;
350 return true;
351 }
352
353 SharedMemoryHandle SharedMemory::handle() const {
354 return SharedMemoryHandle(mapped_file_, false);
355 }
356
357 void SharedMemory::Close() {
358 if (mapped_file_ > 0) {
359 if (close(mapped_file_) < 0)
360 PLOG(ERROR) << "close";
361 mapped_file_ = -1;
362 }
363 if (readonly_mapped_file_ > 0) {
364 if (close(readonly_mapped_file_) < 0)
365 PLOG(ERROR) << "close";
366 readonly_mapped_file_ = -1;
367 }
368 }
369
370 bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) {
371 DCHECK_EQ(-1, mapped_file_);
372 DCHECK_EQ(-1, readonly_mapped_file_);
373 if (fp == NULL)
374 return false;
375
376 // This function theoretically can block on the disk, but realistically
377 // the temporary files we create will just go into the buffer cache
378 // and be deleted before they ever make it out to disk.
379 base::ThreadRestrictions::ScopedAllowIO allow_io;
380
381 struct stat st = {};
382 if (fstat(fileno(fp.get()), &st))
383 NOTREACHED();
384 if (readonly_fd.is_valid()) {
385 struct stat readonly_st = {};
386 if (fstat(readonly_fd.get(), &readonly_st))
387 NOTREACHED();
388 if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) {
389 LOG(ERROR) << "writable and read-only inodes don't match; bailing";
390 return false;
391 }
392 }
393
394 mapped_file_ = HANDLE_EINTR(dup(fileno(fp.get())));
395 if (mapped_file_ == -1) {
396 if (errno == EMFILE) {
397 LOG(WARNING) << "Shared memory creation failed; out of file descriptors";
398 return false;
399 } else {
400 NOTREACHED() << "Call to dup failed, errno=" << errno;
401 }
402 }
403 readonly_mapped_file_ = readonly_fd.release();
404
405 return true;
406 }
407
408 // For the given shmem named |mem_name|, return a filename to mmap()
409 // (and possibly create). Modifies |filename|. Return false on
410 // error, or true of we are happy.
411 bool SharedMemory::FilePathForMemoryName(const std::string& mem_name,
412 FilePath* path) {
413 // mem_name will be used for a filename; make sure it doesn't
414 // contain anything which will confuse us.
415 DCHECK_EQ(std::string::npos, mem_name.find('/'));
416 DCHECK_EQ(std::string::npos, mem_name.find('\0'));
417
418 FilePath temp_dir;
419 if (!GetShmemTempDir(false, &temp_dir))
420 return false;
421
422 std::string name_base = std::string(base::mac::BaseBundleID());
423 *path = temp_dir.AppendASCII(name_base + ".shmem." + mem_name);
424 return true;
425 }
426
427 bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
428 SharedMemoryHandle* new_handle,
429 bool close_self,
430 ShareMode share_mode) {
431 int handle_to_dup = -1;
432 switch (share_mode) {
433 case SHARE_CURRENT_MODE:
434 handle_to_dup = mapped_file_;
435 break;
436 case SHARE_READONLY:
437 // We could imagine re-opening the file from /dev/fd, but that can't make
438 // it readonly on Mac: https://codereview.chromium.org/27265002/#msg10
439 CHECK_GE(readonly_mapped_file_, 0);
440 handle_to_dup = readonly_mapped_file_;
441 break;
442 }
443
444 const int new_fd = HANDLE_EINTR(dup(handle_to_dup));
445 if (new_fd < 0) {
446 DPLOG(ERROR) << "dup() failed.";
447 return false;
448 }
449
450 new_handle->SetFileHandle(new_fd, true);
451
452 if (close_self) {
453 Unmap();
454 Close();
455 }
456
457 return true;
458 }
459
460 } // namespace base
OLDNEW
« no previous file with comments | « base/memory/shared_memory_handle_mac.cc ('k') | base/memory/shared_memory_nacl.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698