blob: d9bd701154c12a19c16287bdfe84a43ff7ab86bb [file] [log] [blame] [edit]
// 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 <lib/syscalls/forward.h>
#include <platform.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <trace.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <object/exception_dispatcher.h>
#include <object/exceptionate.h>
#include <object/job_dispatcher.h>
#include <object/process_dispatcher.h>
#include <object/thread_dispatcher.h>
#define LOCAL_TRACE 0
// zx_status_t zx_task_create_exception_channel
zx_status_t sys_task_create_exception_channel(zx_handle_t handle, uint32_t options,
zx_handle_t* out) {
LTRACE_ENTRY;
if (options & ~ZX_EXCEPTION_CHANNEL_DEBUGGER)
return ZX_ERR_INVALID_ARGS;
auto up = ProcessDispatcher::GetCurrent();
zx_status_t status = up->EnforceBasicPolicy(ZX_POL_NEW_CHANNEL);
if (status != ZX_OK)
return status;
// Required rights to receive exceptions:
// INSPECT: provides non-trivial task information
// DUPLICATE: can create new thread and process handles
// TRANSFER: exceptions or their channels can be transferred
// MANAGE_THREAD: can keep thread paused during exception
// ENUMERATE (job/process): can access child thread (enforced below)
//
// In the future we may want to support some smarter behavior here e.g.
// allowing for exceptions but no task handles if these rights don't exist,
// but to start with we'll keep it simple until we know we want this.
fbl::RefPtr<Dispatcher> task;
zx_rights_t task_rights;
status = up->handle_table().GetDispatcherWithRights(
*up, handle,
ZX_RIGHT_INSPECT | ZX_RIGHT_DUPLICATE | ZX_RIGHT_TRANSFER | ZX_RIGHT_MANAGE_THREAD, &task,
&task_rights);
if (status != ZX_OK)
return status;
// The task handles provided over this exception channel use the rights on
// |handle| so we are sure not to grant any additional rights the caller
// didn't already have.
//
// TODO(https://fxbug.dev/42108165): thread/process/job rights don't always map 1:1.
zx_rights_t process_rights = task_rights;
zx_rights_t thread_rights = task_rights;
// Only one of the |exceptionate| and |job_to_create_debug_exceptionate| will be non-null.
//
// When creating debugger exception channel on a job, |exceptionate| will be null and
// |job_to_create_debug_exceptionate| is used instead, because JobDispatcher::DebugExceptionate
// requires dynamic allocation. For other types, only |exceptionate| will be used.
Exceptionate* exceptionate = nullptr;
JobDispatcher* job_to_create_debug_exceptionate = nullptr;
bool job_or_process = false;
// Use DownCastDispatcher() on the raw task pointer to avoid moving the
// RefPtr out, we still need to retain the RefPtr to keep our extracted
// Exceptionate* alive.
if (auto job = DownCastDispatcher<JobDispatcher>(task.get())) {
if (options & ZX_EXCEPTION_CHANNEL_DEBUGGER) {
job_to_create_debug_exceptionate = job;
} else {
exceptionate = job->exceptionate();
}
job_or_process = true;
} else if (auto process = DownCastDispatcher<ProcessDispatcher>(task.get())) {
if (options & ZX_EXCEPTION_CHANNEL_DEBUGGER) {
exceptionate = process->debug_exceptionate();
} else {
exceptionate = process->exceptionate();
}
job_or_process = true;
} else if (auto thread = DownCastDispatcher<ThreadDispatcher>(task.get())) {
if (options & ZX_EXCEPTION_CHANNEL_DEBUGGER)
return ZX_ERR_INVALID_ARGS;
// We don't provide access up the task chain, so don't send the process
// handle when we're registering on a thread.
process_rights = 0;
exceptionate = thread->exceptionate();
} else {
return ZX_ERR_WRONG_TYPE;
}
DEBUG_ASSERT(task);
// For job and process handlers, we require the handle be able to enumerate
// as proof that the caller is allowed to get to the thread handle.
if (job_or_process && !(task_rights & ZX_RIGHT_ENUMERATE))
return ZX_ERR_ACCESS_DENIED;
KernelHandle<ChannelDispatcher> kernel_handle, user_handle;
zx_rights_t rights;
status = ChannelDispatcher::Create(&kernel_handle, &user_handle, &rights);
if (status != ZX_OK)
return status;
if (job_to_create_debug_exceptionate) {
status = job_to_create_debug_exceptionate->CreateDebugExceptionate(
ktl::move(kernel_handle), thread_rights, process_rights);
} else {
status = exceptionate->SetChannel(ktl::move(kernel_handle), thread_rights, process_rights);
}
if (status != ZX_OK)
return status;
// Strip unwanted rights from the user endpoint, exception channels are
// read-only from userspace.
//
// We don't need to remove the task channel if this fails. Exception
// channels are built to handle the userspace peer closing so it will just
// follow that path if we fail to copy the userspace endpoint out.
return up->MakeAndAddHandle(ktl::move(user_handle),
rights & (ZX_RIGHT_TRANSFER | ZX_RIGHT_WAIT | ZX_RIGHT_READ), out);
}
// zx_status_t zx_exception_get_thread
zx_status_t sys_exception_get_thread(zx_handle_t handle, zx_handle_t* thread) {
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<ExceptionDispatcher> exception;
zx_status_t status = up->handle_table().GetDispatcher(*up, handle, &exception);
if (status != ZX_OK) {
return status;
}
HandleOwner thread_handle;
status = exception->MakeThreadHandle(&thread_handle);
if (status != ZX_OK) {
return status;
}
*thread = up->handle_table().MapHandleToValue(thread_handle);
up->handle_table().AddHandle(ktl::move(thread_handle));
return ZX_OK;
}
// zx_status_t zx_exception_get_process
zx_status_t sys_exception_get_process(zx_handle_t handle, zx_handle_t* process) {
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<ExceptionDispatcher> exception;
zx_status_t status = up->handle_table().GetDispatcher(*up, handle, &exception);
if (status != ZX_OK) {
return status;
}
HandleOwner process_handle;
status = exception->MakeProcessHandle(&process_handle);
if (status != ZX_OK) {
return status;
}
*process = up->handle_table().MapHandleToValue(process_handle);
up->handle_table().AddHandle(ktl::move(process_handle));
return ZX_OK;
}