• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors
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 <mach/vm_map.h>
8 
9 #include "base/mac/mach_logging.h"
10 #include "base/mac/scoped_mach_vm.h"
11 #include "base/metrics/histogram_functions.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "build/build_config.h"
14 
15 namespace base {
16 namespace subtle {
17 
18 namespace {
19 
20 }  // namespace
21 
22 // static
Take(mac::ScopedMachSendRight handle,Mode mode,size_t size,const UnguessableToken & guid)23 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Take(
24     mac::ScopedMachSendRight handle,
25     Mode mode,
26     size_t size,
27     const UnguessableToken& guid) {
28   if (!handle.is_valid())
29     return {};
30 
31   if (size == 0)
32     return {};
33 
34   if (size > static_cast<size_t>(std::numeric_limits<int>::max()))
35     return {};
36 
37   CHECK(
38       CheckPlatformHandlePermissionsCorrespondToMode(handle.get(), mode, size));
39 
40   return PlatformSharedMemoryRegion(std::move(handle), mode, size, guid);
41 }
42 
GetPlatformHandle() const43 mach_port_t PlatformSharedMemoryRegion::GetPlatformHandle() const {
44   return handle_.get();
45 }
46 
IsValid() const47 bool PlatformSharedMemoryRegion::IsValid() const {
48   return handle_.is_valid();
49 }
50 
Duplicate() const51 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Duplicate() const {
52   if (!IsValid())
53     return {};
54 
55   CHECK_NE(mode_, Mode::kWritable)
56       << "Duplicating a writable shared memory region is prohibited";
57 
58   // Increment the ref count.
59   kern_return_t kr = mach_port_mod_refs(mach_task_self(), handle_.get(),
60                                         MACH_PORT_RIGHT_SEND, 1);
61   if (kr != KERN_SUCCESS) {
62     MACH_DLOG(ERROR, kr) << "mach_port_mod_refs";
63     return {};
64   }
65 
66   return PlatformSharedMemoryRegion(mac::ScopedMachSendRight(handle_.get()),
67                                     mode_, size_, guid_);
68 }
69 
ConvertToReadOnly()70 bool PlatformSharedMemoryRegion::ConvertToReadOnly() {
71   return ConvertToReadOnly(nullptr);
72 }
73 
ConvertToReadOnly(void * mapped_addr)74 bool PlatformSharedMemoryRegion::ConvertToReadOnly(void* mapped_addr) {
75   if (!IsValid())
76     return false;
77 
78   CHECK_EQ(mode_, Mode::kWritable)
79       << "Only writable shared memory region can be converted to read-only";
80 
81   mac::ScopedMachSendRight handle_copy(handle_.release());
82 
83   void* temp_addr = mapped_addr;
84   mac::ScopedMachVM scoped_memory;
85   if (!temp_addr) {
86     // Intentionally lower current prot and max prot to |VM_PROT_READ|.
87     kern_return_t kr =
88         vm_map(mach_task_self(), reinterpret_cast<vm_address_t*>(&temp_addr),
89                size_, 0, VM_FLAGS_ANYWHERE, handle_copy.get(), 0, FALSE,
90                VM_PROT_READ, VM_PROT_READ, VM_INHERIT_NONE);
91     if (kr != KERN_SUCCESS) {
92       MACH_DLOG(ERROR, kr) << "vm_map";
93       return false;
94     }
95     scoped_memory.reset(reinterpret_cast<vm_address_t>(temp_addr),
96                         mach_vm_round_page(size_));
97   }
98 
99   // Make new memory object.
100   memory_object_size_t allocation_size = size_;
101   mac::ScopedMachSendRight named_right;
102   kern_return_t kr = mach_make_memory_entry_64(
103       mach_task_self(), &allocation_size,
104       reinterpret_cast<memory_object_offset_t>(temp_addr), VM_PROT_READ,
105       mac::ScopedMachSendRight::Receiver(named_right).get(), MACH_PORT_NULL);
106   if (kr != KERN_SUCCESS) {
107     MACH_DLOG(ERROR, kr) << "mach_make_memory_entry_64";
108     return false;
109   }
110   DCHECK_GE(allocation_size, size_);
111 
112   handle_ = std::move(named_right);
113   mode_ = Mode::kReadOnly;
114   return true;
115 }
116 
ConvertToUnsafe()117 bool PlatformSharedMemoryRegion::ConvertToUnsafe() {
118   if (!IsValid())
119     return false;
120 
121   CHECK_EQ(mode_, Mode::kWritable)
122       << "Only writable shared memory region can be converted to unsafe";
123 
124   mode_ = Mode::kUnsafe;
125   return true;
126 }
127 
128 // static
Create(Mode mode,size_t size)129 PlatformSharedMemoryRegion PlatformSharedMemoryRegion::Create(Mode mode,
130                                                               size_t size) {
131   if (size == 0) {
132     return {};
133   }
134 
135   if (size > static_cast<size_t>(std::numeric_limits<int>::max())) {
136     return {};
137   }
138 
139   CHECK_NE(mode, Mode::kReadOnly) << "Creating a region in read-only mode will "
140                                      "lead to this region being non-modifiable";
141 
142   memory_object_size_t vm_size = size;
143   mac::ScopedMachSendRight named_right;
144   kern_return_t kr = mach_make_memory_entry_64(
145       mach_task_self(), &vm_size,
146       0,  // Address.
147       MAP_MEM_NAMED_CREATE | VM_PROT_READ | VM_PROT_WRITE,
148       mac::ScopedMachSendRight::Receiver(named_right).get(),
149       MACH_PORT_NULL);  // Parent handle.
150   // Crash as soon as shm allocation fails to debug the issue
151   // https://crbug.com/872237.
152   MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_make_memory_entry_64";
153   DCHECK_GE(vm_size, size);
154 
155   return PlatformSharedMemoryRegion(std::move(named_right), mode, size,
156                                     UnguessableToken::Create());
157 }
158 
159 // static
CheckPlatformHandlePermissionsCorrespondToMode(PlatformSharedMemoryHandle handle,Mode mode,size_t size)160 bool PlatformSharedMemoryRegion::CheckPlatformHandlePermissionsCorrespondToMode(
161     PlatformSharedMemoryHandle handle,
162     Mode mode,
163     size_t size) {
164   vm_address_t temp_addr = 0;
165   kern_return_t kr =
166       vm_map(mach_task_self(), &temp_addr, size, 0, VM_FLAGS_ANYWHERE, handle,
167              0, FALSE, VM_PROT_READ | VM_PROT_WRITE,
168              VM_PROT_READ | VM_PROT_WRITE, VM_INHERIT_NONE);
169   if (kr == KERN_SUCCESS) {
170     kern_return_t kr_deallocate =
171         vm_deallocate(mach_task_self(), temp_addr, size);
172     // TODO(crbug.com/838365): convert to DLOG when bug fixed.
173     MACH_LOG_IF(ERROR, kr_deallocate != KERN_SUCCESS, kr_deallocate)
174         << "vm_deallocate";
175   } else if (kr != KERN_INVALID_RIGHT) {
176     MACH_LOG(ERROR, kr) << "vm_map";
177     return false;
178   }
179 
180   bool is_read_only = kr == KERN_INVALID_RIGHT;
181   bool expected_read_only = mode == Mode::kReadOnly;
182 
183   if (is_read_only != expected_read_only) {
184     // TODO(crbug.com/838365): convert to DLOG when bug fixed.
185     LOG(ERROR) << "VM region has a wrong protection mask: it is"
186                << (is_read_only ? " " : " not ") << "read-only but it should"
187                << (expected_read_only ? " " : " not ") << "be";
188     return false;
189   }
190 
191   return true;
192 }
193 
PlatformSharedMemoryRegion(mac::ScopedMachSendRight handle,Mode mode,size_t size,const UnguessableToken & guid)194 PlatformSharedMemoryRegion::PlatformSharedMemoryRegion(
195     mac::ScopedMachSendRight handle,
196     Mode mode,
197     size_t size,
198     const UnguessableToken& guid)
199     : handle_(std::move(handle)), mode_(mode), size_(size), guid_(guid) {}
200 
201 }  // namespace subtle
202 }  // namespace base
203