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 #include <object/excp_port.h>
8
9 #include <err.h>
10 #include <inttypes.h>
11 #include <string.h>
12
13 #include <arch/exception.h>
14
15 #include <object/job_dispatcher.h>
16 #include <object/port_dispatcher.h>
17 #include <object/process_dispatcher.h>
18 #include <object/thread_dispatcher.h>
19
20 #include <fbl/alloc_checker.h>
21
22 #include <trace.h>
23
24 #define LOCAL_TRACE 0
25
MakePacket(uint64_t key,uint32_t type,zx_koid_t pid,zx_koid_t tid)26 static PortPacket* MakePacket(uint64_t key, uint32_t type, zx_koid_t pid, zx_koid_t tid) {
27 if (!ZX_PKT_IS_EXCEPTION(type))
28 return nullptr;
29
30 auto port_packet = PortDispatcher::DefaultPortAllocator()->Alloc();
31 if (!port_packet)
32 return nullptr;
33
34 port_packet->packet.key = key;
35 port_packet->packet.type = type;
36 port_packet->packet.status = ZX_OK;
37 port_packet->packet.exception.pid = pid;
38 port_packet->packet.exception.tid = tid;
39 port_packet->packet.exception.reserved0 = 0;
40 port_packet->packet.exception.reserved1 = 0;
41
42 return port_packet;
43 }
44
45 // static
Create(Type type,fbl::RefPtr<PortDispatcher> port,uint64_t port_key,fbl::RefPtr<ExceptionPort> * out_eport)46 zx_status_t ExceptionPort::Create(Type type, fbl::RefPtr<PortDispatcher> port, uint64_t port_key,
47 fbl::RefPtr<ExceptionPort>* out_eport) {
48 fbl::AllocChecker ac;
49 auto eport = new (&ac) ExceptionPort(type, ktl::move(port), port_key);
50 if (!ac.check())
51 return ZX_ERR_NO_MEMORY;
52
53 // ExceptionPort's ctor causes the first ref to be adopted,
54 // so we should only wrap.
55 *out_eport = fbl::WrapRefPtr<ExceptionPort>(eport);
56 return ZX_OK;
57 }
58
ExceptionPort(Type type,fbl::RefPtr<PortDispatcher> port,uint64_t port_key)59 ExceptionPort::ExceptionPort(Type type, fbl::RefPtr<PortDispatcher> port, uint64_t port_key)
60 : type_(type), port_key_(port_key), port_(port) {
61 LTRACE_ENTRY_OBJ;
62 DEBUG_ASSERT(port_ != nullptr);
63 port_->LinkExceptionPort(this);
64 }
65
~ExceptionPort()66 ExceptionPort::~ExceptionPort() {
67 LTRACE_ENTRY_OBJ;
68 DEBUG_ASSERT(port_ == nullptr);
69 DEBUG_ASSERT(!InContainer());
70 DEBUG_ASSERT(!IsBoundLocked());
71 }
72
SetTarget(const fbl::RefPtr<JobDispatcher> & target)73 void ExceptionPort::SetTarget(const fbl::RefPtr<JobDispatcher>& target) {
74 canary_.Assert();
75
76 LTRACE_ENTRY_OBJ;
77 Guard<fbl::Mutex> guard{&lock_};
78 DEBUG_ASSERT_MSG(type_ == Type::JOB || type_ == Type::JOB_DEBUGGER,
79 "unexpected type %d", static_cast<int>(type_));
80 DEBUG_ASSERT(!IsBoundLocked());
81 DEBUG_ASSERT(target != nullptr);
82 DEBUG_ASSERT(port_ != nullptr);
83 target_ = target;
84 }
85
SetTarget(const fbl::RefPtr<ProcessDispatcher> & target)86 void ExceptionPort::SetTarget(const fbl::RefPtr<ProcessDispatcher>& target) {
87 canary_.Assert();
88
89 LTRACE_ENTRY_OBJ;
90 Guard<fbl::Mutex> guard{&lock_};
91 DEBUG_ASSERT_MSG(type_ == Type::DEBUGGER || type_ == Type::PROCESS,
92 "unexpected type %d", static_cast<int>(type_));
93 DEBUG_ASSERT(!IsBoundLocked());
94 DEBUG_ASSERT(target != nullptr);
95 DEBUG_ASSERT(port_ != nullptr);
96 target_ = target;
97 }
98
SetTarget(const fbl::RefPtr<ThreadDispatcher> & target)99 void ExceptionPort::SetTarget(const fbl::RefPtr<ThreadDispatcher>& target) {
100 canary_.Assert();
101
102 LTRACE_ENTRY_OBJ;
103 Guard<fbl::Mutex> guard{&lock_};
104 DEBUG_ASSERT_MSG(type_ == Type::THREAD,
105 "unexpected type %d", static_cast<int>(type_));
106 DEBUG_ASSERT(!IsBoundLocked());
107 DEBUG_ASSERT(target != nullptr);
108 DEBUG_ASSERT(port_ != nullptr);
109 target_ = target;
110 }
111
112 // Called by PortDispatcher after unlinking us from its eport list.
OnPortZeroHandles()113 void ExceptionPort::OnPortZeroHandles() {
114 canary_.Assert();
115
116 LTRACE_ENTRY_OBJ;
117 Guard<fbl::Mutex> guard{&lock_};
118 if (port_ == nullptr) {
119 // Already unbound. This can happen when
120 // PortDispatcher::on_zero_handles and a manual unbind (via
121 // zx_task_bind_exception_port) race with each other.
122 LTRACEF("already unbound\n");
123 DEBUG_ASSERT(!IsBoundLocked());
124 return;
125 }
126
127 // Unbind ourselves from our target if necessary. At the end of this
128 // block, some thread (ours or another) will have called back into our
129 // ::OnTargetUnbind method, cleaning up our target/port references. The
130 // "other thread" case can happen if another thread manually unbinds after
131 // we release the lock.
132 if (!IsBoundLocked()) {
133 // Created but never bound.
134 guard.Release();
135 // Simulate an unbinding to finish cleaning up.
136 OnTargetUnbind();
137 } else {
138 switch (type_) {
139 case Type::JOB:
140 case Type::JOB_DEBUGGER: {
141 DEBUG_ASSERT(target_ != nullptr);
142 auto job = DownCastDispatcher<JobDispatcher>(&target_);
143 DEBUG_ASSERT(job != nullptr);
144 guard.Release(); // The target may call our ::OnTargetUnbind
145 job->ResetExceptionPort(type_ == Type::JOB_DEBUGGER);
146 break;
147 }
148 case Type::PROCESS:
149 case Type::DEBUGGER: {
150 DEBUG_ASSERT(target_ != nullptr);
151 auto process = DownCastDispatcher<ProcessDispatcher>(&target_);
152 DEBUG_ASSERT(process != nullptr);
153 guard.Release(); // The target may call our ::OnTargetUnbind
154 process->ResetExceptionPort(type_ == Type::DEBUGGER);
155 break;
156 }
157 case Type::THREAD: {
158 DEBUG_ASSERT(target_ != nullptr);
159 auto thread = DownCastDispatcher<ThreadDispatcher>(&target_);
160 DEBUG_ASSERT(thread != nullptr);
161 guard.Release(); // The target may call our ::OnTargetUnbind
162 thread->ResetExceptionPort();
163 break;
164 }
165 default:
166 PANIC("unexpected type %d", static_cast<int>(type_));
167 }
168 }
169 // All cases must release the lock.
170 DEBUG_ASSERT(!lock_.lock().IsHeld());
171
172 #if (LK_DEBUGLEVEL > 1)
173 // The target should have called back into ::OnTargetUnbind by this point,
174 // cleaning up our references.
175 {
176 Guard<fbl::Mutex> guard2{&lock_};
177 DEBUG_ASSERT(port_ == nullptr);
178 DEBUG_ASSERT(!IsBoundLocked());
179 }
180 #endif // if (LK_DEBUGLEVEL > 1)
181
182 LTRACE_EXIT_OBJ;
183 }
184
OnTargetUnbind()185 void ExceptionPort::OnTargetUnbind() {
186 canary_.Assert();
187
188 LTRACE_ENTRY_OBJ;
189 fbl::RefPtr<PortDispatcher> port;
190 {
191 Guard<fbl::Mutex> guard{&lock_};
192 if (port_ == nullptr) {
193 // Already unbound.
194 // This could happen if ::OnPortZeroHandles releases the
195 // lock and another thread immediately does a manual unbind
196 // via zx_task_bind_exception_port.
197 DEBUG_ASSERT(!IsBoundLocked());
198 return;
199 }
200 // Clear port_, indicating that this ExceptionPort has been unbound.
201 port_.swap(port);
202
203 // Drop references to the target.
204 // We may not have a target if the binding (Set*Target) never happened,
205 // so don't require that we're bound.
206 target_.reset();
207 }
208 // It should actually be safe to hold our lock while calling into
209 // the PortDispatcher, but there's no reason to.
210
211 // Unlink ourselves from the PortDispatcher's list.
212 // No-op if this method was ultimately called from
213 // PortDispatcher:on_zero_handles (via ::OnPortZeroHandles).
214 port->UnlinkExceptionPort(this);
215
216 LTRACE_EXIT_OBJ;
217 }
218
PortMatches(const PortDispatcher * port,bool allow_null)219 bool ExceptionPort::PortMatches(const PortDispatcher *port, bool allow_null) {
220 Guard<fbl::Mutex> guard{&lock_};
221 return (allow_null && port_ == nullptr) || port_.get() == port;
222 }
223
SendPacketWorker(uint32_t type,zx_koid_t pid,zx_koid_t tid)224 zx_status_t ExceptionPort::SendPacketWorker(uint32_t type, zx_koid_t pid, zx_koid_t tid) {
225 Guard<fbl::Mutex> guard{&lock_};
226 LTRACEF("%s, type %u, pid %" PRIu64 ", tid %" PRIu64 "\n",
227 port_ == nullptr ? "Not sending exception report on unbound port"
228 : "Sending exception report",
229 type, pid, tid);
230 if (port_ == nullptr) {
231 // The port has been unbound.
232 return ZX_ERR_PEER_CLOSED;
233 }
234
235 auto iopk = MakePacket(port_key_, type, pid, tid);
236 if (!iopk)
237 return ZX_ERR_NO_MEMORY;
238
239 zx_status_t status = port_->Queue(iopk, 0, 0);
240 if (status != ZX_OK) {
241 iopk->Free();
242 }
243 return status;
244 }
245
SendPacket(ThreadDispatcher * thread,uint32_t type)246 zx_status_t ExceptionPort::SendPacket(ThreadDispatcher* thread, uint32_t type) {
247 canary_.Assert();
248
249 zx_koid_t pid = thread->process()->get_koid();
250 zx_koid_t tid = thread->get_koid();
251 return SendPacketWorker(type, pid, tid);
252 }
253
BuildReport(zx_exception_report_t * report,uint32_t type)254 void ExceptionPort::BuildReport(zx_exception_report_t* report, uint32_t type) {
255 memset(report, 0, sizeof(*report));
256 report->header.size = sizeof(*report);
257 report->header.type = type;
258 }
259
BuildArchReport(zx_exception_report_t * report,uint32_t type,const arch_exception_context_t * arch_context)260 void ExceptionPort::BuildArchReport(zx_exception_report_t* report, uint32_t type,
261 const arch_exception_context_t* arch_context) {
262 BuildReport(report, type);
263 arch_fill_in_exception_context(arch_context, report);
264 }
265
OnThreadStartForDebugger(ThreadDispatcher * thread)266 void ExceptionPort::OnThreadStartForDebugger(ThreadDispatcher* thread) {
267 canary_.Assert();
268
269 DEBUG_ASSERT(type_ == Type::DEBUGGER);
270
271 zx_koid_t pid = thread->process()->get_koid();
272 zx_koid_t tid = thread->get_koid();
273 LTRACEF("thread %" PRIu64 ".%" PRIu64 " started\n", pid, tid);
274
275 zx_exception_report_t report;
276 BuildReport(&report, ZX_EXCP_THREAD_STARTING);
277 arch_exception_context_t context;
278 // There is no iframe at the moment. We'll need one (or equivalent) if/when
279 // we want to make $pc, $sp available.
280 memset(&context, 0, sizeof(context));
281 ThreadState::Exception estatus;
282 auto status = thread->ExceptionHandlerExchange(fbl::RefPtr<ExceptionPort>(this), &report, &context, &estatus);
283 if (status != ZX_OK) {
284 // Ignore any errors. There's nothing we can do here, and
285 // we still want the thread to run. It's possible the thread was
286 // killed (status == ZX_ERR_INTERNAL_INTR_KILLED), the kernel will kill the
287 // thread shortly.
288 }
289 }
290
OnProcessStartForDebugger(ThreadDispatcher * thread)291 void ExceptionPort::OnProcessStartForDebugger(ThreadDispatcher* thread) {
292 canary_.Assert();
293
294 DEBUG_ASSERT(type_ == Type::JOB_DEBUGGER);
295
296 zx_koid_t pid = thread->process()->get_koid();
297 zx_koid_t tid = thread->get_koid();
298 LTRACEF("process %" PRIu64 ".%" PRIu64 " started\n", pid, tid);
299
300 zx_exception_report_t report;
301 BuildReport(&report, ZX_EXCP_PROCESS_STARTING);
302 arch_exception_context_t context;
303 // There is no iframe at the moment. We'll need one (or equivalent) if/when
304 // we want to make $pc, $sp available.
305 memset(&context, 0, sizeof(context));
306 ThreadState::Exception estatus;
307 auto status = thread->ExceptionHandlerExchange(fbl::RefPtr<ExceptionPort>(this), &report, &context, &estatus);
308 if (status != ZX_OK) {
309 // Ignore any errors. There's nothing we can do here, and
310 // we still want the thread to run. It's possible the thread was
311 // killed (status == ZX_ERR_INTERNAL_INTR_KILLED), the kernel will kill the
312 // thread shortly.
313 }
314 }
315
316 // This isn't called for every thread's destruction, only when a debugger
317 // is attached.
318
OnThreadExitForDebugger(ThreadDispatcher * thread)319 void ExceptionPort::OnThreadExitForDebugger(ThreadDispatcher* thread) {
320 canary_.Assert();
321
322 DEBUG_ASSERT(type_ == Type::DEBUGGER);
323
324 zx_koid_t pid = thread->process()->get_koid();
325 zx_koid_t tid = thread->get_koid();
326 LTRACEF("thread %" PRIu64 ".%" PRIu64 " exited\n", pid, tid);
327
328 zx_exception_report_t report;
329 BuildReport(&report, ZX_EXCP_THREAD_EXITING);
330 arch_exception_context_t context;
331 // There is no iframe at the moment. We'll need one (or equivalent) if/when
332 // we want to make $pc, $sp available.
333 memset(&context, 0, sizeof(context));
334 ThreadState::Exception estatus;
335 // N.B. If the process is exiting it will have killed all threads. That
336 // means all threads get marked with THREAD_SIGNAL_KILL which means this
337 // exchange will return immediately with ZX_ERR_INTERNAL_INTR_KILLED.
338 auto status = thread->ExceptionHandlerExchange(fbl::RefPtr<ExceptionPort>(this), &report, &context, &estatus);
339 if (status != ZX_OK) {
340 // Ignore any errors, we still want the thread to continue exiting.
341 }
342 }
343