1 // Copyright 2017 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 
7 #pragma once
8 
9 #include <stdint.h>
10 
11 #include <arch/exception.h>
12 #include <kernel/lockdep.h>
13 #include <kernel/mutex.h>
14 #include <object/dispatcher.h>
15 
16 #include <zircon/syscalls/exception.h>
17 #include <zircon/syscalls/port.h>
18 #include <zircon/types.h>
19 #include <fbl/canary.h>
20 #include <fbl/intrusive_double_list.h>
21 #include <fbl/mutex.h>
22 #include <fbl/ref_counted.h>
23 #include <fbl/ref_ptr.h>
24 
25 class ThreadDispatcher;
26 class ProcessDispatcher;
27 class PortDispatcher;
28 
29 // Represents the binding of an exception port to a specific target
30 // (job/process/thread). Multiple ExceptionPorts may exist for a
31 // single underlying PortDispatcher.
32 class ExceptionPort : public fbl::DoublyLinkedListable<fbl::RefPtr<ExceptionPort>>
33                     , public fbl::RefCounted<ExceptionPort> {
34 public:
35     enum class Type { NONE, JOB_DEBUGGER, DEBUGGER, THREAD, PROCESS, JOB};
36 
37     static zx_status_t Create(Type type, fbl::RefPtr<PortDispatcher> port,
38                               uint64_t port_key,
39                               fbl::RefPtr<ExceptionPort>* eport);
40     ~ExceptionPort();
41 
type()42     Type type() const { return type_; }
43 
44     zx_status_t SendPacket(ThreadDispatcher* thread, uint32_t type);
45 
46     void OnThreadStartForDebugger(ThreadDispatcher* thread);
47     void OnThreadExitForDebugger(ThreadDispatcher* thread);
48     void OnProcessStartForDebugger(ThreadDispatcher* thread);
49 
50     // Records the target that the ExceptionPort is bound to, so it can
51     // unbind when the underlying PortDispatcher dies.
52     void SetTarget(const fbl::RefPtr<JobDispatcher>& target);
53     void SetTarget(const fbl::RefPtr<ProcessDispatcher>& target);
54     void SetTarget(const fbl::RefPtr<ThreadDispatcher>& target);
55 
56     // Drops the ExceptionPort's references to its target and PortDispatcher.
57     // Called by the target when the port is explicitly unbound.
58     void OnTargetUnbind();
59 
60     // Validates that this eport is associated with the given instance.
61     bool PortMatches(const PortDispatcher* port, bool allow_null);
62 
63     static void BuildArchReport(zx_exception_report_t* report, uint32_t type,
64                                 const arch_exception_context_t* arch_context);
65 
66 private:
67     friend class PortDispatcher;
68 
69     ExceptionPort(Type type, fbl::RefPtr<PortDispatcher> port, uint64_t port_key);
70 
71     ExceptionPort(const ExceptionPort&) = delete;
72     ExceptionPort& operator=(const ExceptionPort&) = delete;
73 
74     zx_status_t SendPacketWorker(uint32_t type, zx_koid_t pid, zx_koid_t tid);
75 
76     // Unbinds from the target if bound, and drops the ref to |port_|.
77     // Called by |port_| when it reaches zero handles.
78     void OnPortZeroHandles();
79 
80     // Returns true if the ExceptionPort is currently bound to a target.
IsBoundLocked()81     bool IsBoundLocked() const TA_REQ(lock_) {
82         return target_ != nullptr;
83     }
84 
85     static void BuildReport(zx_exception_report_t* report, uint32_t type);
86 
87     fbl::Canary<fbl::magic("EXCP")> canary_;
88 
89     // These aren't locked as once the exception port is created these are
90     // immutable (the port itself has its own locking though).
91     const Type type_;
92     const uint64_t port_key_;
93 
94     // The underlying port. If null, the ExceptionPort has been unbound.
95     fbl::RefPtr<PortDispatcher> port_ TA_GUARDED(lock_);
96 
97     // The target of the exception port.
98     // The system exception port doesn't have a Dispatcher, hence the bool.
99     fbl::RefPtr<Dispatcher> target_ TA_GUARDED(lock_);
100 
101     DECLARE_MUTEX(ExceptionPort) lock_;
102 
103     // NOTE: The DoublyLinkedListNodeState is guarded by |port_|'s lock,
104     // and should only be touched using port_->LinkExceptionPort()
105     // or port_->UnlinkExceptionPort(). This goes for ::InContainer(), too.
106 };
107