| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/memory/shared_memory.h" | 5 #include "base/memory/shared_memory.h" |
| 6 | 6 |
| 7 #include <errno.h> | 7 #include <errno.h> |
| 8 #include <fcntl.h> | 8 #include <fcntl.h> |
| 9 #include <stddef.h> | 9 #include <stddef.h> |
| 10 #include <sys/mman.h> | 10 #include <sys/mman.h> |
| 11 #include <sys/stat.h> | 11 #include <sys/stat.h> |
| 12 #include <unistd.h> | 12 #include <unistd.h> |
| 13 | 13 |
| 14 #include "base/files/file_util.h" | 14 #include "base/files/file_util.h" |
| 15 #include "base/files/scoped_file.h" | 15 #include "base/files/scoped_file.h" |
| 16 #include "base/logging.h" | 16 #include "base/logging.h" |
| 17 #include "base/memory/shared_memory_helper.h" |
| 17 #include "base/posix/eintr_wrapper.h" | 18 #include "base/posix/eintr_wrapper.h" |
| 18 #include "base/posix/safe_strerror.h" | 19 #include "base/posix/safe_strerror.h" |
| 19 #include "base/process/process_metrics.h" | 20 #include "base/process/process_metrics.h" |
| 20 #include "base/profiler/scoped_tracker.h" | |
| 21 #include "base/scoped_generic.h" | 21 #include "base/scoped_generic.h" |
| 22 #include "base/strings/utf_string_conversions.h" | 22 #include "base/strings/utf_string_conversions.h" |
| 23 #include "base/threading/thread_restrictions.h" |
| 23 #include "build/build_config.h" | 24 #include "build/build_config.h" |
| 24 | 25 |
| 25 #if defined(OS_ANDROID) | 26 #if defined(OS_ANDROID) |
| 26 #include "base/os_compat_android.h" | 27 #include "base/os_compat_android.h" |
| 27 #include "third_party/ashmem/ashmem.h" | 28 #include "third_party/ashmem/ashmem.h" |
| 28 #endif | 29 #endif |
| 29 | 30 |
| 30 namespace base { | 31 namespace base { |
| 31 | 32 |
| 32 namespace { | |
| 33 | |
| 34 struct ScopedPathUnlinkerTraits { | |
| 35 static FilePath* InvalidValue() { return nullptr; } | |
| 36 | |
| 37 static void Free(FilePath* path) { | |
| 38 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437 | |
| 39 // is fixed. | |
| 40 tracked_objects::ScopedTracker tracking_profile( | |
| 41 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 42 "466437 SharedMemory::Create::Unlink")); | |
| 43 if (unlink(path->value().c_str())) | |
| 44 PLOG(WARNING) << "unlink"; | |
| 45 } | |
| 46 }; | |
| 47 | |
| 48 // Unlinks the FilePath when the object is destroyed. | |
| 49 typedef ScopedGeneric<FilePath*, ScopedPathUnlinkerTraits> ScopedPathUnlinker; | |
| 50 | |
| 51 #if !defined(OS_ANDROID) | |
| 52 // Makes a temporary file, fdopens it, and then unlinks it. |fp| is populated | |
| 53 // with the fdopened FILE. |readonly_fd| is populated with the opened fd if | |
| 54 // options.share_read_only is true. |path| is populated with the location of | |
| 55 // the file before it was unlinked. | |
| 56 // Returns false if there's an unhandled failure. | |
| 57 bool CreateAnonymousSharedMemory(const SharedMemoryCreateOptions& options, | |
| 58 ScopedFILE* fp, | |
| 59 ScopedFD* readonly_fd, | |
| 60 FilePath* path) { | |
| 61 // It doesn't make sense to have a open-existing private piece of shmem | |
| 62 DCHECK(!options.open_existing_deprecated); | |
| 63 // Q: Why not use the shm_open() etc. APIs? | |
| 64 // A: Because they're limited to 4mb on OS X. FFFFFFFUUUUUUUUUUU | |
| 65 FilePath directory; | |
| 66 ScopedPathUnlinker path_unlinker; | |
| 67 if (GetShmemTempDir(options.executable, &directory)) { | |
| 68 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437 | |
| 69 // is fixed. | |
| 70 tracked_objects::ScopedTracker tracking_profile( | |
| 71 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 72 "466437 SharedMemory::Create::OpenTemporaryFile")); | |
| 73 fp->reset(base::CreateAndOpenTemporaryFileInDir(directory, path)); | |
| 74 | |
| 75 // Deleting the file prevents anyone else from mapping it in (making it | |
| 76 // private), and prevents the need for cleanup (once the last fd is | |
| 77 // closed, it is truly freed). | |
| 78 if (*fp) | |
| 79 path_unlinker.reset(path); | |
| 80 } | |
| 81 | |
| 82 if (*fp) { | |
| 83 if (options.share_read_only) { | |
| 84 // TODO(erikchen): Remove ScopedTracker below once | |
| 85 // http://crbug.com/466437 is fixed. | |
| 86 tracked_objects::ScopedTracker tracking_profile( | |
| 87 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 88 "466437 SharedMemory::Create::OpenReadonly")); | |
| 89 // Also open as readonly so that we can ShareReadOnlyToProcess. | |
| 90 readonly_fd->reset(HANDLE_EINTR(open(path->value().c_str(), O_RDONLY))); | |
| 91 if (!readonly_fd->is_valid()) { | |
| 92 DPLOG(ERROR) << "open(\"" << path->value() << "\", O_RDONLY) failed"; | |
| 93 fp->reset(); | |
| 94 return false; | |
| 95 } | |
| 96 } | |
| 97 } | |
| 98 return true; | |
| 99 } | |
| 100 #endif // !defined(OS_ANDROID) | |
| 101 } | |
| 102 | |
| 103 SharedMemory::SharedMemory() | 33 SharedMemory::SharedMemory() |
| 104 : mapped_file_(-1), | 34 : mapped_file_(-1), |
| 105 readonly_mapped_file_(-1), | 35 readonly_mapped_file_(-1), |
| 106 mapped_size_(0), | 36 mapped_size_(0), |
| 107 memory_(NULL), | 37 memory_(NULL), |
| 108 read_only_(false), | 38 read_only_(false), |
| 109 requested_size_(0) { | 39 requested_size_(0) { |
| 110 } | 40 } |
| 111 | 41 |
| 112 SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only) | 42 SharedMemory::SharedMemory(const SharedMemoryHandle& handle, bool read_only) |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 return true; | 108 return true; |
| 179 } | 109 } |
| 180 | 110 |
| 181 // Chromium mostly only uses the unique/private shmem as specified by | 111 // Chromium mostly only uses the unique/private shmem as specified by |
| 182 // "name == L"". The exception is in the StatsTable. | 112 // "name == L"". The exception is in the StatsTable. |
| 183 // TODO(jrg): there is no way to "clean up" all unused named shmem if | 113 // TODO(jrg): there is no way to "clean up" all unused named shmem if |
| 184 // we restart from a crash. (That isn't a new problem, but it is a problem.) | 114 // we restart from a crash. (That isn't a new problem, but it is a problem.) |
| 185 // In case we want to delete it later, it may be useful to save the value | 115 // In case we want to delete it later, it may be useful to save the value |
| 186 // of mem_filename after FilePathForMemoryName(). | 116 // of mem_filename after FilePathForMemoryName(). |
| 187 bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { | 117 bool SharedMemory::Create(const SharedMemoryCreateOptions& options) { |
| 188 // TODO(erikchen): Remove ScopedTracker below once http://crbug.com/466437 | |
| 189 // is fixed. | |
| 190 tracked_objects::ScopedTracker tracking_profile1( | |
| 191 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
| 192 "466437 SharedMemory::Create::Start")); | |
| 193 DCHECK_EQ(-1, mapped_file_); | 118 DCHECK_EQ(-1, mapped_file_); |
| 194 if (options.size == 0) return false; | 119 if (options.size == 0) return false; |
| 195 | 120 |
| 196 if (options.size > static_cast<size_t>(std::numeric_limits<int>::max())) | 121 if (options.size > static_cast<size_t>(std::numeric_limits<int>::max())) |
| 197 return false; | 122 return false; |
| 198 | 123 |
| 199 // This function theoretically can block on the disk, but realistically | 124 // This function theoretically can block on the disk, but realistically |
| 200 // the temporary files we create will just go into the buffer cache | 125 // the temporary files we create will just go into the buffer cache |
| 201 // and be deleted before they ever make it out to disk. | 126 // and be deleted before they ever make it out to disk. |
| 202 base::ThreadRestrictions::ScopedAllowIO allow_io; | 127 base::ThreadRestrictions::ScopedAllowIO allow_io; |
| (...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 285 if (access(dir.value().c_str(), W_OK | X_OK) < 0) { | 210 if (access(dir.value().c_str(), W_OK | X_OK) < 0) { |
| 286 PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value(); | 211 PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value(); |
| 287 if (dir.value() == "/dev/shm") { | 212 if (dir.value() == "/dev/shm") { |
| 288 LOG(FATAL) << "This is frequently caused by incorrect permissions on " | 213 LOG(FATAL) << "This is frequently caused by incorrect permissions on " |
| 289 << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix."; | 214 << "/dev/shm. Try 'sudo chmod 1777 /dev/shm' to fix."; |
| 290 } | 215 } |
| 291 } | 216 } |
| 292 return false; | 217 return false; |
| 293 } | 218 } |
| 294 | 219 |
| 295 return PrepareMapFile(std::move(fp), std::move(readonly_fd)); | 220 return PrepareMapFile(std::move(fp), std::move(readonly_fd), &mapped_file_, |
| 221 &readonly_mapped_file_); |
| 296 } | 222 } |
| 297 | 223 |
| 298 // Our current implementation of shmem is with mmap()ing of files. | 224 // Our current implementation of shmem is with mmap()ing of files. |
| 299 // These files need to be deleted explicitly. | 225 // These files need to be deleted explicitly. |
| 300 // In practice this call is only needed for unit tests. | 226 // In practice this call is only needed for unit tests. |
| 301 bool SharedMemory::Delete(const std::string& name) { | 227 bool SharedMemory::Delete(const std::string& name) { |
| 302 FilePath path; | 228 FilePath path; |
| 303 if (!FilePathForMemoryName(name, &path)) | 229 if (!FilePathForMemoryName(name, &path)) |
| 304 return false; | 230 return false; |
| 305 | 231 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 317 | 243 |
| 318 read_only_ = read_only; | 244 read_only_ = read_only; |
| 319 | 245 |
| 320 const char *mode = read_only ? "r" : "r+"; | 246 const char *mode = read_only ? "r" : "r+"; |
| 321 ScopedFILE fp(base::OpenFile(path, mode)); | 247 ScopedFILE fp(base::OpenFile(path, mode)); |
| 322 ScopedFD readonly_fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); | 248 ScopedFD readonly_fd(HANDLE_EINTR(open(path.value().c_str(), O_RDONLY))); |
| 323 if (!readonly_fd.is_valid()) { | 249 if (!readonly_fd.is_valid()) { |
| 324 DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; | 250 DPLOG(ERROR) << "open(\"" << path.value() << "\", O_RDONLY) failed"; |
| 325 return false; | 251 return false; |
| 326 } | 252 } |
| 327 return PrepareMapFile(std::move(fp), std::move(readonly_fd)); | 253 return PrepareMapFile(std::move(fp), std::move(readonly_fd), &mapped_file_, |
| 254 &readonly_mapped_file_); |
| 328 } | 255 } |
| 329 #endif // !defined(OS_ANDROID) | 256 #endif // !defined(OS_ANDROID) |
| 330 | 257 |
| 331 bool SharedMemory::MapAt(off_t offset, size_t bytes) { | 258 bool SharedMemory::MapAt(off_t offset, size_t bytes) { |
| 332 if (mapped_file_ == -1) | 259 if (mapped_file_ == -1) |
| 333 return false; | 260 return false; |
| 334 | 261 |
| 335 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) | 262 if (bytes > static_cast<size_t>(std::numeric_limits<int>::max())) |
| 336 return false; | 263 return false; |
| 337 | 264 |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 mapped_file_ = -1; | 321 mapped_file_ = -1; |
| 395 } | 322 } |
| 396 if (readonly_mapped_file_ > 0) { | 323 if (readonly_mapped_file_ > 0) { |
| 397 if (IGNORE_EINTR(close(readonly_mapped_file_)) < 0) | 324 if (IGNORE_EINTR(close(readonly_mapped_file_)) < 0) |
| 398 PLOG(ERROR) << "close"; | 325 PLOG(ERROR) << "close"; |
| 399 readonly_mapped_file_ = -1; | 326 readonly_mapped_file_ = -1; |
| 400 } | 327 } |
| 401 } | 328 } |
| 402 | 329 |
| 403 #if !defined(OS_ANDROID) | 330 #if !defined(OS_ANDROID) |
| 404 bool SharedMemory::PrepareMapFile(ScopedFILE fp, ScopedFD readonly_fd) { | |
| 405 DCHECK_EQ(-1, mapped_file_); | |
| 406 DCHECK_EQ(-1, readonly_mapped_file_); | |
| 407 if (fp == NULL) | |
| 408 return false; | |
| 409 | |
| 410 // This function theoretically can block on the disk, but realistically | |
| 411 // the temporary files we create will just go into the buffer cache | |
| 412 // and be deleted before they ever make it out to disk. | |
| 413 base::ThreadRestrictions::ScopedAllowIO allow_io; | |
| 414 | |
| 415 struct stat st = {}; | |
| 416 if (fstat(fileno(fp.get()), &st)) | |
| 417 NOTREACHED(); | |
| 418 if (readonly_fd.is_valid()) { | |
| 419 struct stat readonly_st = {}; | |
| 420 if (fstat(readonly_fd.get(), &readonly_st)) | |
| 421 NOTREACHED(); | |
| 422 if (st.st_dev != readonly_st.st_dev || st.st_ino != readonly_st.st_ino) { | |
| 423 LOG(ERROR) << "writable and read-only inodes don't match; bailing"; | |
| 424 return false; | |
| 425 } | |
| 426 } | |
| 427 | |
| 428 mapped_file_ = HANDLE_EINTR(dup(fileno(fp.get()))); | |
| 429 if (mapped_file_ == -1) { | |
| 430 if (errno == EMFILE) { | |
| 431 LOG(WARNING) << "Shared memory creation failed; out of file descriptors"; | |
| 432 return false; | |
| 433 } else { | |
| 434 NOTREACHED() << "Call to dup failed, errno=" << errno; | |
| 435 } | |
| 436 } | |
| 437 readonly_mapped_file_ = readonly_fd.release(); | |
| 438 | |
| 439 return true; | |
| 440 } | |
| 441 | |
| 442 // For the given shmem named |mem_name|, return a filename to mmap() | 331 // For the given shmem named |mem_name|, return a filename to mmap() |
| 443 // (and possibly create). Modifies |filename|. Return false on | 332 // (and possibly create). Modifies |filename|. Return false on |
| 444 // error, or true of we are happy. | 333 // error, or true of we are happy. |
| 445 bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, | 334 bool SharedMemory::FilePathForMemoryName(const std::string& mem_name, |
| 446 FilePath* path) { | 335 FilePath* path) { |
| 447 // mem_name will be used for a filename; make sure it doesn't | 336 // mem_name will be used for a filename; make sure it doesn't |
| 448 // contain anything which will confuse us. | 337 // contain anything which will confuse us. |
| 449 DCHECK_EQ(std::string::npos, mem_name.find('/')); | 338 DCHECK_EQ(std::string::npos, mem_name.find('/')); |
| 450 DCHECK_EQ(std::string::npos, mem_name.find('\0')); | 339 DCHECK_EQ(std::string::npos, mem_name.find('\0')); |
| 451 | 340 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 495 | 384 |
| 496 if (close_self) { | 385 if (close_self) { |
| 497 Unmap(); | 386 Unmap(); |
| 498 Close(); | 387 Close(); |
| 499 } | 388 } |
| 500 | 389 |
| 501 return true; | 390 return true; |
| 502 } | 391 } |
| 503 | 392 |
| 504 } // namespace base | 393 } // namespace base |
| OLD | NEW |