1 // Copyright 2016 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6
7 #include <err.h>
8 #include <inttypes.h>
9 #include <trace.h>
10
11 #include <vm/vm_object.h>
12 #include <vm/vm_object_paged.h>
13
14 #include <lib/user_copy/user_ptr.h>
15
16 #include <object/handle.h>
17 #include <object/process_dispatcher.h>
18 #include <object/resource.h>
19 #include <object/vm_object_dispatcher.h>
20
21 #include <fbl/auto_call.h>
22 #include <fbl/ref_ptr.h>
23 #include <zircon/thread_annotations.h>
24
25 #include "priv.h"
26
27 #define LOCAL_TRACE 0
28
29 static_assert(ZX_CACHE_POLICY_CACHED == ARCH_MMU_FLAG_CACHED,
30 "Cache policy constant mismatch - CACHED");
31 static_assert(ZX_CACHE_POLICY_UNCACHED == ARCH_MMU_FLAG_UNCACHED,
32 "Cache policy constant mismatch - UNCACHED");
33 static_assert(ZX_CACHE_POLICY_UNCACHED_DEVICE == ARCH_MMU_FLAG_UNCACHED_DEVICE,
34 "Cache policy constant mismatch - UNCACHED_DEVICE");
35 static_assert(ZX_CACHE_POLICY_WRITE_COMBINING == ARCH_MMU_FLAG_WRITE_COMBINING,
36 "Cache policy constant mismatch - WRITE_COMBINING");
37 static_assert(ZX_CACHE_POLICY_MASK == ARCH_MMU_FLAG_CACHE_MASK,
38 "Cache policy constant mismatch - CACHE_MASK");
39
40 // zx_status_t zx_vmo_create
sys_vmo_create(uint64_t size,uint32_t options,user_out_handle * out)41 zx_status_t sys_vmo_create(uint64_t size, uint32_t options,
42 user_out_handle* out) {
43 LTRACEF("size %#" PRIx64 "\n", size);
44
45 switch (options) {
46 case 0: options = VmObjectPaged::kResizable; break;
47 case ZX_VMO_NON_RESIZABLE: options = 0u; break;
48 default: return ZX_ERR_INVALID_ARGS;
49 }
50
51 auto up = ProcessDispatcher::GetCurrent();
52 zx_status_t res = up->QueryBasicPolicy(ZX_POL_NEW_VMO);
53 if (res != ZX_OK)
54 return res;
55
56 // create a vm object
57 fbl::RefPtr<VmObject> vmo;
58 res = VmObjectPaged::Create(PMM_ALLOC_FLAG_ANY, options, size, &vmo);
59 if (res != ZX_OK)
60 return res;
61
62 // create a Vm Object dispatcher
63 fbl::RefPtr<Dispatcher> dispatcher;
64 zx_rights_t rights;
65 zx_status_t result = VmObjectDispatcher::Create(ktl::move(vmo), &dispatcher, &rights);
66 if (result != ZX_OK)
67 return result;
68
69 // create a handle and attach the dispatcher to it
70 return out->make(ktl::move(dispatcher), rights);
71 }
72
73 // zx_status_t zx_vmo_read
sys_vmo_read(zx_handle_t handle,user_out_ptr<void> _data,uint64_t offset,size_t len)74 zx_status_t sys_vmo_read(zx_handle_t handle, user_out_ptr<void> _data,
75 uint64_t offset, size_t len) {
76 LTRACEF("handle %x, data %p, offset %#" PRIx64 ", len %#zx\n",
77 handle, _data.get(), offset, len);
78
79 auto up = ProcessDispatcher::GetCurrent();
80
81 // lookup the dispatcher from handle
82 fbl::RefPtr<VmObjectDispatcher> vmo;
83 zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_READ, &vmo);
84 if (status != ZX_OK)
85 return status;
86
87 // Force map the range, even if it crosses multiple mappings.
88 // TODO(ZX-730): This is a workaround for this bug. If we start decommitting
89 // things, the bug will come back. We should fix this more properly.
90 {
91 uint8_t byte = 0;
92 auto int_data = _data.reinterpret<uint8_t>();
93 for (size_t i = 0; i < len; i += PAGE_SIZE) {
94 status = int_data.copy_array_to_user(&byte, 1, i);
95 if (status != ZX_OK) {
96 return status;
97 }
98 }
99 if (len > 0) {
100 status = int_data.copy_array_to_user(&byte, 1, len - 1);
101 if (status != ZX_OK) {
102 return status;
103 }
104 }
105 }
106
107 return vmo->Read(_data, len, offset);
108 }
109
110 // zx_status_t zx_vmo_write
sys_vmo_write(zx_handle_t handle,user_in_ptr<const void> _data,uint64_t offset,size_t len)111 zx_status_t sys_vmo_write(zx_handle_t handle, user_in_ptr<const void> _data,
112 uint64_t offset, size_t len) {
113 LTRACEF("handle %x, data %p, offset %#" PRIx64 ", len %#zx\n",
114 handle, _data.get(), offset, len);
115
116 auto up = ProcessDispatcher::GetCurrent();
117
118 // lookup the dispatcher from handle
119 fbl::RefPtr<VmObjectDispatcher> vmo;
120 zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_WRITE, &vmo);
121 if (status != ZX_OK)
122 return status;
123
124 // Force map the range, even if it crosses multiple mappings.
125 // TODO(ZX-730): This is a workaround for this bug. If we start decommitting
126 // things, the bug will come back. We should fix this more properly.
127 {
128 uint8_t byte = 0;
129 auto int_data = _data.reinterpret<const uint8_t>();
130 for (size_t i = 0; i < len; i += PAGE_SIZE) {
131 status = int_data.copy_array_from_user(&byte, 1, i);
132 if (status != ZX_OK) {
133 return status;
134 }
135 }
136 if (len > 0) {
137 status = int_data.copy_array_from_user(&byte, 1, len - 1);
138 if (status != ZX_OK) {
139 return status;
140 }
141 }
142 }
143
144 return vmo->Write(_data, len, offset);
145 }
146
147 // zx_status_t zx_vmo_get_size
sys_vmo_get_size(zx_handle_t handle,user_out_ptr<uint64_t> _size)148 zx_status_t sys_vmo_get_size(zx_handle_t handle, user_out_ptr<uint64_t> _size) {
149 LTRACEF("handle %x, sizep %p\n", handle, _size.get());
150
151 auto up = ProcessDispatcher::GetCurrent();
152
153 // lookup the dispatcher from handle
154 fbl::RefPtr<VmObjectDispatcher> vmo;
155 zx_status_t status = up->GetDispatcher(handle, &vmo);
156 if (status != ZX_OK)
157 return status;
158
159 // no rights check, anyone should be able to get the size
160
161 // do the operation
162 uint64_t size = 0;
163 status = vmo->GetSize(&size);
164
165 // copy the size back, even if it failed
166 status = _size.copy_to_user(size);
167 if (status != ZX_OK)
168 return status;
169
170 return status;
171 }
172
173 // zx_status_t zx_vmo_set_size
sys_vmo_set_size(zx_handle_t handle,uint64_t size)174 zx_status_t sys_vmo_set_size(zx_handle_t handle, uint64_t size) {
175 LTRACEF("handle %x, size %#" PRIx64 "\n", handle, size);
176
177 auto up = ProcessDispatcher::GetCurrent();
178
179 // lookup the dispatcher from handle
180 fbl::RefPtr<VmObjectDispatcher> vmo;
181 zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_WRITE, &vmo);
182 if (status != ZX_OK)
183 return status;
184
185 // do the operation
186 return vmo->SetSize(size);
187 }
188
189 // zx_status_t zx_vmo_op_range
sys_vmo_op_range(zx_handle_t handle,uint32_t op,uint64_t offset,uint64_t size,user_inout_ptr<void> _buffer,size_t buffer_size)190 zx_status_t sys_vmo_op_range(zx_handle_t handle, uint32_t op, uint64_t offset, uint64_t size,
191 user_inout_ptr<void> _buffer, size_t buffer_size) {
192 LTRACEF("handle %x op %u offset %#" PRIx64 " size %#" PRIx64
193 " buffer %p buffer_size %zu\n",
194 handle, op, offset, size, _buffer.get(), buffer_size);
195
196 auto up = ProcessDispatcher::GetCurrent();
197
198 // lookup the dispatcher from handle
199 // save the rights and pass down into the dispatcher for further testing
200 fbl::RefPtr<VmObjectDispatcher> vmo;
201 zx_rights_t rights;
202 zx_status_t status = up->GetDispatcherAndRights(handle, &vmo, &rights);
203 if (status != ZX_OK) {
204 return status;
205 }
206
207 return vmo->RangeOp(op, offset, size, _buffer, buffer_size, rights);
208 }
209
210 // zx_status_t zx_vmo_set_cache_policy
sys_vmo_set_cache_policy(zx_handle_t handle,uint32_t cache_policy)211 zx_status_t sys_vmo_set_cache_policy(zx_handle_t handle, uint32_t cache_policy) {
212 fbl::RefPtr<VmObjectDispatcher> vmo;
213 zx_status_t status = ZX_OK;
214 auto up = ProcessDispatcher::GetCurrent();
215
216 // Sanity check the cache policy.
217 if (cache_policy & ~ZX_CACHE_POLICY_MASK) {
218 return ZX_ERR_INVALID_ARGS;
219 }
220
221 // lookup the dispatcher from handle.
222 status = up->GetDispatcherWithRights(handle, ZX_RIGHT_MAP, &vmo);
223 if (status != ZX_OK) {
224 return status;
225 }
226
227 return vmo->SetMappingCachePolicy(cache_policy);
228 }
229
230 // zx_status_t zx_vmo_clone
sys_vmo_clone(zx_handle_t handle,uint32_t options,uint64_t offset,uint64_t size,user_out_handle * out_handle)231 zx_status_t sys_vmo_clone(zx_handle_t handle, uint32_t options,
232 uint64_t offset, uint64_t size,
233 user_out_handle* out_handle) {
234 LTRACEF("handle %x options %#x offset %#" PRIx64 " size %#" PRIx64 "\n",
235 handle, options, offset, size);
236
237 auto up = ProcessDispatcher::GetCurrent();
238
239 zx_status_t status;
240 fbl::RefPtr<VmObject> clone_vmo;
241 zx_rights_t in_rights;
242
243 {
244 // lookup the dispatcher from handle, save a copy of the rights for later
245 fbl::RefPtr<VmObjectDispatcher> vmo;
246 status = up->GetDispatcherWithRights(handle, ZX_RIGHT_DUPLICATE | ZX_RIGHT_READ, &vmo, &in_rights);
247 if (status != ZX_OK)
248 return status;
249
250 // clone the vmo into a new one
251 status = vmo->Clone(options, offset, size, in_rights & ZX_RIGHT_GET_PROPERTY, &clone_vmo);
252 if (status != ZX_OK)
253 return status;
254
255 DEBUG_ASSERT(clone_vmo);
256 }
257
258 // create a Vm Object dispatcher
259 fbl::RefPtr<Dispatcher> dispatcher;
260 zx_rights_t default_rights;
261 zx_status_t result = VmObjectDispatcher::Create(ktl::move(clone_vmo), &dispatcher, &default_rights);
262 if (result != ZX_OK)
263 return result;
264
265 // Set the rights to the new handle to no greater than the input
266 // handle, plus WRITE if making a COW clone, and always allow
267 // GET/SET_PROPERTY so the user can set ZX_PROP_NAME on the new clone.
268 zx_rights_t rights =
269 in_rights | ZX_RIGHT_GET_PROPERTY | ZX_RIGHT_SET_PROPERTY;
270 if (options & ZX_VMO_CLONE_COPY_ON_WRITE)
271 rights |= ZX_RIGHT_WRITE;
272
273 // make sure we're somehow not elevating rights beyond what a new vmo should have
274 DEBUG_ASSERT((default_rights & rights) == rights);
275
276 // create a handle and attach the dispatcher to it
277 return out_handle->make(ktl::move(dispatcher), rights);
278 }
279
280 // zx_status_t zx_vmo_replace_as_executable
sys_vmo_replace_as_executable(zx_handle_t handle,zx_handle_t vmex,user_out_handle * out)281 zx_status_t sys_vmo_replace_as_executable(
282 zx_handle_t handle, zx_handle_t vmex, user_out_handle* out) {
283 LTRACEF("repexec %x %x\n", handle, vmex);
284
285 zx_status_t vmex_status = ZX_OK;
286 if (vmex != ZX_HANDLE_INVALID) {
287 vmex_status = validate_resource(vmex, ZX_RSRC_KIND_VMEX);
288 } else {
289 // TODO(mdempsky): Print warning that VMEX resource is
290 // required, and eventually reject outright.
291 }
292
293 auto up = ProcessDispatcher::GetCurrent();
294
295 Guard<fbl::Mutex> guard{up->handle_table_lock()};
296 auto source = up->GetHandleLocked(handle);
297 if (!source)
298 return ZX_ERR_BAD_HANDLE;
299
300 auto handle_cleanup = fbl::MakeAutoCall([up, handle]() TA_NO_THREAD_SAFETY_ANALYSIS {
301 up->RemoveHandleLocked(handle);
302 });
303
304 if (vmex_status != ZX_OK)
305 return vmex_status;
306 if (source->dispatcher()->get_type() != ZX_OBJ_TYPE_VMO)
307 return ZX_ERR_BAD_HANDLE;
308
309 return out->dup(source, source->rights() | ZX_RIGHT_EXECUTE);
310 }
311