blob: b76a56d4d6dc1618097a32d9476febba025ce320 [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
#ifndef ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_EXCEPTION_DISPATCHER_H_
#define ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_EXCEPTION_DISPATCHER_H_
#include <zircon/rights.h>
#include <zircon/syscalls/exception.h>
#include <zircon/types.h>
#include <arch/exception.h>
#include <fbl/ref_ptr.h>
#include <kernel/event.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);
static zx_exception_report_t BuildArchReport(uint32_t type,
const arch_exception_context_t& arch_context);
~ExceptionDispatcher() final;
zx_obj_type_t get_type() const final { return ZX_OBJ_TYPE_EXCEPTION; }
// Marks the current exception handler as done.
//
// Once a handle has been created around this object, either
// WaitForHandleClose() or DiscardHandleClose() must be called to reset
// our state for the next handler.
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.
bool ResumesThreadOnClose() const;
void SetWhetherResumesThreadOnClose(bool resume_on_close);
// Whether a debugger should have a second chance to handle the exception
// after the process handler has tried and failed to do so.
bool IsSecondChance() const;
void SetWhetherSecondChance(bool second_chance);
// Blocks until the exception handler is done processing.
//
// This must be called exactly once every time this exception is
// successfully sent out to userspace, in order to wait for the response
// and reset the internal state.
//
// 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 WaitForHandleClose();
// Resets the exception state for the next handler.
//
// This must be called instead of WaitForHandleClose() if a handle is
// created around this exception but fails to make it out to userspace,
// in order to reset the internal state.
void DiscardHandleClose();
// 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;
bool second_chance_ TA_GUARDED(get_lock()) = false;
AutounsignalEvent response_event_;
};
#endif // ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_EXCEPTION_DISPATCHER_H_