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 <platform.h>
10#include <stdint.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14#include <trace.h>
15
16#include <arch/arch_ops.h>
17
18#include <lib/ktrace.h>
19#include <lib/user_copy/user_ptr.h>
20#include <object/handle.h>
21#include <object/job_dispatcher.h>
22#include <object/process_dispatcher.h>
23#include <object/resource_dispatcher.h>
24#include <object/suspend_token_dispatcher.h>
25#include <object/thread_dispatcher.h>
26#include <object/vm_address_region_dispatcher.h>
27
28#include <fbl/auto_lock.h>
29#include <fbl/inline_array.h>
30#include <fbl/ref_ptr.h>
31#include <fbl/string_piece.h>
32#include <zircon/syscalls/debug.h>
33#include <zircon/syscalls/policy.h>
34
35#include "priv.h"
36
37#define LOCAL_TRACE 0
38#define THREAD_SET_PRIORITY_EXPERIMENT 1
39
40#if THREAD_SET_PRIORITY_EXPERIMENT
41#include <kernel/cmdline.h>
42#include <kernel/thread.h>
43#include <lk/init.h>
44#endif
45
46namespace {
47
48constexpr size_t kMaxDebugReadBlock = 64 * 1024u * 1024u;
49constexpr size_t kMaxDebugWriteBlock = 64 * 1024u * 1024u;
50
51// Assume the typical set-policy call has 8 items or less.
52constexpr size_t kPolicyBasicInlineCount = 8;
53
54#if THREAD_SET_PRIORITY_EXPERIMENT
55// This was initially set to false by default. See ZX-940 for the rationale
56// for being enabled by default.
57bool thread_set_priority_allowed = true;
58void thread_set_priority_experiment_init_hook(uint) {
59    thread_set_priority_allowed = !cmdline_get_bool("thread.set.priority.disable", false);
60    printf("thread set priority experiment is : %s\n",
61           thread_set_priority_allowed ? "ENABLED" : "DISABLED");
62}
63LK_INIT_HOOK(thread_set_priority_experiment,
64             thread_set_priority_experiment_init_hook,
65             LK_INIT_LEVEL_THREADING - 1);
66#endif
67
68// TODO(ZX-1025): copy_user_string may truncate the incoming string,
69// and may copy extra data past the NUL.
70// TODO(dbort): If anyone else needs this, move it into user_ptr.
71zx_status_t copy_user_string(const user_in_ptr<const char>& src,
72                             size_t src_len,
73                             char* buf, size_t buf_len,
74                             fbl::StringPiece* sp) {
75    if (!src || src_len > buf_len) {
76        return ZX_ERR_INVALID_ARGS;
77    }
78    zx_status_t result = src.copy_array_from_user(buf, src_len);
79    if (result != ZX_OK) {
80        return ZX_ERR_INVALID_ARGS;
81    }
82
83    // ensure zero termination
84    size_t str_len = (src_len == buf_len ? src_len - 1 : src_len);
85    buf[str_len] = 0;
86    *sp = fbl::StringPiece(buf);
87
88    return ZX_OK;
89}
90
91// Convenience function to go from process handle to process.
92zx_status_t get_process(ProcessDispatcher* up,
93                        zx_handle_t proc_handle,
94                        fbl::RefPtr<ProcessDispatcher>* proc) {
95    return up->GetDispatcherWithRights(proc_handle, ZX_RIGHT_WRITE, proc);
96}
97
98// This represents the local storage for thread_read/write_state. It should be large enough to
99// handle all structures passed over these APIs.
100union thread_state_local_buffer_t {
101    zx_thread_state_general_regs_t general_regs; // ZX_THREAD_STATE_GENERAL_REGS
102    zx_thread_state_fp_regs_t fp_regs;           // ZX_THREAD_STATE_FP_REGS
103    zx_thread_state_vector_regs_t vector_regs;   // ZX_THREAD_STATE_VECTOR_REGS
104    uint32_t single_step;                        // ZX_THREAD_STATE_SINGLE_STEP
105    uint64_t x86_register_fs;                    // ZX_THREAD_X86_REGISTER_FS;
106    uint64_t x86_register_gs;                    // ZX_THREAD_X86_REGISTER_GS;
107};
108
109// Validates the input topic to thread_read_state and thread_write_state is a valid value, and
110// checks that the input buffer size is at least as large as necessary for the topic. On ZX_OK, the
111// actual size necessary for the buffer will be placed in the output parameter.
112zx_status_t validate_thread_state_input(uint32_t in_topic, size_t in_len, size_t* out_len) {
113    switch (in_topic) {
114    case ZX_THREAD_STATE_GENERAL_REGS:
115        *out_len = sizeof(zx_thread_state_general_regs_t);
116        break;
117    case ZX_THREAD_STATE_FP_REGS:
118        *out_len = sizeof(zx_thread_state_fp_regs_t);
119        break;
120    case ZX_THREAD_STATE_VECTOR_REGS:
121        *out_len = sizeof(zx_thread_state_vector_regs_t);
122        break;
123    case ZX_THREAD_STATE_SINGLE_STEP:
124        *out_len = sizeof(zx_thread_state_single_step_t);
125        break;
126    case ZX_THREAD_X86_REGISTER_FS:
127        *out_len = sizeof(zx_thread_x86_register_fs_t);
128        break;
129    case ZX_THREAD_X86_REGISTER_GS:
130        *out_len = sizeof(zx_thread_x86_register_gs_t);
131        break;
132    default:
133        return ZX_ERR_INVALID_ARGS;
134    }
135
136    if (in_len < *out_len)
137        return ZX_ERR_BUFFER_TOO_SMALL;
138    return ZX_OK;
139}
140
141} // namespace
142
143zx_status_t sys_thread_create(zx_handle_t process_handle,
144                              user_in_ptr<const char> _name, size_t name_len,
145                              uint32_t options, user_out_handle* out) {
146    LTRACEF("process handle %x, options %#x\n", process_handle, options);
147
148    // currently, the only valid option value is 0
149    if (options != 0)
150        return ZX_ERR_INVALID_ARGS;
151
152    // copy out the name
153    char buf[ZX_MAX_NAME_LEN];
154    fbl::StringPiece sp;
155    // Silently truncate the given name.
156    if (name_len > sizeof(buf))
157        name_len = sizeof(buf);
158    zx_status_t result = copy_user_string(_name, name_len,
159                                          buf, sizeof(buf), &sp);
160    if (result != ZX_OK)
161        return result;
162    LTRACEF("name %s\n", buf);
163
164    // convert process handle to process dispatcher
165    auto up = ProcessDispatcher::GetCurrent();
166
167    fbl::RefPtr<ProcessDispatcher> process;
168    result = get_process(up, process_handle, &process);
169    if (result != ZX_OK)
170        return result;
171
172    uint32_t pid = (uint32_t)process->get_koid();
173
174    // create the thread dispatcher
175    fbl::RefPtr<Dispatcher> thread_dispatcher;
176    zx_rights_t thread_rights;
177    result = ThreadDispatcher::Create(fbl::move(process), options, sp,
178                                      &thread_dispatcher, &thread_rights);
179    if (result != ZX_OK)
180        return result;
181
182    uint32_t tid = (uint32_t)thread_dispatcher->get_koid();
183    ktrace(TAG_THREAD_CREATE, tid, pid, 0, 0);
184    ktrace_name(TAG_THREAD_NAME, tid, pid, buf);
185
186    return out->make(fbl::move(thread_dispatcher), thread_rights);
187}
188
189zx_status_t sys_thread_start(zx_handle_t thread_handle, zx_vaddr_t entry,
190                             zx_vaddr_t stack, uintptr_t arg1, uintptr_t arg2) {
191    LTRACEF("handle %x, entry %#" PRIxPTR ", sp %#" PRIxPTR
192            ", arg1 %#" PRIxPTR ", arg2 %#" PRIxPTR "\n",
193            thread_handle, entry, stack, arg1, arg2);
194
195    auto up = ProcessDispatcher::GetCurrent();
196
197    fbl::RefPtr<ThreadDispatcher> thread;
198    zx_status_t status = up->GetDispatcherWithRights(thread_handle, ZX_RIGHT_MANAGE_THREAD,
199                                                     &thread);
200    if (status != ZX_OK) {
201        // Try again, but with the WRITE right.
202        // TODO(kulakowski) Remove this when all callers are using MANAGE_THREAD.
203        status = up->GetDispatcherWithRights(thread_handle, ZX_RIGHT_WRITE, &thread);
204        if (status != ZX_OK)
205            return status;
206    }
207
208    ktrace(TAG_THREAD_START, (uint32_t)thread->get_koid(), 0, 0, 0);
209    return thread->Start(entry, stack, arg1, arg2, /* initial_thread= */ false);
210}
211
212void sys_thread_exit() {
213    LTRACE_ENTRY;
214    ThreadDispatcher::GetCurrent()->Exit();
215}
216
217zx_status_t sys_thread_read_state(zx_handle_t handle, uint32_t state_kind,
218                                  user_out_ptr<void> _buffer, size_t buffer_len) {
219    LTRACEF("handle %x, state_kind %u\n", handle, state_kind);
220
221    auto up = ProcessDispatcher::GetCurrent();
222
223    // TODO(ZX-968): debug rights
224    fbl::RefPtr<ThreadDispatcher> thread;
225    zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_READ, &thread);
226    if (status != ZX_OK)
227        return status;
228
229    thread_state_local_buffer_t local_buffer;
230    size_t local_buffer_len = 0;
231    status = validate_thread_state_input(state_kind, buffer_len, &local_buffer_len);
232    if (status != ZX_OK)
233        return status;
234
235    status = thread->ReadState(static_cast<zx_thread_state_topic_t>(state_kind), &local_buffer,
236                               local_buffer_len);
237    if (status != ZX_OK)
238        return status;
239
240    if (_buffer.copy_array_to_user(&local_buffer, local_buffer_len) != ZX_OK)
241        return ZX_ERR_INVALID_ARGS;
242    return ZX_OK;
243}
244
245zx_status_t sys_thread_write_state(zx_handle_t handle, uint32_t state_kind,
246                                   user_in_ptr<const void> _buffer, size_t buffer_len) {
247    LTRACEF("handle %x, state_kind %u\n", handle, state_kind);
248
249    auto up = ProcessDispatcher::GetCurrent();
250
251    // TODO(ZX-968): debug rights
252    fbl::RefPtr<ThreadDispatcher> thread;
253    zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_WRITE, &thread);
254    if (status != ZX_OK)
255        return status;
256
257    thread_state_local_buffer_t local_buffer;
258    size_t local_buffer_len = 0;
259    status = validate_thread_state_input(state_kind, buffer_len, &local_buffer_len);
260    if (status != ZX_OK)
261        return status;
262
263    // Additionally check that the buffer is the exact size expected (validate only checks it's
264    // larger, which is sufficient for reading).
265    if (local_buffer_len != buffer_len)
266        return ZX_ERR_INVALID_ARGS;
267
268    status = _buffer.copy_array_from_user(&local_buffer, local_buffer_len);
269    if (status != ZX_OK)
270        return ZX_ERR_INVALID_ARGS;
271
272    return thread->WriteState(static_cast<zx_thread_state_topic_t>(state_kind), &local_buffer,
273                              local_buffer_len);
274}
275
276// See ZX-940
277zx_status_t sys_thread_set_priority(int32_t prio) {
278#if THREAD_SET_PRIORITY_EXPERIMENT
279    // If the experimental zx_thread_set_priority has not been enabled using the
280    // kernel command line option, simply deny this request.
281    if (!thread_set_priority_allowed)
282        return ZX_ERR_NOT_SUPPORTED;
283
284    if ((prio < LOWEST_PRIORITY) || (prio > HIGHEST_PRIORITY))
285        return ZX_ERR_INVALID_ARGS;
286
287    thread_set_priority(get_current_thread(), prio);
288
289    return ZX_OK;
290#else
291    return ZX_ERR_NOT_SUPPORTED;
292#endif
293}
294
295zx_status_t sys_task_suspend(zx_handle_t task_handle, user_out_handle* token) {
296    LTRACE_ENTRY;
297
298    auto up = ProcessDispatcher::GetCurrent();
299
300    // TODO(ZX-1840): Add support for tasks other than threads
301    fbl::RefPtr<ThreadDispatcher> thread;
302    zx_status_t status = up->GetDispatcherWithRights(task_handle, ZX_RIGHT_WRITE,
303                                                     &thread);
304    if (status != ZX_OK)
305        return status;
306
307    fbl::RefPtr<Dispatcher> suspend_token;
308    zx_rights_t rights;
309    status = SuspendTokenDispatcher::Create(fbl::move(thread), &suspend_token, &rights);
310    if (status == ZX_OK)
311        status = token->make(fbl::move(suspend_token), rights);
312    return status;
313}
314
315zx_status_t sys_task_suspend_token(zx_handle_t task_handle, user_out_handle* token) {
316    return sys_task_suspend(task_handle, token);
317}
318
319zx_status_t sys_process_create(zx_handle_t job_handle,
320                               user_in_ptr<const char> _name, size_t name_len,
321                               uint32_t options,
322                               user_out_handle* proc_handle,
323                               user_out_handle* vmar_handle) {
324    LTRACEF("job handle %x, options %#x\n", job_handle, options);
325
326    // currently, the only valid option value is 0
327    if (options != 0)
328        return ZX_ERR_INVALID_ARGS;
329
330    auto up = ProcessDispatcher::GetCurrent();
331
332    // We check the policy against the process calling zx_process_create, which
333    // is the operative policy, rather than against |job_handle|. Access to
334    // |job_handle| is controlled by the rights associated with the handle.
335    zx_status_t result = up->QueryPolicy(ZX_POL_NEW_PROCESS);
336    if (result != ZX_OK)
337        return result;
338
339    // copy out the name
340    char buf[ZX_MAX_NAME_LEN];
341    fbl::StringPiece sp;
342    // Silently truncate the given name.
343    if (name_len > sizeof(buf))
344        name_len = sizeof(buf);
345    result = copy_user_string(_name, name_len, buf, sizeof(buf), &sp);
346    if (result != ZX_OK)
347        return result;
348    LTRACEF("name %s\n", buf);
349
350    fbl::RefPtr<JobDispatcher> job;
351    // TODO(ZX-968): define process creation job rights.
352    auto status = up->GetDispatcherWithRights(job_handle, ZX_RIGHT_WRITE, &job);
353    if (status != ZX_OK)
354        return status;
355
356    // create a new process dispatcher
357    fbl::RefPtr<Dispatcher> proc_dispatcher;
358    fbl::RefPtr<VmAddressRegionDispatcher> vmar_dispatcher;
359    zx_rights_t proc_rights, vmar_rights;
360    result = ProcessDispatcher::Create(fbl::move(job), sp, options,
361                                       &proc_dispatcher, &proc_rights,
362                                       &vmar_dispatcher, &vmar_rights);
363    if (result != ZX_OK)
364        return result;
365
366    uint32_t koid = (uint32_t)proc_dispatcher->get_koid();
367    ktrace(TAG_PROC_CREATE, koid, 0, 0, 0);
368    ktrace_name(TAG_PROC_NAME, koid, 0, buf);
369
370    // Give arch-specific tracing a chance to record process creation.
371    arch_trace_process_create(koid, vmar_dispatcher->vmar()->aspace()->arch_aspace().arch_table_phys());
372
373    result = proc_handle->make(fbl::move(proc_dispatcher), proc_rights);
374    if (result == ZX_OK)
375        result = vmar_handle->make(fbl::move(vmar_dispatcher), vmar_rights);
376    return result;
377}
378
379// Note: This is used to start the main thread (as opposed to using
380// sys_thread_start for that) for a few reasons:
381// - less easily exploitable
382//   We want to make sure we can't generically transfer handles to a process.
383//   This has the nice property of restricting the evil (transferring handle
384//   to new process) to exactly one spot, and can be called exactly once per
385//   process, since it also pushes it into a new state.
386// - maintains the state machine invariant that 'started' processes have one
387//   thread running
388
389zx_status_t sys_process_start(zx_handle_t process_handle, zx_handle_t thread_handle,
390                              zx_vaddr_t pc, zx_vaddr_t sp,
391                              zx_handle_t arg_handle_value, uintptr_t arg2) {
392    LTRACEF("phandle %x, thandle %x, pc %#" PRIxPTR ", sp %#" PRIxPTR
393            ", arg_handle %x, arg2 %#" PRIxPTR "\n",
394            process_handle, thread_handle, pc, sp, arg_handle_value, arg2);
395
396    auto up = ProcessDispatcher::GetCurrent();
397
398    // get process dispatcher
399    fbl::RefPtr<ProcessDispatcher> process;
400    zx_status_t status = get_process(up, process_handle, &process);
401    if (status != ZX_OK) {
402        up->RemoveHandle(arg_handle_value);
403        return status;
404    }
405
406    // get thread_dispatcher
407    fbl::RefPtr<ThreadDispatcher> thread;
408    status = up->GetDispatcherWithRights(thread_handle, ZX_RIGHT_WRITE, &thread);
409    if (status != ZX_OK) {
410        up->RemoveHandle(arg_handle_value);
411        return status;
412    }
413
414    HandleOwner arg_handle = up->RemoveHandle(arg_handle_value);
415
416    // test that the thread belongs to the starting process
417    if (thread->process() != process.get())
418        return ZX_ERR_ACCESS_DENIED;
419    if (!arg_handle)
420        return ZX_ERR_BAD_HANDLE;
421    if (!arg_handle->HasRights(ZX_RIGHT_TRANSFER))
422        return ZX_ERR_ACCESS_DENIED;
423
424    auto arg_nhv = process->MapHandleToValue(arg_handle);
425    process->AddHandle(fbl::move(arg_handle));
426
427    status = thread->Start(pc, sp, static_cast<uintptr_t>(arg_nhv),
428                           arg2, /* initial_thread */ true);
429    if (status != ZX_OK) {
430        // Remove |arg_handle| from the process that failed to start.
431        process->RemoveHandle(arg_nhv);
432        return status;
433    }
434
435    ktrace(TAG_PROC_START, (uint32_t)thread->get_koid(),
436           (uint32_t)process->get_koid(), 0, 0);
437
438    return ZX_OK;
439}
440
441void sys_process_exit(int64_t retcode) {
442    LTRACEF("retcode %" PRId64 "\n", retcode);
443    ProcessDispatcher::GetCurrent()->Exit(retcode);
444}
445
446zx_status_t sys_process_read_memory(zx_handle_t proc, zx_vaddr_t vaddr,
447                                    user_out_ptr<void> _buffer,
448                                    size_t len, user_out_ptr<size_t> _actual) {
449    LTRACEF("vaddr 0x%" PRIxPTR ", size %zu\n", vaddr, len);
450
451    if (!_buffer)
452        return ZX_ERR_INVALID_ARGS;
453    if (len == 0 || len > kMaxDebugReadBlock)
454        return ZX_ERR_INVALID_ARGS;
455
456    auto up = ProcessDispatcher::GetCurrent();
457
458    fbl::RefPtr<ProcessDispatcher> process;
459    zx_status_t status = up->GetDispatcherWithRights(proc, ZX_RIGHT_READ | ZX_RIGHT_WRITE,
460                                                     &process);
461    if (status != ZX_OK)
462        return status;
463
464    auto aspace = process->aspace();
465    if (!aspace)
466        return ZX_ERR_BAD_STATE;
467
468    auto region = aspace->FindRegion(vaddr);
469    if (!region)
470        return ZX_ERR_NO_MEMORY;
471
472    auto vm_mapping = region->as_vm_mapping();
473    if (!vm_mapping)
474        return ZX_ERR_NO_MEMORY;
475
476    auto vmo = vm_mapping->vmo();
477    if (!vmo)
478        return ZX_ERR_NO_MEMORY;
479
480    // Force map the range, even if it crosses multiple mappings.
481    // TODO(ZX-730): This is a workaround for this bug.  If we start decommitting
482    // things, the bug will come back.  We should fix this more properly.
483    {
484        uint8_t byte = 0;
485        auto int_data = _buffer.reinterpret<uint8_t>();
486        for (size_t i = 0; i < len; i += PAGE_SIZE) {
487            status = int_data.copy_array_to_user(&byte, 1, i);
488            if (status != ZX_OK) {
489                return status;
490            }
491        }
492        if (len > 0) {
493            status = int_data.copy_array_to_user(&byte, 1, len - 1);
494            if (status != ZX_OK) {
495                return status;
496            }
497        }
498    }
499
500    uint64_t offset = vaddr - vm_mapping->base() + vm_mapping->object_offset();
501    // TODO(ZX-1631): While this limits reading to the mapped address space of
502    // this VMO, it should be reading from multiple VMOs, not a single one.
503    // Additionally, it is racy with the mapping going away.
504    len = MIN(len, vm_mapping->size() - (vaddr - vm_mapping->base()));
505    zx_status_t st = vmo->ReadUser(_buffer, offset, len);
506
507    if (st == ZX_OK) {
508        zx_status_t status = _actual.copy_to_user(static_cast<size_t>(len));
509        if (status != ZX_OK)
510            return status;
511    }
512    return st;
513}
514
515zx_status_t sys_process_write_memory(zx_handle_t proc, zx_vaddr_t vaddr,
516                                     user_in_ptr<const void> _buffer,
517                                     size_t len, user_out_ptr<size_t> _actual) {
518    LTRACEF("vaddr 0x%" PRIxPTR ", size %zu\n", vaddr, len);
519
520    if (!_buffer)
521        return ZX_ERR_INVALID_ARGS;
522    if (len == 0 || len > kMaxDebugWriteBlock)
523        return ZX_ERR_INVALID_ARGS;
524
525    auto up = ProcessDispatcher::GetCurrent();
526
527    fbl::RefPtr<ProcessDispatcher> process;
528    zx_status_t status = up->GetDispatcherWithRights(proc, ZX_RIGHT_WRITE, &process);
529    if (status != ZX_OK)
530        return status;
531
532    auto aspace = process->aspace();
533    if (!aspace)
534        return ZX_ERR_BAD_STATE;
535
536    auto region = aspace->FindRegion(vaddr);
537    if (!region)
538        return ZX_ERR_NO_MEMORY;
539
540    auto vm_mapping = region->as_vm_mapping();
541    if (!vm_mapping)
542        return ZX_ERR_NO_MEMORY;
543
544    auto vmo = vm_mapping->vmo();
545    if (!vmo)
546        return ZX_ERR_NO_MEMORY;
547
548    // Force map the range, even if it crosses multiple mappings.
549    // TODO(ZX-730): This is a workaround for this bug.  If we start decommitting
550    // things, the bug will come back.  We should fix this more properly.
551    {
552        uint8_t byte = 0;
553        auto int_data = _buffer.reinterpret<const uint8_t>();
554        for (size_t i = 0; i < len; i += PAGE_SIZE) {
555            status = int_data.copy_array_from_user(&byte, 1, i);
556            if (status != ZX_OK) {
557                return status;
558            }
559        }
560        if (len > 0) {
561            status = int_data.copy_array_from_user(&byte, 1, len - 1);
562            if (status != ZX_OK) {
563                return status;
564            }
565        }
566    }
567
568    uint64_t offset = vaddr - vm_mapping->base() + vm_mapping->object_offset();
569    // TODO(ZX-1631): While this limits writing to the mapped address space of
570    // this VMO, it should be writing to multiple VMOs, not a single one.
571    // Additionally, it is racy with the mapping going away.
572    len = MIN(len, vm_mapping->size() - (vaddr - vm_mapping->base()));
573    zx_status_t st = vmo->WriteUser(_buffer, offset, len);
574
575    if (st == ZX_OK) {
576        zx_status_t status = _actual.copy_to_user(static_cast<size_t>(len));
577        if (status != ZX_OK)
578            return status;
579    }
580    return st;
581}
582
583// helper routine for sys_task_kill
584template <typename T>
585static zx_status_t kill_task(fbl::RefPtr<Dispatcher> dispatcher) {
586    auto task = DownCastDispatcher<T>(&dispatcher);
587    if (!task)
588        return ZX_ERR_WRONG_TYPE;
589
590    task->Kill();
591    return ZX_OK;
592}
593
594zx_status_t sys_task_kill(zx_handle_t task_handle) {
595    LTRACEF("handle %x\n", task_handle);
596
597    auto up = ProcessDispatcher::GetCurrent();
598
599    fbl::RefPtr<Dispatcher> dispatcher;
600    auto status = up->GetDispatcherWithRights(task_handle, ZX_RIGHT_DESTROY, &dispatcher);
601    if (status != ZX_OK)
602        return status;
603
604    // see if it's a process or thread and dispatch accordingly
605    switch (dispatcher->get_type()) {
606    case ZX_OBJ_TYPE_PROCESS:
607        return kill_task<ProcessDispatcher>(fbl::move(dispatcher));
608    case ZX_OBJ_TYPE_THREAD:
609        return kill_task<ThreadDispatcher>(fbl::move(dispatcher));
610    case ZX_OBJ_TYPE_JOB:
611        return kill_task<JobDispatcher>(fbl::move(dispatcher));
612    default:
613        return ZX_ERR_WRONG_TYPE;
614    }
615}
616
617zx_status_t sys_job_create(zx_handle_t parent_job, uint32_t options,
618                           user_out_handle* out) {
619    LTRACEF("parent: %x\n", parent_job);
620
621    if (options != 0u)
622        return ZX_ERR_INVALID_ARGS;
623
624    auto up = ProcessDispatcher::GetCurrent();
625
626    fbl::RefPtr<JobDispatcher> parent;
627    zx_status_t status = up->GetDispatcherWithRights(parent_job, ZX_RIGHT_MANAGE_JOB, &parent);
628    if (status != ZX_OK) {
629        // Try again, but with the WRITE right.
630        // TODO(kulakowski) Remove this when all callers are using MANAGE_JOB.
631        status = up->GetDispatcherWithRights(parent_job, ZX_RIGHT_WRITE, &parent);
632        if (status != ZX_OK) {
633            return status;
634        }
635    }
636
637    fbl::RefPtr<Dispatcher> job;
638    zx_rights_t rights;
639    status = JobDispatcher::Create(options, fbl::move(parent), &job, &rights);
640    if (status == ZX_OK)
641        status = out->make(fbl::move(job), rights);
642    return status;
643}
644
645zx_status_t sys_job_set_policy(zx_handle_t job_handle, uint32_t options,
646                               uint32_t topic, user_in_ptr<const void> _policy,
647                               uint32_t count) {
648
649    if ((options != ZX_JOB_POL_RELATIVE) && (options != ZX_JOB_POL_ABSOLUTE))
650        return ZX_ERR_INVALID_ARGS;
651    if (!_policy || (count == 0u))
652        return ZX_ERR_INVALID_ARGS;
653
654    if (topic != ZX_JOB_POL_BASIC)
655        return ZX_ERR_INVALID_ARGS;
656
657    fbl::AllocChecker ac;
658    fbl::InlineArray<
659        zx_policy_basic, kPolicyBasicInlineCount>
660        policy(&ac, count);
661    if (!ac.check())
662        return ZX_ERR_NO_MEMORY;
663
664    auto status = _policy.copy_array_from_user(policy.get(), sizeof(zx_policy_basic) * count);
665    if (status != ZX_OK)
666        return ZX_ERR_INVALID_ARGS;
667
668    auto up = ProcessDispatcher::GetCurrent();
669
670    fbl::RefPtr<JobDispatcher> job;
671    status = up->GetDispatcherWithRights(job_handle, ZX_RIGHT_SET_POLICY, &job);
672    if (status != ZX_OK)
673        return status;
674
675    return job->SetPolicy(options, policy.get(), policy.size());
676}
677