| // 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/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, |
| user_out_handle* 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(fxbug.dev/33015): thread/process/job rights don't always map 1:1. |
| zx_rights_t process_rights = task_rights; |
| zx_rights_t thread_rights = task_rights; |
| |
| Exceptionate::Type type = (options & ZX_EXCEPTION_CHANNEL_DEBUGGER) |
| ? Exceptionate::Type::kDebug |
| : Exceptionate::Type::kStandard; |
| Exceptionate* 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())) { |
| exceptionate = job->exceptionate(type); |
| job_or_process = true; |
| } else if (auto process = DownCastDispatcher<ProcessDispatcher>(task.get())) { |
| exceptionate = process->exceptionate(type); |
| job_or_process = true; |
| } else if (auto thread = DownCastDispatcher<ThreadDispatcher>(task.get())) { |
| if (type == Exceptionate::Type::kDebug) |
| 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; |
| |
| 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 out->make(ktl::move(user_handle), |
| rights & (ZX_RIGHT_TRANSFER | ZX_RIGHT_WAIT | ZX_RIGHT_READ)); |
| } |
| |
| // zx_status_t zx_exception_get_thread |
| zx_status_t sys_exception_get_thread(zx_handle_t handle, user_out_handle* 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; |
| } |
| |
| return thread->transfer(ktl::move(thread_handle)); |
| } |
| |
| // zx_status_t zx_exception_get_process |
| zx_status_t sys_exception_get_process(zx_handle_t handle, user_out_handle* 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; |
| } |
| |
| return process->transfer(ktl::move(process_handle)); |
| } |