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