blob: 09e00594e6f4c94692fe80ca040b66198a07aa5a [file] [log] [blame]
// Copyright 2019 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
#pragma once
#include <arch/exception.h>
#include <kernel/event.h>
#include <zircon/rights.h>
#include <zircon/syscalls/exception.h>
#include <zircon/types.h>
#include <fbl/ref_ptr.h>
#include <object/dispatcher.h>
#include <object/thread_dispatcher.h>
// Zircon channel-based exception handling uses two primary classes,
// ExceptionDispatcher (this file) and Exceptionate (exceptionate.h).
//
// An ExceptionDispatcher represents a single currently-active exception. This
// will be transmitted to registered exception handlers in userspace and
// provides them with exception state and control functionality.
//
// An Exceptionate wraps a channel endpoint to help with sending exceptions to
// userspace. It is a kernel-internal helper class and not exposed to userspace.
class ExceptionDispatcher final :
public SoloDispatcher<ExceptionDispatcher, ZX_DEFAULT_EXCEPTION_RIGHTS> {
public:
// Returns nullptr on memory allocation failure.
static fbl::RefPtr<ExceptionDispatcher> Create(fbl::RefPtr<ThreadDispatcher> thread,
zx_excp_type_t exception_type,
const zx_exception_report_t* report,
const arch_exception_context_t* arch_context);
~ExceptionDispatcher() final;
zx_obj_type_t get_type() const final { return ZX_OBJ_TYPE_EXCEPTION; }
void on_zero_handles() final;
fbl::RefPtr<ThreadDispatcher> thread() const { return thread_; }
zx_excp_type_t exception_type() const { return exception_type_; }
// Copies the exception report provided at ExceptionDispatcher creation.
//
// The exception report is only available while the exception thread is
// still alive.
//
// Returns false and leaves |report| untouched if the thread has died.
bool FillReport(zx_exception_report_t* report) const;
// Sets the task rights to use for subsequent handle creation.
//
// rights == 0 indicates that the current exception handler is not allowed
// to access the corresponding task handle, for example a thread-level
// handler cannot access its parent process handle.
//
// This must only be called by an Exceptionate before transmitting the
// exception - we don't ever want to be changing task rights while the
// exception is out in userspace.
void SetTaskRights(zx_rights_t thread_rights, zx_rights_t process_rights);
// Creates new thread or process handles.
//
// Returns:
// ZX_OK on success.
// ZX_ERR_ACCESS_DENIED if the task rights have been set to 0.
// ZX_ERR_NO_MEMORY if the Handle failed to allocate.
zx_status_t MakeThreadHandle(HandleOwner* handle) const;
zx_status_t MakeProcessHandle(HandleOwner* handle) const;
// Whether to resume the thread on exception close or pass it to the
// next handler in line.
void GetResumeThreadOnClose(bool* resume_on_close) const;
void SetResumeThreadOnClose(bool resume_on_close);
// Blocks until the exception handler is done processing.
//
// Returns:
// ZX_OK if the exception was handled and the thread should resume.
// ZX_ERR_NEXT if the exception should be passed to the next handler.
// ZX_ERR_INTERNAL_INTR_KILLED if the thread was killed.
zx_status_t WaitForResponse();
// Wipe out exception state, which indicates the thread has died.
void Clear();
private:
ExceptionDispatcher(fbl::RefPtr<ThreadDispatcher> thread,
zx_excp_type_t exception_type,
const zx_exception_report_t* report,
const arch_exception_context_t* arch_context);
// These are const and only set during construction, so don't need to be
// guarded with get_lock().
const fbl::RefPtr<ThreadDispatcher> thread_;
const zx_excp_type_t exception_type_;
// These gets updated by the Exceptionate whenever we get transmitted,
// according to the rights that specific Exceptionate was registered with.
zx_rights_t thread_rights_ TA_GUARDED(get_lock()) = 0;
zx_rights_t process_rights_ TA_GUARDED(get_lock()) = 0;
// These will be nulled out if the underlying thread is killed while
// userspace still has access to this exception.
const zx_exception_report_t* report_ TA_GUARDED(get_lock());
const arch_exception_context_t* arch_context_ TA_GUARDED(get_lock());
bool resume_on_close_ TA_GUARDED(get_lock()) = false;
Event response_event_;
};