- add sources.
[platform/framework/web/crosswalk.git] / src / base / memory / shared_memory_posix.cc
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 <sys/mman.h>
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <unistd.h>
13
14 #include "base/file_util.h"
15 #include "base/lazy_instance.h"
16 #include "base/logging.h"
17 #include "base/process/process_metrics.h"
18 #include "base/safe_strerror_posix.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/synchronization/lock.h"
21 #include "base/threading/platform_thread.h"
22 #include "base/threading/thread_restrictions.h"
23
24 #if defined(OS_MACOSX)
25 #include "base/mac/foundation_util.h"
26 #endif  // OS_MACOSX
27
28 #if defined(OS_ANDROID)
29 #include "base/os_compat_android.h"
30 #include "third_party/ashmem/ashmem.h"
31 #endif
32
33 namespace base {
34
35 namespace {
36
37 LazyInstance<Lock>::Leaky g_thread_lock_ = LAZY_INSTANCE_INITIALIZER;
38
39 }
40
41 SharedMemory::SharedMemory()
42     : mapped_file_(-1),
43       inode_(0),
44       mapped_size_(0),
45       memory_(NULL),
46       read_only_(false),
47       requested_size_(0) {
48 }
49
50 SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
51     : mapped_file_(handle.fd),
52       inode_(0),
53       mapped_size_(0),
54       memory_(NULL),
55       read_only_(read_only),
56       requested_size_(0) {
57   struct stat st;
58   if (fstat(handle.fd, &st) == 0) {
59     // If fstat fails, then the file descriptor is invalid and we'll learn this
60     // fact when Map() fails.
61     inode_ = st.st_ino;
62   }
63 }
64
65 SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
66                            ProcessHandle process)
67     : mapped_file_(handle.fd),
68       inode_(0),
69       mapped_size_(0),
70       memory_(NULL),
71       read_only_(read_only),
72       requested_size_(0) {
73   // We don't handle this case yet (note the ignored parameter); let's die if
74   // someone comes calling.
75   NOTREACHED();
76 }
77
78 SharedMemory::~SharedMemory() {
79   Close();
80 }
81
82 // static
83 bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
84   return handle.fd >= 0;
85 }
86
87 // static
88 SharedMemoryHandle SharedMemory::NULLHandle() {
89   return SharedMemoryHandle();
90 }
91
92 // static
93 void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
94   DCHECK_GE(handle.fd, 0);
95   if (HANDLE_EINTR(close(handle.fd)) < 0)
96     DPLOG(ERROR) << "close";
97 }
98
99 // static
100 size_t SharedMemory::GetHandleLimit() {
101   return base::GetMaxFds();
102 }
103
104 bool SharedMemory::CreateAndMapAnonymous(size_t size) {
105   return CreateAnonymous(size) && Map(size);
106 }
107
108 #if !defined(OS_ANDROID)
109 // Chromium mostly only uses the unique/private shmem as specified by
110 // "name == L"". The exception is in the StatsTable.
111 // TODO(jrg): there is no way to "clean up" all unused named shmem if
112 // we restart from a crash.  (That isn't a new problem, but it is a problem.)
113 // In case we want to delete it later, it may be useful to save the value
114 // of mem_filename after FilePathForMemoryName().
115 bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
116   DCHECK_EQ(-1, mapped_file_);
117   if (options.size == 0) return false;
118
119   if (options.size > static_cast<size_t>(std::numeric_limits<int>::max()))
120     return false;
121
122   // This function theoretically can block on the disk, but realistically
123   // the temporary files we create will just go into the buffer cache
124   // and be deleted before they ever make it out to disk.
125   base::ThreadRestrictions::ScopedAllowIO allow_io;
126
127   FILE *fp;
128   bool fix_size = true;
129
130   FilePath path;
131   if (options.name == NULL || options.name->empty()) {
132     // It doesn't make sense to have a open-existing private piece of shmem
133     DCHECK(!options.open_existing);
134     // Q: Why not use the shm_open() etc. APIs?
135     // A: Because they're limited to 4mb on OS X.  FFFFFFFUUUUUUUUUUU
136     fp = file_util::CreateAndOpenTemporaryShmemFile(&path, options.executable);
137
138     // Deleting the file prevents anyone else from mapping it in (making it
139     // private), and prevents the need for cleanup (once the last fd is closed,
140     // it is truly freed).
141     if (fp) {
142       if (unlink(path.value().c_str()))
143         PLOG(WARNING) << "unlink";
144     }
145   } else {
146     if (!FilePathForMemoryName(*options.name, &path))
147       return false;
148
149     // Make sure that the file is opened without any permission
150     // to other users on the system.
151     const mode_t kOwnerOnly = S_IRUSR | S_IWUSR;
152
153     // First, try to create the file.
154     int fd = HANDLE_EINTR(
155         open(path.value().c_str(), O_RDWR | O_CREAT | O_EXCL, kOwnerOnly));
156     if (fd == -1 && options.open_existing) {
157       // If this doesn't work, try and open an existing file in append mode.
158       // Opening an existing file in a world writable directory has two main
159       // security implications:
160       // - Attackers could plant a file under their control, so ownership of
161       //   the file is checked below.
162       // - Attackers could plant a symbolic link so that an unexpected file
163       //   is opened, so O_NOFOLLOW is passed to open().
164       fd = HANDLE_EINTR(
165           open(path.value().c_str(), O_RDWR | O_APPEND | O_NOFOLLOW));
166
167       // Check that the current user owns the file.
168       // If uid != euid, then a more complex permission model is used and this
169       // API is not appropriate.
170       const uid_t real_uid = getuid();
171       const uid_t effective_uid = geteuid();
172       struct stat sb;
173       if (fd >= 0 &&
174           (fstat(fd, &sb) != 0 || sb.st_uid != real_uid ||
175            sb.st_uid != effective_uid)) {
176         LOG(ERROR) <<
177             "Invalid owner when opening existing shared memory file.";
178         HANDLE_EINTR(close(fd));
179         return false;
180       }
181
182       // An existing file was opened, so its size should not be fixed.
183       fix_size = false;
184     }
185     fp = NULL;
186     if (fd >= 0) {
187       // "a+" is always appropriate: if it's a new file, a+ is similar to w+.
188       fp = fdopen(fd, "a+");
189     }
190   }
191   if (fp && fix_size) {
192     // Get current size.
193     struct stat stat;
194     if (fstat(fileno(fp), &stat) != 0) {
195       file_util::CloseFile(fp);
196       return false;
197     }
198     const size_t current_size = stat.st_size;
199     if (current_size != options.size) {
200       if (HANDLE_EINTR(ftruncate(fileno(fp), options.size)) != 0) {
201         file_util::CloseFile(fp);
202         return false;
203       }
204     }
205     requested_size_ = options.size;
206   }
207   if (fp == NULL) {
208 #if !defined(OS_MACOSX)
209     PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
210     FilePath dir = path.DirName();
211     if (access(dir.value().c_str(), W_OK | X_OK) < 0) {
212       PLOG(ERROR) << "Unable to access(W_OK|X_OK) " << dir.value();
213       if (dir.value() == "/dev/shm") {
214         LOG(FATAL) << "This is frequently caused by incorrect permissions on "
215                    << "/dev/shm.  Try 'sudo chmod 1777 /dev/shm' to fix.";
216       }
217     }
218 #else
219     PLOG(ERROR) << "Creating shared memory in " << path.value() << " failed";
220 #endif
221     return false;
222   }
223
224   return PrepareMapFile(fp);
225 }
226
227 // Our current implementation of shmem is with mmap()ing of files.
228 // These files need to be deleted explicitly.
229 // In practice this call is only needed for unit tests.
230 bool SharedMemory::Delete(const std::string& name) {
231   FilePath path;
232   if (!FilePathForMemoryName(name, &path))
233     return false;
234
235   if (PathExists(path))
236     return base::DeleteFile(path, false);
237
238   // Doesn't exist, so success.
239   return true;
240 }
241
242 bool SharedMemory::Open(const std::string& name, bool read_only) {
243   FilePath path;
244   if (!FilePathForMemoryName(name, &path))
245     return false;
246
247   read_only_ = read_only;
248
249   const char *mode = read_only ? "r" : "r+";
250   FILE *fp = file_util::OpenFile(path, mode);
251   return PrepareMapFile(fp);
252 }
253
254 #endif  // !defined(OS_ANDROID)
255
256 bool SharedMemory::MapAt(off_t offset, size_t bytes) {
257   if (mapped_file_ == -1)
258     return false;
259
260   if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
261     return false;
262
263 #if defined(OS_ANDROID)
264   // On Android, Map can be called with a size and offset of zero to use the
265   // ashmem-determined size.
266   if (bytes == 0) {
267     DCHECK_EQ(0, offset);
268     int ashmem_bytes = ashmem_get_size_region(mapped_file_);
269     if (ashmem_bytes < 0)
270       return false;
271     bytes = ashmem_bytes;
272   }
273 #endif
274
275   memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE),
276                  MAP_SHARED, mapped_file_, offset);
277
278   bool mmap_succeeded = memory_ != (void*)-1 && memory_ != NULL;
279   if (mmap_succeeded) {
280     mapped_size_ = bytes;
281     DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
282         (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
283   } else {
284     memory_ = NULL;
285   }
286
287   return mmap_succeeded;
288 }
289
290 bool SharedMemory::Unmap() {
291   if (memory_ == NULL)
292     return false;
293
294   munmap(memory_, mapped_size_);
295   memory_ = NULL;
296   mapped_size_ = 0;
297   return true;
298 }
299
300 SharedMemoryHandle SharedMemory::handle() const {
301   return FileDescriptor(mapped_file_, false);
302 }
303
304 void SharedMemory::Close() {
305   Unmap();
306
307   if (mapped_file_ > 0) {
308     if (HANDLE_EINTR(close(mapped_file_)) < 0)
309       PLOG(ERROR) << "close";
310     mapped_file_ = -1;
311   }
312 }
313
314 void SharedMemory::Lock() {
315   g_thread_lock_.Get().Acquire();
316   LockOrUnlockCommon(F_LOCK);
317 }
318
319 void SharedMemory::Unlock() {
320   LockOrUnlockCommon(F_ULOCK);
321   g_thread_lock_.Get().Release();
322 }
323
324 #if !defined(OS_ANDROID)
325 bool SharedMemory::PrepareMapFile(FILE *fp) {
326   DCHECK_EQ(-1, mapped_file_);
327   if (fp == NULL) return false;
328
329   // This function theoretically can block on the disk, but realistically
330   // the temporary files we create will just go into the buffer cache
331   // and be deleted before they ever make it out to disk.
332   base::ThreadRestrictions::ScopedAllowIO allow_io;
333
334   file_util::ScopedFILE file_closer(fp);
335
336   mapped_file_ = dup(fileno(fp));
337   if (mapped_file_ == -1) {
338     if (errno == EMFILE) {
339       LOG(WARNING) << "Shared memory creation failed; out of file descriptors";
340       return false;
341     } else {
342       NOTREACHED() << "Call to dup failed, errno=" << errno;
343     }
344   }
345
346   struct stat st;
347   if (fstat(mapped_file_, &st))
348     NOTREACHED();
349   inode_ = st.st_ino;
350
351   return true;
352 }
353 #endif
354
355 // For the given shmem named |mem_name|, return a filename to mmap()
356 // (and possibly create).  Modifies |filename|.  Return false on
357 // error, or true of we are happy.
358 bool SharedMemory::FilePathForMemoryName(const std::string& mem_name,
359                                          FilePath* path) {
360   // mem_name will be used for a filename; make sure it doesn't
361   // contain anything which will confuse us.
362   DCHECK_EQ(std::string::npos, mem_name.find('/'));
363   DCHECK_EQ(std::string::npos, mem_name.find('\0'));
364
365   FilePath temp_dir;
366   if (!file_util::GetShmemTempDir(&temp_dir, false))
367     return false;
368
369 #if !defined(OS_MACOSX)
370 #if defined(GOOGLE_CHROME_BUILD)
371   std::string name_base = std::string("com.google.Chrome");
372 #else
373   std::string name_base = std::string("org.chromium.Chromium");
374 #endif
375 #else  // OS_MACOSX
376   std::string name_base = std::string(base::mac::BaseBundleID());
377 #endif  // OS_MACOSX
378   *path = temp_dir.AppendASCII(name_base + ".shmem." + mem_name);
379   return true;
380 }
381
382 void SharedMemory::LockOrUnlockCommon(int function) {
383   DCHECK_GE(mapped_file_, 0);
384   while (lockf(mapped_file_, function, 0) < 0) {
385     if (errno == EINTR) {
386       continue;
387     } else if (errno == ENOLCK) {
388       // temporary kernel resource exaustion
389       base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(500));
390       continue;
391     } else {
392       NOTREACHED() << "lockf() failed."
393                    << " function:" << function
394                    << " fd:" << mapped_file_
395                    << " errno:" << errno
396                    << " msg:" << safe_strerror(errno);
397     }
398   }
399 }
400
401 bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
402                                         SharedMemoryHandle *new_handle,
403                                         bool close_self) {
404   const int new_fd = dup(mapped_file_);
405   if (new_fd < 0) {
406     DPLOG(ERROR) << "dup() failed.";
407     return false;
408   }
409
410   new_handle->fd = new_fd;
411   new_handle->auto_close = true;
412
413   if (close_self)
414     Close();
415
416   return true;
417 }
418
419 }  // namespace base