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