| // Copyright 2016 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include <err.h> |
| #include <inttypes.h> |
| #include <platform.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <trace.h> |
| |
| #include <arch/arch_ops.h> |
| |
| #include <lib/ktrace.h> |
| #include <lib/user_copy/user_ptr.h> |
| #include <object/handle_owner.h> |
| #include <object/handles.h> |
| #include <object/job_dispatcher.h> |
| #include <object/process_dispatcher.h> |
| #include <object/resource_dispatcher.h> |
| #include <object/thread_dispatcher.h> |
| #include <object/vm_address_region_dispatcher.h> |
| |
| #include <zircon/syscalls/debug.h> |
| #include <zircon/syscalls/policy.h> |
| #include <fbl/auto_lock.h> |
| #include <fbl/inline_array.h> |
| #include <fbl/ref_ptr.h> |
| #include <fbl/string_piece.h> |
| |
| #include "syscalls_priv.h" |
| |
| #define LOCAL_TRACE 0 |
| #define THREAD_SET_PRIORITY_EXPERIMENT 1 |
| |
| #if THREAD_SET_PRIORITY_EXPERIMENT |
| #include <kernel/cmdline.h> |
| #include <kernel/thread.h> |
| #include <lk/init.h> |
| #endif |
| |
| // For reading general purpose integer registers, we can allocate in |
| // an inline array and save the malloc. Assume 64 registers as a |
| // conservative estimate for an architecture with 32 general purpose |
| // integer registers. |
| constexpr uint32_t kInlineThreadStateSize = sizeof(void*) * 64; |
| constexpr uint32_t kMaxThreadStateSize = ZX_MAX_THREAD_STATE_SIZE; |
| |
| constexpr size_t kMaxDebugReadBlock = 64 * 1024u * 1024u; |
| constexpr size_t kMaxDebugWriteBlock = 64 * 1024u * 1024u; |
| |
| // Assume the typical set-policy call has 8 items or less. |
| constexpr size_t kPolicyBasicInlineCount = 8; |
| |
| #if THREAD_SET_PRIORITY_EXPERIMENT |
| // See ZX-940 |
| static bool thread_set_priority_allowed = false; |
| static void thread_set_priority_experiment_init_hook(uint) { |
| thread_set_priority_allowed = cmdline_get_bool("thread.set.priority.allowed", false); |
| printf("thread set priority experiment is : %s\n", |
| thread_set_priority_allowed ? "ENABLED" : "DISABLED"); |
| } |
| LK_INIT_HOOK(thread_set_priority_experiment, |
| thread_set_priority_experiment_init_hook, |
| LK_INIT_LEVEL_THREADING - 1); |
| #endif |
| |
| // TODO(ZX-1025): copy_user_string may truncate the incoming string, |
| // and may copy extra data past the NUL. |
| // TODO(dbort): If anyone else needs this, move it into user_ptr. |
| static zx_status_t copy_user_string(const user_ptr<const char>& src, |
| size_t src_len, |
| char* buf, size_t buf_len, |
| fbl::StringPiece* sp) { |
| if (!src || src_len > buf_len) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| zx_status_t result = src.copy_array_from_user(buf, src_len); |
| if (result != ZX_OK) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| // ensure zero termination |
| size_t str_len = (src_len == buf_len ? src_len - 1 : src_len); |
| buf[str_len] = 0; |
| *sp = fbl::StringPiece(buf); |
| |
| return ZX_OK; |
| } |
| |
| // Convenience function to go from process handle to process. |
| static zx_status_t get_process(ProcessDispatcher* up, |
| zx_handle_t proc_handle, |
| fbl::RefPtr<ProcessDispatcher>* proc) { |
| return up->GetDispatcherWithRights(proc_handle, ZX_RIGHT_WRITE, proc); |
| } |
| |
| zx_status_t sys_thread_create(zx_handle_t process_handle, |
| user_ptr<const char> _name, uint32_t name_len, |
| uint32_t options, user_ptr<zx_handle_t> _out) { |
| LTRACEF("process handle %x, options %#x\n", process_handle, options); |
| |
| // currently, the only valid option value is 0 |
| if (options != 0) |
| return ZX_ERR_INVALID_ARGS; |
| |
| // copy out the name |
| char buf[ZX_MAX_NAME_LEN]; |
| fbl::StringPiece sp; |
| // Silently truncate the given name. |
| if (name_len > sizeof(buf)) |
| name_len = sizeof(buf); |
| zx_status_t result = copy_user_string(_name, name_len, |
| buf, sizeof(buf), &sp); |
| if (result != ZX_OK) |
| return result; |
| LTRACEF("name %s\n", buf); |
| |
| // convert process handle to process dispatcher |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| fbl::RefPtr<ProcessDispatcher> process; |
| result = get_process(up, process_handle, &process); |
| if (result != ZX_OK) |
| return result; |
| |
| uint32_t pid = (uint32_t)process->get_koid(); |
| |
| // create the thread dispatcher |
| fbl::RefPtr<Dispatcher> thread_dispatcher; |
| zx_rights_t thread_rights; |
| result = ThreadDispatcher::Create(fbl::move(process), options, sp, |
| &thread_dispatcher, &thread_rights); |
| if (result != ZX_OK) |
| return result; |
| |
| uint32_t tid = (uint32_t)thread_dispatcher->get_koid(); |
| ktrace(TAG_THREAD_CREATE, tid, pid, 0, 0); |
| ktrace_name(TAG_THREAD_NAME, tid, pid, buf); |
| |
| HandleOwner handle(MakeHandle(fbl::move(thread_dispatcher), thread_rights)); |
| if (!handle) |
| return ZX_ERR_NO_MEMORY; |
| |
| if (_out.copy_to_user(up->MapHandleToValue(handle)) != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| up->AddHandle(fbl::move(handle)); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t sys_thread_start(zx_handle_t thread_handle, uintptr_t entry, |
| uintptr_t stack, uintptr_t arg1, uintptr_t arg2) { |
| LTRACEF("handle %x, entry %#" PRIxPTR ", sp %#" PRIxPTR |
| ", arg1 %#" PRIxPTR ", arg2 %#" PRIxPTR "\n", |
| thread_handle, entry, stack, arg1, arg2); |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| fbl::RefPtr<ThreadDispatcher> thread; |
| zx_status_t status = up->GetDispatcherWithRights(thread_handle, ZX_RIGHT_WRITE, |
| &thread); |
| if (status != ZX_OK) |
| return status; |
| |
| ktrace(TAG_THREAD_START, (uint32_t)thread->get_koid(), 0, 0, 0); |
| return thread->Start(entry, stack, arg1, arg2, /* initial_thread= */ false); |
| } |
| |
| void sys_thread_exit() { |
| LTRACE_ENTRY; |
| ThreadDispatcher::GetCurrent()->Exit(); |
| } |
| |
| zx_status_t sys_thread_read_state(zx_handle_t handle, uint32_t state_kind, |
| user_ptr<void> _buffer, |
| uint32_t buffer_len, user_ptr<uint32_t> _actual) { |
| LTRACEF("handle %x, state_kind %u\n", handle, state_kind); |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| // TODO(ZX-968): debug rights |
| fbl::RefPtr<ThreadDispatcher> thread; |
| zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_READ, &thread); |
| if (status != ZX_OK) |
| return status; |
| |
| // avoid malloc'ing insane amounts |
| if (buffer_len > kMaxThreadStateSize) |
| return ZX_ERR_INVALID_ARGS; |
| |
| fbl::AllocChecker ac; |
| fbl::InlineArray<uint8_t, kInlineThreadStateSize> bytes(&ac, buffer_len); |
| if (!ac.check()) |
| return ZX_ERR_NO_MEMORY; |
| |
| status = thread->ReadState(state_kind, bytes.get(), &buffer_len); |
| |
| // Always set the actual size so the caller can provide larger buffers. |
| // The value is only usable if the status is ZX_OK or ZX_ERR_BUFFER_TOO_SMALL. |
| if (status == ZX_OK || status == ZX_ERR_BUFFER_TOO_SMALL) { |
| if (_actual.copy_to_user(buffer_len) != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| } |
| |
| if (status != ZX_OK) |
| return status; |
| |
| if (_buffer.copy_array_to_user(bytes.get(), buffer_len) != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t sys_thread_write_state(zx_handle_t handle, uint32_t state_kind, |
| user_ptr<const void> _buffer, uint32_t buffer_len) { |
| LTRACEF("handle %x, state_kind %u\n", handle, state_kind); |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| // TODO(ZX-968): debug rights |
| fbl::RefPtr<ThreadDispatcher> thread; |
| zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_WRITE, &thread); |
| if (status != ZX_OK) |
| return status; |
| |
| // avoid malloc'ing insane amounts |
| if (buffer_len > kMaxThreadStateSize) |
| return ZX_ERR_INVALID_ARGS; |
| |
| fbl::AllocChecker ac; |
| fbl::InlineArray<uint8_t, kInlineThreadStateSize> bytes(&ac, buffer_len); |
| if (!ac.check()) |
| return ZX_ERR_NO_MEMORY; |
| |
| status = _buffer.copy_array_from_user(bytes.get(), buffer_len); |
| if (status != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| |
| status = thread->WriteState(state_kind, bytes.get(), buffer_len); |
| return status; |
| } |
| |
| // See ZX-940 |
| zx_status_t sys_thread_set_priority(int32_t prio) { |
| #if THREAD_SET_PRIORITY_EXPERIMENT |
| // If the experimental zx_thread_set_priority has not been enabled using the |
| // kernel command line option, simply deny this request. |
| if (!thread_set_priority_allowed) |
| return ZX_ERR_NOT_SUPPORTED; |
| |
| if ((prio < LOWEST_PRIORITY) || (prio > HIGHEST_PRIORITY)) |
| return ZX_ERR_INVALID_ARGS; |
| |
| thread_set_priority(prio); |
| |
| return ZX_OK; |
| #else |
| return ZX_ERR_NOT_SUPPORTED; |
| #endif |
| } |
| |
| zx_status_t sys_task_suspend(zx_handle_t task_handle) { |
| LTRACE_ENTRY; |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| // TODO(teisenbe): Add support for tasks other than threads |
| fbl::RefPtr<ThreadDispatcher> thread; |
| zx_status_t status = up->GetDispatcherWithRights(task_handle, ZX_RIGHT_WRITE, |
| &thread); |
| if (status != ZX_OK) |
| return status; |
| |
| return thread->Suspend(); |
| } |
| |
| zx_status_t sys_process_create(zx_handle_t job_handle, |
| user_ptr<const char> _name, uint32_t name_len, |
| uint32_t options, user_ptr<zx_handle_t> _proc_handle, |
| user_ptr<zx_handle_t> _vmar_handle) { |
| LTRACEF("job handle %x, options %#x\n", job_handle, options); |
| |
| // currently, the only valid option value is 0 |
| if (options != 0) |
| return ZX_ERR_INVALID_ARGS; |
| |
| // copy out the name |
| char buf[ZX_MAX_NAME_LEN]; |
| fbl::StringPiece sp; |
| // Silently truncate the given name. |
| if (name_len > sizeof(buf)) |
| name_len = sizeof(buf); |
| zx_status_t result = copy_user_string(_name, name_len, |
| buf, sizeof(buf), &sp); |
| if (result != ZX_OK) |
| return result; |
| LTRACEF("name %s\n", buf); |
| |
| // convert job handle to job dispatcher |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| fbl::RefPtr<JobDispatcher> job; |
| // TODO(ZX-968): define process creation job rights. |
| auto status = up->GetDispatcherWithRights(job_handle, ZX_RIGHT_WRITE, &job); |
| if (status != ZX_OK) |
| return status; |
| |
| // create a new process dispatcher |
| fbl::RefPtr<Dispatcher> proc_dispatcher; |
| fbl::RefPtr<VmAddressRegionDispatcher> vmar_dispatcher; |
| zx_rights_t proc_rights, vmar_rights; |
| zx_status_t res = ProcessDispatcher::Create(fbl::move(job), sp, options, |
| &proc_dispatcher, &proc_rights, |
| &vmar_dispatcher, &vmar_rights); |
| if (res != ZX_OK) |
| return res; |
| |
| uint32_t koid = (uint32_t)proc_dispatcher->get_koid(); |
| ktrace(TAG_PROC_CREATE, koid, 0, 0, 0); |
| ktrace_name(TAG_PROC_NAME, koid, 0, buf); |
| |
| // Give arch-specific tracing a chance to record process creation. |
| arch_trace_process_create(koid, vmar_dispatcher->vmar()->aspace()->arch_aspace().arch_table_phys()); |
| |
| // Create a handle and attach the dispatcher to it |
| HandleOwner proc_h(MakeHandle(fbl::move(proc_dispatcher), proc_rights)); |
| if (!proc_h) |
| return ZX_ERR_NO_MEMORY; |
| |
| // Create a handle and attach the dispatcher to it |
| HandleOwner vmar_h(MakeHandle(fbl::move(vmar_dispatcher), vmar_rights)); |
| if (!vmar_h) |
| return ZX_ERR_NO_MEMORY; |
| |
| if (_proc_handle.copy_to_user(up->MapHandleToValue(proc_h)) != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| |
| if (_vmar_handle.copy_to_user(up->MapHandleToValue(vmar_h)) != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| |
| up->AddHandle(fbl::move(vmar_h)); |
| up->AddHandle(fbl::move(proc_h)); |
| |
| return ZX_OK; |
| } |
| |
| // Note: This is used to start the main thread (as opposed to using |
| // sys_thread_start for that) for a few reasons: |
| // - less easily exploitable |
| // We want to make sure we can't generically transfer handles to a process. |
| // This has the nice property of restricting the evil (transferring handle |
| // to new process) to exactly one spot, and can be called exactly once per |
| // process, since it also pushes it into a new state. |
| // - maintains the state machine invariant that 'started' processes have one |
| // thread running |
| |
| zx_status_t sys_process_start(zx_handle_t process_handle, zx_handle_t thread_handle, |
| uintptr_t pc, uintptr_t sp, |
| zx_handle_t arg_handle_value, uintptr_t arg2) { |
| LTRACEF("phandle %x, thandle %x, pc %#" PRIxPTR ", sp %#" PRIxPTR |
| ", arg_handle %x, arg2 %#" PRIxPTR "\n", |
| process_handle, thread_handle, pc, sp, arg_handle_value, arg2); |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| // get process dispatcher |
| fbl::RefPtr<ProcessDispatcher> process; |
| zx_status_t status = get_process(up, process_handle, &process); |
| if (status != ZX_OK) |
| return status; |
| |
| // get thread_dispatcher |
| fbl::RefPtr<ThreadDispatcher> thread; |
| status = up->GetDispatcherWithRights(thread_handle, ZX_RIGHT_WRITE, &thread); |
| if (status != ZX_OK) |
| return status; |
| |
| // test that the thread belongs to the starting process |
| if (thread->process() != process.get()) |
| return ZX_ERR_ACCESS_DENIED; |
| |
| HandleOwner arg_handle; |
| { |
| fbl::AutoLock lock(up->handle_table_lock()); |
| auto handle = up->GetHandleLocked(arg_handle_value); |
| if (!handle) |
| return ZX_ERR_BAD_HANDLE; |
| if (!handle->HasRights(ZX_RIGHT_TRANSFER)) |
| return ZX_ERR_ACCESS_DENIED; |
| arg_handle = up->RemoveHandleLocked(arg_handle_value); |
| } |
| |
| auto arg_nhv = process->MapHandleToValue(arg_handle); |
| process->AddHandle(fbl::move(arg_handle)); |
| |
| status = thread->Start(pc, sp, arg_nhv, arg2, /* initial_thread */ true); |
| if (status != ZX_OK) { |
| // Put back the |arg_handle| into the calling process. |
| auto handle = process->RemoveHandle(arg_nhv); |
| up->AddHandle(fbl::move(handle)); |
| return status; |
| } |
| |
| ktrace(TAG_PROC_START, (uint32_t)thread->get_koid(), |
| (uint32_t)process->get_koid(), 0, 0); |
| |
| return ZX_OK; |
| } |
| |
| void sys_process_exit(int retcode) { |
| LTRACEF("retcode %d\n", retcode); |
| ProcessDispatcher::GetCurrent()->Exit(retcode); |
| } |
| |
| zx_status_t sys_process_read_memory(zx_handle_t proc, uintptr_t vaddr, |
| user_ptr<void> _buffer, |
| size_t len, user_ptr<size_t> _actual) { |
| LTRACEF("vaddr 0x%" PRIxPTR ", size %zu\n", vaddr, len); |
| |
| if (!_buffer) |
| return ZX_ERR_INVALID_ARGS; |
| if (len == 0 || len > kMaxDebugReadBlock) |
| return ZX_ERR_INVALID_ARGS; |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| fbl::RefPtr<ProcessDispatcher> process; |
| zx_status_t status = up->GetDispatcherWithRights(proc, ZX_RIGHT_READ | ZX_RIGHT_WRITE, |
| &process); |
| if (status != ZX_OK) |
| return status; |
| |
| auto aspace = process->aspace(); |
| if (!aspace) |
| return ZX_ERR_BAD_STATE; |
| |
| auto region = aspace->FindRegion(vaddr); |
| if (!region) |
| return ZX_ERR_NO_MEMORY; |
| |
| auto vm_mapping = region->as_vm_mapping(); |
| if (!vm_mapping) |
| return ZX_ERR_NO_MEMORY; |
| |
| auto vmo = vm_mapping->vmo(); |
| if (!vmo) |
| return ZX_ERR_NO_MEMORY; |
| |
| uint64_t offset = vaddr - vm_mapping->base() + vm_mapping->object_offset(); |
| size_t read = 0; |
| |
| // Force map the range, even if it crosses multiple mappings. |
| // TODO(ZX-730): This is a workaround for this bug. If we start decommitting |
| // things, the bug will come back. We should fix this more properly. |
| { |
| uint8_t byte = 0; |
| auto int_data = _buffer.reinterpret<uint8_t>(); |
| for (size_t i = 0; i < len; i += PAGE_SIZE) { |
| status = int_data.copy_array_to_user(&byte, 1, i); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| if (len > 0) { |
| status = int_data.copy_array_to_user(&byte, 1, len - 1); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| } |
| |
| zx_status_t st = vmo->ReadUser(_buffer, offset, len, &read); |
| |
| if (st == ZX_OK) { |
| if (_actual.copy_to_user(static_cast<size_t>(read)) != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| } |
| return st; |
| } |
| |
| zx_status_t sys_process_write_memory(zx_handle_t proc, uintptr_t vaddr, |
| user_ptr<const void> _buffer, |
| size_t len, user_ptr<size_t> _actual) { |
| LTRACEF("vaddr 0x%" PRIxPTR ", size %zu\n", vaddr, len); |
| |
| if (!_buffer) |
| return ZX_ERR_INVALID_ARGS; |
| if (len == 0 || len > kMaxDebugWriteBlock) |
| return ZX_ERR_INVALID_ARGS; |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| fbl::RefPtr<ProcessDispatcher> process; |
| zx_status_t status = up->GetDispatcherWithRights(proc, ZX_RIGHT_WRITE, &process); |
| if (status != ZX_OK) |
| return status; |
| |
| auto aspace = process->aspace(); |
| if (!aspace) |
| return ZX_ERR_BAD_STATE; |
| |
| auto region = aspace->FindRegion(vaddr); |
| if (!region) |
| return ZX_ERR_NO_MEMORY; |
| |
| auto vm_mapping = region->as_vm_mapping(); |
| if (!vm_mapping) |
| return ZX_ERR_NO_MEMORY; |
| |
| auto vmo = vm_mapping->vmo(); |
| if (!vmo) |
| return ZX_ERR_NO_MEMORY; |
| |
| // Force map the range, even if it crosses multiple mappings. |
| // TODO(ZX-730): This is a workaround for this bug. If we start decommitting |
| // things, the bug will come back. We should fix this more properly. |
| { |
| uint8_t byte = 0; |
| auto int_data = _buffer.reinterpret<const uint8_t>(); |
| for (size_t i = 0; i < len; i += PAGE_SIZE) { |
| status = int_data.copy_array_from_user(&byte, 1, i); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| if (len > 0) { |
| status = int_data.copy_array_from_user(&byte, 1, len - 1); |
| if (status != ZX_OK) { |
| return status; |
| } |
| } |
| } |
| |
| uint64_t offset = vaddr - vm_mapping->base() + vm_mapping->object_offset(); |
| size_t written = 0; |
| |
| zx_status_t st = vmo->WriteUser(_buffer, offset, len, &written); |
| |
| if (st == ZX_OK) { |
| if (_actual.copy_to_user(static_cast<size_t>(written)) != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| } |
| return st; |
| } |
| |
| // helper routine for sys_task_kill |
| template <typename T> |
| static zx_status_t kill_task(fbl::RefPtr<Dispatcher> dispatcher) { |
| auto task = DownCastDispatcher<T>(&dispatcher); |
| if (!task) |
| return ZX_ERR_WRONG_TYPE; |
| |
| task->Kill(); |
| return ZX_OK; |
| } |
| |
| zx_status_t sys_task_kill(zx_handle_t task_handle) { |
| LTRACEF("handle %x\n", task_handle); |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| fbl::RefPtr<Dispatcher> dispatcher; |
| auto status = up->GetDispatcherWithRights(task_handle, ZX_RIGHT_DESTROY, &dispatcher); |
| if (status != ZX_OK) |
| return status; |
| |
| // see if it's a process or thread and dispatch accordingly |
| switch (dispatcher->get_type()) { |
| case ZX_OBJ_TYPE_PROCESS: |
| return kill_task<ProcessDispatcher>(fbl::move(dispatcher)); |
| case ZX_OBJ_TYPE_THREAD: |
| return kill_task<ThreadDispatcher>(fbl::move(dispatcher)); |
| case ZX_OBJ_TYPE_JOB: |
| return kill_task<JobDispatcher>(fbl::move(dispatcher)); |
| default: |
| return ZX_ERR_WRONG_TYPE; |
| } |
| } |
| |
| zx_status_t sys_job_create(zx_handle_t parent_job, uint32_t options, user_ptr<zx_handle_t> _out) { |
| LTRACEF("parent: %x\n", parent_job); |
| |
| if (options != 0u) |
| return ZX_ERR_INVALID_ARGS; |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| fbl::RefPtr<JobDispatcher> parent; |
| zx_status_t status = up->GetDispatcherWithRights(parent_job, ZX_RIGHT_WRITE, &parent); |
| if (status != ZX_OK) |
| return status; |
| |
| fbl::RefPtr<Dispatcher> job; |
| zx_rights_t rights; |
| status = JobDispatcher::Create(options, fbl::move(parent), &job, &rights); |
| if (status != ZX_OK) |
| return status; |
| |
| HandleOwner job_handle(MakeHandle(fbl::move(job), rights)); |
| if (_out.copy_to_user(up->MapHandleToValue(job_handle)) != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| |
| up->AddHandle(fbl::move(job_handle)); |
| return ZX_OK; |
| } |
| |
| zx_status_t sys_job_set_policy(zx_handle_t job_handle, uint32_t options, |
| uint32_t topic, user_ptr<const void> _policy, uint32_t count) { |
| |
| if ((options != ZX_JOB_POL_RELATIVE) && (options != ZX_JOB_POL_ABSOLUTE)) |
| return ZX_ERR_INVALID_ARGS; |
| if (!_policy || (count == 0u)) |
| return ZX_ERR_INVALID_ARGS; |
| |
| if (topic != ZX_JOB_POL_BASIC) |
| return ZX_ERR_INVALID_ARGS; |
| |
| fbl::AllocChecker ac; |
| fbl::InlineArray< |
| zx_policy_basic, kPolicyBasicInlineCount> policy(&ac, count); |
| if (!ac.check()) |
| return ZX_ERR_NO_MEMORY; |
| |
| auto status = _policy.copy_array_from_user(policy.get(), sizeof(zx_policy_basic) * count); |
| if (status != ZX_OK) |
| return ZX_ERR_INVALID_ARGS; |
| |
| auto up = ProcessDispatcher::GetCurrent(); |
| |
| fbl::RefPtr<JobDispatcher> job; |
| status = up->GetDispatcherWithRights(job_handle, ZX_RIGHT_SET_POLICY, &job); |
| if (status != ZX_OK) |
| return status; |
| |
| return job->SetPolicy(options, policy.get(), policy.size()); |
| } |
| |
| zx_status_t sys_job_set_relative_importance( |
| zx_handle_t resource_handle, |
| zx_handle_t job_handle, zx_handle_t less_important_job_handle) { |
| |
| ProcessDispatcher* up = ProcessDispatcher::GetCurrent(); |
| |
| // If the caller has a valid handle to the root resource, let them perform |
| // this operation no matter the rights on the job handles. |
| { |
| fbl::RefPtr<ResourceDispatcher> resource; |
| zx_status_t status = up->GetDispatcherWithRights( |
| resource_handle, ZX_RIGHT_NONE, &resource); |
| if (status != ZX_OK) |
| return status; |
| // TODO(ZX-971): Check that this is actually the appropriate resource |
| } |
| |
| // Get the job to modify. |
| fbl::RefPtr<JobDispatcher> job; |
| zx_status_t status = up->GetDispatcherWithRights( |
| job_handle, ZX_RIGHT_NONE, &job); |
| if (status != ZX_OK) |
| return status; |
| |
| // Get its less-important neighbor, or null. |
| fbl::RefPtr<JobDispatcher> li_job; |
| if (less_important_job_handle != ZX_HANDLE_INVALID) { |
| status = up->GetDispatcherWithRights( |
| less_important_job_handle, ZX_RIGHT_NONE, &li_job); |
| if (status != ZX_OK) |
| return status; |
| } |
| |
| return job->MakeMoreImportantThan(fbl::move(li_job)); |
| } |