1// Copyright (c) 2011 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 <aclapi.h>
8
9#include "base/logging.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/rand_util.h"
12#include "base/strings/stringprintf.h"
13#include "base/strings/utf_string_conversions.h"
14
15namespace {
16
17// Returns the length of the memory section starting at the supplied address.
18size_t GetMemorySectionSize(void* address) {
19  MEMORY_BASIC_INFORMATION memory_info;
20  if (!::VirtualQuery(address, &memory_info, sizeof(memory_info)))
21    return 0;
22  return memory_info.RegionSize - (static_cast<char*>(address) -
23         static_cast<char*>(memory_info.AllocationBase));
24}
25
26}  // namespace.
27
28namespace base {
29
30SharedMemory::SharedMemory()
31    : mapped_file_(NULL),
32      memory_(NULL),
33      read_only_(false),
34      mapped_size_(0),
35      requested_size_(0),
36      lock_(NULL) {
37}
38
39SharedMemory::SharedMemory(const std::wstring& name)
40    : mapped_file_(NULL),
41      memory_(NULL),
42      read_only_(false),
43      requested_size_(0),
44      mapped_size_(0),
45      lock_(NULL),
46      name_(name) {
47}
48
49SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only)
50    : mapped_file_(handle),
51      memory_(NULL),
52      read_only_(read_only),
53      requested_size_(0),
54      mapped_size_(0),
55      lock_(NULL) {
56}
57
58SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only,
59                           ProcessHandle process)
60    : mapped_file_(NULL),
61      memory_(NULL),
62      read_only_(read_only),
63      requested_size_(0),
64      mapped_size_(0),
65      lock_(NULL) {
66  ::DuplicateHandle(process, handle,
67                    GetCurrentProcess(), &mapped_file_,
68                    read_only_ ? FILE_MAP_READ : FILE_MAP_READ |
69                        FILE_MAP_WRITE,
70                    FALSE, 0);
71}
72
73SharedMemory::~SharedMemory() {
74  Close();
75  if (lock_ != NULL)
76    CloseHandle(lock_);
77}
78
79// static
80bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) {
81  return handle != NULL;
82}
83
84// static
85SharedMemoryHandle SharedMemory::NULLHandle() {
86  return NULL;
87}
88
89// static
90void SharedMemory::CloseHandle(const SharedMemoryHandle& handle) {
91  DCHECK(handle != NULL);
92  ::CloseHandle(handle);
93}
94
95// static
96size_t SharedMemory::GetHandleLimit() {
97  // Rounded down from value reported here:
98  // http://blogs.technet.com/b/markrussinovich/archive/2009/09/29/3283844.aspx
99  return static_cast<size_t>(1 << 23);
100}
101
102bool SharedMemory::CreateAndMapAnonymous(size_t size) {
103  return CreateAnonymous(size) && Map(size);
104}
105
106bool SharedMemory::Create(const SharedMemoryCreateOptions& options) {
107  // TODO(bsy,sehr): crbug.com/210609 NaCl forces us to round up 64k here,
108  // wasting 32k per mapping on average.
109  static const size_t kSectionMask = 65536 - 1;
110  DCHECK(!options.executable);
111  DCHECK(!mapped_file_);
112  if (options.size == 0)
113    return false;
114
115  // Check maximum accounting for overflow.
116  if (options.size >
117      static_cast<size_t>(std::numeric_limits<int>::max()) - kSectionMask)
118    return false;
119
120  size_t rounded_size = (options.size + kSectionMask) & ~kSectionMask;
121  name_ = ASCIIToWide(options.name_deprecated == NULL ? "" :
122                      *options.name_deprecated);
123  SECURITY_ATTRIBUTES sa = { sizeof(sa), NULL, FALSE };
124  SECURITY_DESCRIPTOR sd;
125  ACL dacl;
126
127  if (options.share_read_only && name_.empty()) {
128    // Add an empty DACL to enforce anonymous read-only sections.
129    sa.lpSecurityDescriptor = &sd;
130    if (!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION))
131      return false;
132    if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
133      return false;
134    if (!SetSecurityDescriptorDacl(&sd, TRUE, &dacl, FALSE))
135      return false;
136
137    // Windows ignores DACLs on certain unnamed objects (like shared sections).
138    // So, we generate a random name when we need to enforce read-only.
139    uint64_t rand_values[4];
140    base::RandBytes(&rand_values, sizeof(rand_values));
141    name_ = base::StringPrintf(L"CrSharedMem_%016x%016x%016x%016x",
142                               rand_values[0], rand_values[1],
143                               rand_values[2], rand_values[3]);
144  }
145  mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, &sa,
146      PAGE_READWRITE, 0, static_cast<DWORD>(rounded_size), name_.c_str());
147  if (!mapped_file_)
148    return false;
149
150  requested_size_ = options.size;
151
152  // Check if the shared memory pre-exists.
153  if (GetLastError() == ERROR_ALREADY_EXISTS) {
154    // If the file already existed, set requested_size_ to 0 to show that
155    // we don't know the size.
156    requested_size_ = 0;
157    if (!options.open_existing_deprecated) {
158      Close();
159      return false;
160    }
161  }
162
163  return true;
164}
165
166bool SharedMemory::Delete(const std::string& name) {
167  // intentionally empty -- there is nothing for us to do on Windows.
168  return true;
169}
170
171bool SharedMemory::Open(const std::string& name, bool read_only) {
172  DCHECK(!mapped_file_);
173
174  name_ = ASCIIToWide(name);
175  read_only_ = read_only;
176  mapped_file_ = OpenFileMapping(
177      read_only_ ? FILE_MAP_READ : FILE_MAP_READ | FILE_MAP_WRITE,
178      false, name_.empty() ? NULL : name_.c_str());
179  if (mapped_file_ != NULL) {
180    // Note: size_ is not set in this case.
181    return true;
182  }
183  return false;
184}
185
186bool SharedMemory::MapAt(off_t offset, size_t bytes) {
187  if (mapped_file_ == NULL)
188    return false;
189
190  if (bytes > static_cast<size_t>(std::numeric_limits<int>::max()))
191    return false;
192
193  if (memory_)
194    return false;
195
196  memory_ = MapViewOfFile(mapped_file_,
197                          read_only_ ? FILE_MAP_READ : FILE_MAP_READ |
198                              FILE_MAP_WRITE,
199                          static_cast<uint64>(offset) >> 32,
200                          static_cast<DWORD>(offset),
201                          bytes);
202  if (memory_ != NULL) {
203    DCHECK_EQ(0U, reinterpret_cast<uintptr_t>(memory_) &
204        (SharedMemory::MAP_MINIMUM_ALIGNMENT - 1));
205    mapped_size_ = GetMemorySectionSize(memory_);
206    return true;
207  }
208  return false;
209}
210
211bool SharedMemory::Unmap() {
212  if (memory_ == NULL)
213    return false;
214
215  UnmapViewOfFile(memory_);
216  memory_ = NULL;
217  return true;
218}
219
220bool SharedMemory::ShareToProcessCommon(ProcessHandle process,
221                                        SharedMemoryHandle *new_handle,
222                                        bool close_self,
223                                        ShareMode share_mode) {
224  *new_handle = 0;
225  DWORD access = FILE_MAP_READ;
226  DWORD options = 0;
227  HANDLE mapped_file = mapped_file_;
228  HANDLE result;
229  if (share_mode == SHARE_CURRENT_MODE && !read_only_)
230    access |= FILE_MAP_WRITE;
231  if (close_self) {
232    // DUPLICATE_CLOSE_SOURCE causes DuplicateHandle to close mapped_file.
233    options = DUPLICATE_CLOSE_SOURCE;
234    mapped_file_ = NULL;
235    Unmap();
236  }
237
238  if (process == GetCurrentProcess() && close_self) {
239    *new_handle = mapped_file;
240    return true;
241  }
242
243  if (!DuplicateHandle(GetCurrentProcess(), mapped_file, process,
244      &result, access, FALSE, options))
245    return false;
246  *new_handle = result;
247  return true;
248}
249
250
251void SharedMemory::Close() {
252  if (memory_ != NULL) {
253    UnmapViewOfFile(memory_);
254    memory_ = NULL;
255  }
256
257  if (mapped_file_ != NULL) {
258    CloseHandle(mapped_file_);
259    mapped_file_ = NULL;
260  }
261}
262
263void SharedMemory::LockDeprecated() {
264  if (lock_ == NULL) {
265    std::wstring name = name_;
266    name.append(L"lock");
267    lock_ = CreateMutex(NULL, FALSE, name.c_str());
268    if (lock_ == NULL) {
269      DPLOG(ERROR) << "Could not create mutex.";
270      NOTREACHED();
271      return;  // There is nothing good we can do here.
272    }
273  }
274  DWORD result = WaitForSingleObject(lock_, INFINITE);
275  DCHECK_EQ(result, WAIT_OBJECT_0);
276}
277
278void SharedMemory::UnlockDeprecated() {
279  DCHECK(lock_ != NULL);
280  ReleaseMutex(lock_);
281}
282
283SharedMemoryHandle SharedMemory::handle() const {
284  return mapped_file_;
285}
286
287}  // namespace base
288