blob: 7079aab2bf9b796b20ce736f0a93b010710d7c79 [file] [log] [blame]
// 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.h>
#include <object/job_dispatcher.h>
#include <object/process_dispatcher.h>
#include <object/resource_dispatcher.h>
#include <object/suspend_token_dispatcher.h>
#include <object/thread_dispatcher.h>
#include <object/vm_address_region_dispatcher.h>
#include <fbl/auto_lock.h>
#include <fbl/inline_array.h>
#include <fbl/ref_ptr.h>
#include <fbl/string_piece.h>
#include <zircon/syscalls/debug.h>
#include <zircon/syscalls/policy.h>
#include "priv.h"
#define LOCAL_TRACE 0
namespace {
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;
// 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.
zx_status_t copy_user_string(const user_in_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;
}
// This represents the local storage for thread_read/write_state. It should be large enough to
// handle all structures passed over these APIs.
union thread_state_local_buffer_t {
zx_thread_state_general_regs_t general_regs; // ZX_THREAD_STATE_GENERAL_REGS
zx_thread_state_fp_regs_t fp_regs; // ZX_THREAD_STATE_FP_REGS
zx_thread_state_vector_regs_t vector_regs; // ZX_THREAD_STATE_VECTOR_REGS
zx_thread_state_debug_regs_t debug_regs; // ZX_THREAD_STATE_DEBUG_REGS
uint32_t single_step; // ZX_THREAD_STATE_SINGLE_STEP
uint64_t x86_register_fs; // ZX_THREAD_X86_REGISTER_FS;
uint64_t x86_register_gs; // ZX_THREAD_X86_REGISTER_GS;
};
// Validates the input topic to thread_read_state and thread_write_state is a valid value, and
// checks that the input buffer size is at least as large as necessary for the topic. On ZX_OK, the
// actual size necessary for the buffer will be placed in the output parameter.
zx_status_t validate_thread_state_input(uint32_t in_topic, size_t in_len, size_t* out_len) {
switch (in_topic) {
case ZX_THREAD_STATE_GENERAL_REGS:
// We are temporarily supporting programs that pass in the length of
// the old struct.
// TODO(ZX-3883): remove after old struct users have been migrated
if (in_len >= sizeof(__old_zx_thread_state_general_regs_t) &&
in_len < sizeof(zx_thread_state_general_regs_t))
*out_len = sizeof(__old_zx_thread_state_general_regs_t);
else
*out_len = sizeof(zx_thread_state_general_regs_t);
break;
case ZX_THREAD_STATE_FP_REGS:
*out_len = sizeof(zx_thread_state_fp_regs_t);
break;
case ZX_THREAD_STATE_VECTOR_REGS:
*out_len = sizeof(zx_thread_state_vector_regs_t);
break;
case ZX_THREAD_STATE_DEBUG_REGS:
*out_len = sizeof(zx_thread_state_debug_regs_t);
break;
case ZX_THREAD_STATE_SINGLE_STEP:
*out_len = sizeof(zx_thread_state_single_step_t);
break;
case ZX_THREAD_X86_REGISTER_FS:
*out_len = sizeof(zx_thread_x86_register_fs_t);
break;
case ZX_THREAD_X86_REGISTER_GS:
*out_len = sizeof(zx_thread_x86_register_gs_t);
break;
default:
return ZX_ERR_INVALID_ARGS;
}
if (in_len < *out_len)
return ZX_ERR_BUFFER_TOO_SMALL;
return ZX_OK;
}
} // namespace
// zx_status_t zx_thread_create
zx_status_t sys_thread_create(zx_handle_t process_handle,
user_in_ptr<const char> _name, size_t name_len,
uint32_t options, user_out_handle* 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 = up->GetDispatcherWithRights(process_handle, ZX_RIGHT_MANAGE_THREAD, &process);
if (result != ZX_OK) {
return result;
}
uint32_t pid = (uint32_t)process->get_koid();
// create the thread dispatcher
KernelHandle<ThreadDispatcher> handle;
zx_rights_t thread_rights;
result = ThreadDispatcher::Create(ktl::move(process), options, sp,
&handle, &thread_rights);
if (result != ZX_OK)
return result;
uint32_t tid = (uint32_t)handle.dispatcher()->get_koid();
ktrace(TAG_THREAD_CREATE, tid, pid, 0, 0);
ktrace_name(TAG_THREAD_NAME, tid, pid, buf);
return out->make(ktl::move(handle), thread_rights);
}
// zx_status_t zx_thread_start
zx_status_t sys_thread_start(zx_handle_t handle, zx_vaddr_t thread_entry,
zx_vaddr_t stack, uintptr_t arg1, uintptr_t arg2) {
LTRACEF("handle %x, entry %#" PRIxPTR ", sp %#" PRIxPTR
", arg1 %#" PRIxPTR ", arg2 %#" PRIxPTR "\n",
handle, thread_entry, stack, arg1, arg2);
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<ThreadDispatcher> thread;
zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_MANAGE_THREAD, &thread);
if (status != ZX_OK) {
return status;
}
ktrace(TAG_THREAD_START, (uint32_t)thread->get_koid(), 0, 0, 0);
return thread->Start(ThreadDispatcher::EntryState{thread_entry, stack, arg1, arg2},
/* initial_thread= */ false);
}
void sys_thread_exit() {
LTRACE_ENTRY;
ThreadDispatcher::GetCurrent()->Exit();
}
// zx_status_t zx_thread_read_state
zx_status_t sys_thread_read_state(zx_handle_t handle, uint32_t kind,
user_out_ptr<void> buffer, size_t buffer_size) {
LTRACEF("handle %x, kind %u\n", handle, 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;
thread_state_local_buffer_t local_buffer;
size_t local_buffer_len = 0;
status = validate_thread_state_input(kind, buffer_size, &local_buffer_len);
if (status != ZX_OK)
return status;
status = thread->ReadState(static_cast<zx_thread_state_topic_t>(kind), &local_buffer,
local_buffer_len);
if (status != ZX_OK)
return status;
if (buffer.copy_array_to_user(&local_buffer, local_buffer_len) != ZX_OK)
return ZX_ERR_INVALID_ARGS;
return ZX_OK;
}
// zx_status_t zx_thread_write_state
zx_status_t sys_thread_write_state(zx_handle_t handle, uint32_t kind,
user_in_ptr<const void> buffer, size_t buffer_size) {
LTRACEF("handle %x, kind %u\n", handle, 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;
thread_state_local_buffer_t local_buffer;
size_t local_buffer_len = 0;
status = validate_thread_state_input(kind, buffer_size, &local_buffer_len);
if (status != ZX_OK)
return status;
// Additionally check that the buffer is the exact size expected (validate only checks it's
// larger, which is sufficient for reading).
if (local_buffer_len != buffer_size)
return ZX_ERR_INVALID_ARGS;
status = buffer.copy_array_from_user(&local_buffer, local_buffer_len);
if (status != ZX_OK)
return ZX_ERR_INVALID_ARGS;
// Don't clobber new registers if this program uses the old struct.
// TODO(ZX-3883): remove after old struct users have been migrated
if (kind == ZX_THREAD_STATE_GENERAL_REGS &&
local_buffer_len == sizeof(__old_zx_thread_state_general_regs_t)) {
zx_thread_state_general_regs_t current_regs;
status = thread->ReadState(ZX_THREAD_STATE_GENERAL_REGS, &current_regs,
sizeof(current_regs));
if (status != ZX_OK)
return status;
#if defined(__x86_64__)
local_buffer.general_regs.fs_base = current_regs.fs_base;
local_buffer.general_regs.gs_base = current_regs.gs_base;
#else
local_buffer.general_regs.tpidr = current_regs.tpidr;
#endif
}
return thread->WriteState(static_cast<zx_thread_state_topic_t>(kind), &local_buffer,
local_buffer_len);
}
// zx_status_t zx_task_suspend
zx_status_t sys_task_suspend(zx_handle_t handle, user_out_handle* token) {
LTRACE_ENTRY;
auto up = ProcessDispatcher::GetCurrent();
// TODO(ZX-858): Add support for jobs
fbl::RefPtr<Dispatcher> task;
zx_status_t status = up->GetDispatcherWithRights(handle, ZX_RIGHT_WRITE, &task);
if (status != ZX_OK)
return status;
KernelHandle<SuspendTokenDispatcher> new_token;
zx_rights_t rights;
status = SuspendTokenDispatcher::Create(ktl::move(task), &new_token, &rights);
if (status == ZX_OK)
status = token->make(ktl::move(new_token), rights);
return status;
}
// zx_status_t zx_task_suspend_token
zx_status_t sys_task_suspend_token(zx_handle_t handle, user_out_handle* token) {
return sys_task_suspend(handle, token);
}
// zx_status_t zx_process_create
zx_status_t sys_process_create(zx_handle_t job_handle,
user_in_ptr<const char> _name, size_t name_len,
uint32_t options,
user_out_handle* proc_handle,
user_out_handle* 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;
auto up = ProcessDispatcher::GetCurrent();
// We check the policy against the process calling zx_process_create, which
// is the operative policy, rather than against |job_handle|. Access to
// |job_handle| is controlled by the rights associated with the handle.
zx_status_t result = up->EnforceBasicPolicy(ZX_POL_NEW_PROCESS);
if (result != ZX_OK)
return result;
// 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);
result = copy_user_string(_name, name_len, buf, sizeof(buf), &sp);
if (result != ZX_OK)
return result;
LTRACEF("name %s\n", buf);
fbl::RefPtr<JobDispatcher> job;
auto status = up->GetDispatcherWithRights(job_handle, ZX_RIGHT_MANAGE_PROCESS, &job);
if (status != ZX_OK) {
// Try again, but with the WRITE right.
// TODO(ZX-2967) Remove this when all callers are using MANAGE_PROCESS.
status = up->GetDispatcherWithRights(job_handle, ZX_RIGHT_WRITE, &job);
if (status != ZX_OK) {
return status;
}
}
// create a new process dispatcher
KernelHandle<ProcessDispatcher> new_process_handle;
KernelHandle<VmAddressRegionDispatcher> new_vmar_handle;
zx_rights_t proc_rights, vmar_rights;
result = ProcessDispatcher::Create(ktl::move(job), sp, options,
&new_process_handle, &proc_rights,
&new_vmar_handle, &vmar_rights);
if (result != ZX_OK)
return result;
uint32_t koid = (uint32_t)new_process_handle.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, new_vmar_handle.dispatcher()->vmar()->aspace()->arch_aspace().arch_table_phys());
result = proc_handle->make(ktl::move(new_process_handle), proc_rights);
if (result == ZX_OK)
result = vmar_handle->make(ktl::move(new_vmar_handle), vmar_rights);
return result;
}
// 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 zx_process_start
zx_status_t sys_process_start(zx_handle_t process_handle, zx_handle_t thread_handle,
zx_vaddr_t pc, zx_vaddr_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 = up->GetDispatcherWithRights(process_handle, ZX_RIGHT_WRITE, &process);
if (status != ZX_OK) {
up->RemoveHandle(arg_handle_value);
return status;
}
// get thread_dispatcher
fbl::RefPtr<ThreadDispatcher> thread;
status = up->GetDispatcherWithRights(thread_handle, ZX_RIGHT_WRITE, &thread);
if (status != ZX_OK) {
up->RemoveHandle(arg_handle_value);
return status;
}
HandleOwner arg_handle = up->RemoveHandle(arg_handle_value);
// test that the thread belongs to the starting process
if (thread->process() != process.get())
return ZX_ERR_ACCESS_DENIED;
zx_handle_t arg_nhv = ZX_HANDLE_INVALID;
if (arg_handle) {
if (!arg_handle->HasRights(ZX_RIGHT_TRANSFER))
return ZX_ERR_ACCESS_DENIED;
arg_nhv = process->MapHandleToValue(arg_handle);
process->AddHandle(ktl::move(arg_handle));
}
status =
thread->Start(ThreadDispatcher::EntryState{pc, sp, static_cast<uintptr_t>(arg_nhv), arg2},
/* initial_thread */ true);
if (status != ZX_OK) {
// Remove |arg_handle| from the process that failed to start.
process->RemoveHandle(arg_nhv);
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(int64_t retcode) {
LTRACEF("retcode %" PRId64 "\n", retcode);
ProcessDispatcher::GetCurrent()->Exit(retcode);
}
// zx_status_t zx_process_read_memory
zx_status_t sys_process_read_memory(zx_handle_t handle, zx_vaddr_t vaddr,
user_out_ptr<void> buffer,
size_t buffer_size, user_out_ptr<size_t> _actual) {
LTRACEF("vaddr 0x%" PRIxPTR ", size %zu\n", vaddr, buffer_size);
if (!buffer)
return ZX_ERR_INVALID_ARGS;
if (buffer_size == 0 || buffer_size > kMaxDebugReadBlock)
return ZX_ERR_INVALID_ARGS;
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<ProcessDispatcher> process;
zx_status_t status = up->GetDispatcherWithRights(handle, 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;
// 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 < buffer_size; i += PAGE_SIZE) {
status = int_data.copy_array_to_user(&byte, 1, i);
if (status != ZX_OK) {
return status;
}
}
if (buffer_size > 0) {
status = int_data.copy_array_to_user(&byte, 1, buffer_size - 1);
if (status != ZX_OK) {
return status;
}
}
}
uint64_t offset = vaddr - vm_mapping->base() + vm_mapping->object_offset();
// TODO(ZX-1631): While this limits reading to the mapped address space of
// this VMO, it should be reading from multiple VMOs, not a single one.
// Additionally, it is racy with the mapping going away.
buffer_size = MIN(buffer_size, vm_mapping->size() - (vaddr - vm_mapping->base()));
zx_status_t st = vmo->ReadUser(buffer, offset, buffer_size);
if (st == ZX_OK) {
zx_status_t status = _actual.copy_to_user(static_cast<size_t>(buffer_size));
if (status != ZX_OK)
return status;
}
return st;
}
// zx_status_t zx_process_write_memory
zx_status_t sys_process_write_memory(zx_handle_t handle, zx_vaddr_t vaddr,
user_in_ptr<const void> buffer,
size_t buffer_size, user_out_ptr<size_t> _actual) {
LTRACEF("vaddr 0x%" PRIxPTR ", size %zu\n", vaddr, buffer_size);
if (!buffer)
return ZX_ERR_INVALID_ARGS;
if (buffer_size == 0 || buffer_size > kMaxDebugWriteBlock)
return ZX_ERR_INVALID_ARGS;
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<ProcessDispatcher> process;
zx_status_t status = up->GetDispatcherWithRights(handle, 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 < buffer_size; i += PAGE_SIZE) {
status = int_data.copy_array_from_user(&byte, 1, i);
if (status != ZX_OK) {
return status;
}
}
if (buffer_size > 0) {
status = int_data.copy_array_from_user(&byte, 1, buffer_size - 1);
if (status != ZX_OK) {
return status;
}
}
}
uint64_t offset = vaddr - vm_mapping->base() + vm_mapping->object_offset();
// TODO(ZX-1631): While this limits writing to the mapped address space of
// this VMO, it should be writing to multiple VMOs, not a single one.
// Additionally, it is racy with the mapping going away.
buffer_size = MIN(buffer_size, vm_mapping->size() - (vaddr - vm_mapping->base()));
zx_status_t st = vmo->WriteUser(buffer, offset, buffer_size);
if (st == ZX_OK) {
zx_status_t status = _actual.copy_to_user(static_cast<size_t>(buffer_size));
if (status != ZX_OK)
return status;
}
return st;
}
// helper routine for sys_task_kill
template <typename T, bool retcode>
static zx_status_t kill_task(fbl::RefPtr<Dispatcher> dispatcher) {
auto task = DownCastDispatcher<T>(&dispatcher);
if (!task)
return ZX_ERR_WRONG_TYPE;
if constexpr (retcode) {
task->Kill(ZX_TASK_RETCODE_SYSCALL_KILL);
} else {
task->Kill();
}
return ZX_OK;
}
// zx_status_t zx_task_kill
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_JOB:
return kill_task<JobDispatcher, true>(ktl::move(dispatcher));
case ZX_OBJ_TYPE_PROCESS:
return kill_task<ProcessDispatcher, true>(ktl::move(dispatcher));
case ZX_OBJ_TYPE_THREAD:
return kill_task<ThreadDispatcher, false>(ktl::move(dispatcher));
default:
return ZX_ERR_WRONG_TYPE;
}
}
// zx_status_t zx_job_create
zx_status_t sys_job_create(zx_handle_t parent_job, uint32_t options,
user_out_handle* 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_MANAGE_JOB, &parent);
if (status != ZX_OK) {
// Try again, but with the WRITE right.
// TODO(kulakowski) Remove this when all callers are using MANAGE_JOB.
status = up->GetDispatcherWithRights(parent_job, ZX_RIGHT_WRITE, &parent);
if (status != ZX_OK) {
return status;
}
}
KernelHandle<JobDispatcher> handle;
zx_rights_t rights;
status = JobDispatcher::Create(options, ktl::move(parent), &handle, &rights);
if (status == ZX_OK)
status = out->make(ktl::move(handle), rights);
return status;
}
static zx_status_t job_set_policy_basic(zx_handle_t handle, uint32_t options,
user_in_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;
}
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(handle, ZX_RIGHT_SET_POLICY, &job);
if (status != ZX_OK) {
return status;
}
return job->SetBasicPolicy(options, policy.get(), policy.size());
}
static zx_status_t job_set_policy_timer_slack(zx_handle_t handle, uint32_t options,
user_in_ptr<const void> _policy, uint32_t count) {
if (options != ZX_JOB_POL_RELATIVE) {
return ZX_ERR_INVALID_ARGS;
}
if (!_policy || (count != 1u)) {
return ZX_ERR_INVALID_ARGS;
}
zx_policy_timer_slack slack_policy;
auto status = _policy.reinterpret<const zx_policy_timer_slack>().copy_from_user(&slack_policy);
if (status != ZX_OK) {
return ZX_ERR_INVALID_ARGS;
}
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<JobDispatcher> job;
status = up->GetDispatcherWithRights(handle, ZX_RIGHT_SET_POLICY, &job);
if (status != ZX_OK) {
return status;
}
return job->SetTimerSlackPolicy(slack_policy);
}
// zx_status_t zx_job_set_policy
zx_status_t sys_job_set_policy(zx_handle_t handle, uint32_t options,
uint32_t topic, user_in_ptr<const void> _policy,
uint32_t count) {
switch (topic) {
case ZX_JOB_POL_BASIC:
return job_set_policy_basic(handle, options, _policy, count);
case ZX_JOB_POL_TIMER_SLACK:
return job_set_policy_timer_slack(handle, options, _policy, count);
default:
return ZX_ERR_INVALID_ARGS;
};
}