• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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/platform_shared_memory_region.h"
6 
7 #include <sys/mman.h>
8 
9 #include "base/posix/eintr_wrapper.h"
10 #include "third_party/ashmem/ashmem.h"
11 
12 namespace base {
13 namespace subtle {
14 
15 // For Android, we use ashmem to implement SharedMemory. ashmem_create_region
16 // will automatically pin the region. We never explicitly call pin/unpin. When
17 // all the file descriptors from different processes associated with the region
18 // are closed, the memory buffer will go away.
19 
20 namespace {
21 
GetAshmemRegionProtectionMask(int fd)22 static int GetAshmemRegionProtectionMask(int fd) {
23   int prot = ashmem_get_prot_region(fd);
24   if (prot < 0) {
25     DPLOG(ERROR) << "ashmem_get_prot_region failed";
26     return -1;
27   }
28   return prot;
29 }
30 
31 }  // namespace
32 
33 // static
Take(ScopedFD fd,Mode mode,size_t size,const UnguessableToken & guid)34 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
35     ScopedFD fd,
36     Mode mode,
37     size_t size,
38     const UnguessableToken& guid) {
39   if (!fd.is_valid())
40     return {};
41 
42   if (size == 0)
43     return {};
44 
45   if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
46     return {};
47 
48   CHECK(CheckPlatformHandlePermissionsCorrespondToMode(fd.get(), mode, size));
49 
50   return PlatformSharedMemoryRegion(std::move(fd), mode, size, guid);
51 }
52 
GetPlatformHandle() const53 int PlatformSharedMemoryRegion::GetPlatformHandle() const {
54   return handle_.get();
55 }
56 
IsValid() const57 bool PlatformSharedMemoryRegion::IsValid() const {
58   return handle_.is_valid();
59 }
60 
Duplicate() const61 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const {
62   if (!IsValid())
63     return {};
64 
65   CHECK_NE(mode_, Mode::kWritable)
66       << "Duplicating a writable shared memory region is prohibited";
67 
68   ScopedFD duped_fd(HANDLE_EINTR(dup(handle_.get())));
69   if (!duped_fd.is_valid()) {
70     DPLOG(ERROR) << "dup(" << handle_.get() << ") failed";
71     return {};
72   }
73 
74   return PlatformSharedMemoryRegion(std::move(duped_fd), mode_, size_, guid_);
75 }
76 
ConvertToReadOnly()77 bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
78   if (!IsValid())
79     return false;
80 
81   CHECK_EQ(mode_, Mode::kWritable)
82       << "Only writable shared memory region can be converted to read-only";
83 
84   ScopedFD handle_copy(handle_.release());
85 
86   int prot = GetAshmemRegionProtectionMask(handle_copy.get());
87   if (prot < 0)
88     return false;
89 
90   prot &= ~PROT_WRITE;
91   int ret = ashmem_set_prot_region(handle_copy.get(), prot);
92   if (ret != 0) {
93     DPLOG(ERROR) << "ashmem_set_prot_region failed";
94     return false;
95   }
96 
97   handle_ = std::move(handle_copy);
98   mode_ = Mode::kReadOnly;
99   return true;
100 }
101 
ConvertToUnsafe()102 bool PlatformSharedMemoryRegion::ConvertToUnsafe() {
103   if (!IsValid())
104     return false;
105 
106   CHECK_EQ(mode_, Mode::kWritable)
107       << "Only writable shared memory region can be converted to unsafe";
108 
109   mode_ = Mode::kUnsafe;
110   return true;
111 }
112 
MapAt(off_t offset,size_t size,void ** memory,size_t * mapped_size) const113 bool PlatformSharedMemoryRegion::MapAt(off_t offset,
114                                        size_t size,
115                                        void** memory,
116                                        size_t* mapped_size) const {
117   if (!IsValid())
118     return false;
119 
120   size_t end_byte;
121   if (!CheckAdd(offset, size).AssignIfValid(&end_byte) || end_byte > size_) {
122     return false;
123   }
124 
125   bool write_allowed = mode_ != Mode::kReadOnly;
126   *memory = mmap(nullptr, size, PROT_READ | (write_allowed ? PROT_WRITE : 0),
127                  MAP_SHARED, handle_.get(), offset);
128 
129   bool mmap_succeeded = *memory && *memory != reinterpret_cast<void*>(-1);
130   if (!mmap_succeeded) {
131     DPLOG(ERROR) << "mmap " << handle_.get() << " failed";
132     return false;
133   }
134 
135   *mapped_size = size;
136   DCHECK_EQ(0U,
137             reinterpret_cast<uintptr_t>(*memory) & (kMapMinimumAlignment - 1));
138   return true;
139 }
140 
141 // static
Create(Mode mode,size_t size)142 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
143                                                               size_t size) {
144   if (size == 0)
145     return {};
146 
147   if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
148     return {};
149 
150   CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will "
151                                      "lead to this region being non-modifiable";
152 
153   UnguessableToken guid = UnguessableToken::Create();
154 
155   // trace_event is not supported in libchrome. To avoid includes of more
156   // trace_event code by base/memory/shared_memory_tracker.h, replace
157   // SharedMemoryTracker::GetDumpNameForTracing by the actual implementation.
158   ScopedFD fd(ashmem_create_region(
159       ("shared_memory/"+guid.ToString()).c_str(), size));
160   if (!fd.is_valid()) {
161     DPLOG(ERROR) << "ashmem_create_region failed";
162     return {};
163   }
164 
165   int err = ashmem_set_prot_region(fd.get(), PROT_READ | PROT_WRITE);
166   if (err < 0) {
167     DPLOG(ERROR) << "ashmem_set_prot_region failed";
168     return {};
169   }
170 
171   return PlatformSharedMemoryRegion(std::move(fd), mode, size, guid);
172 }
173 
CheckPlatformHandlePermissionsCorrespondToMode(PlatformHandle handle,Mode mode,size_t size)174 bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
175     PlatformHandle handle,
176     Mode mode,
177     size_t size) {
178   int prot = GetAshmemRegionProtectionMask(handle);
179   if (prot < 0)
180     return false;
181 
182   bool is_read_only = (prot & PROT_WRITE) == 0;
183   bool expected_read_only = mode == Mode::kReadOnly;
184 
185   if (is_read_only != expected_read_only) {
186     DLOG(ERROR) << "Ashmem region has a wrong protection mask: it is"
187                 << (is_read_only ? " " : " not ") << "read-only but it should"
188                 << (expected_read_only ? " " : " not ") << "be";
189     return false;
190   }
191 
192   return true;
193 }
194 
PlatformSharedMemoryRegion(ScopedFD fd,Mode mode,size_t size,const UnguessableToken & guid)195 PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
196     ScopedFD fd,
197     Mode mode,
198     size_t size,
199     const UnguessableToken& guid)
200     : handle_(std::move(fd)), mode_(mode), size_(size), guid_(guid) {}
201 
202 }  // namespace subtle
203 }  // namespace base
204