| // 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_ |