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 26static 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 46zx_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, fbl::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 59ExceptionPort::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 66ExceptionPort::~ExceptionPort() { 67 LTRACE_ENTRY_OBJ; 68 DEBUG_ASSERT(port_ == nullptr); 69 DEBUG_ASSERT(!InContainer()); 70 DEBUG_ASSERT(!IsBoundLocked()); 71} 72 73void 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 86void 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 99void 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. 113void ExceptionPort::OnPortZeroHandles() { 114 canary_.Assert(); 115 116 // TODO(ZX-988): Add a way to mark specific ports as unbinding quietly 117 // when auto-unbinding. 118 static const bool default_quietness = false; 119 120 LTRACE_ENTRY_OBJ; 121 Guard<fbl::Mutex> guard{&lock_}; 122 if (port_ == nullptr) { 123 // Already unbound. This can happen when 124 // PortDispatcher::on_zero_handles and a manual unbind (via 125 // zx_task_bind_exception_port) race with each other. 126 LTRACEF("already unbound\n"); 127 DEBUG_ASSERT(!IsBoundLocked()); 128 return; 129 } 130 131 // Unbind ourselves from our target if necessary. At the end of this 132 // block, some thread (ours or another) will have called back into our 133 // ::OnTargetUnbind method, cleaning up our target/port references. The 134 // "other thread" case can happen if another thread manually unbinds after 135 // we release the lock. 136 if (!IsBoundLocked()) { 137 // Created but never bound. 138 guard.Release(); 139 // Simulate an unbinding to finish cleaning up. 140 OnTargetUnbind(); 141 } else { 142 switch (type_) { 143 case Type::JOB: 144 case Type::JOB_DEBUGGER: { 145 DEBUG_ASSERT(target_ != nullptr); 146 auto job = DownCastDispatcher<JobDispatcher>(&target_); 147 DEBUG_ASSERT(job != nullptr); 148 guard.Release(); // The target may call our ::OnTargetUnbind 149 job->ResetExceptionPort(type_ == Type::JOB_DEBUGGER, default_quietness); 150 break; 151 } 152 case Type::PROCESS: 153 case Type::DEBUGGER: { 154 DEBUG_ASSERT(target_ != nullptr); 155 auto process = DownCastDispatcher<ProcessDispatcher>(&target_); 156 DEBUG_ASSERT(process != nullptr); 157 guard.Release(); // The target may call our ::OnTargetUnbind 158 process->ResetExceptionPort(type_ == Type::DEBUGGER, default_quietness); 159 break; 160 } 161 case Type::THREAD: { 162 DEBUG_ASSERT(target_ != nullptr); 163 auto thread = DownCastDispatcher<ThreadDispatcher>(&target_); 164 DEBUG_ASSERT(thread != nullptr); 165 guard.Release(); // The target may call our ::OnTargetUnbind 166 thread->ResetExceptionPort(default_quietness); 167 break; 168 } 169 default: 170 PANIC("unexpected type %d", static_cast<int>(type_)); 171 } 172 } 173 // All cases must release the lock. 174 DEBUG_ASSERT(!lock_.lock().IsHeld()); 175 176#if (LK_DEBUGLEVEL > 1) 177 // The target should have called back into ::OnTargetUnbind by this point, 178 // cleaning up our references. 179 { 180 Guard<fbl::Mutex> guard2{&lock_}; 181 DEBUG_ASSERT(port_ == nullptr); 182 DEBUG_ASSERT(!IsBoundLocked()); 183 } 184#endif // if (LK_DEBUGLEVEL > 1) 185 186 LTRACE_EXIT_OBJ; 187} 188 189void ExceptionPort::OnTargetUnbind() { 190 canary_.Assert(); 191 192 LTRACE_ENTRY_OBJ; 193 fbl::RefPtr<PortDispatcher> port; 194 { 195 Guard<fbl::Mutex> guard{&lock_}; 196 if (port_ == nullptr) { 197 // Already unbound. 198 // This could happen if ::OnPortZeroHandles releases the 199 // lock and another thread immediately does a manual unbind 200 // via zx_task_bind_exception_port. 201 DEBUG_ASSERT(!IsBoundLocked()); 202 return; 203 } 204 // Clear port_, indicating that this ExceptionPort has been unbound. 205 port_.swap(port); 206 207 // Drop references to the target. 208 // We may not have a target if the binding (Set*Target) never happened, 209 // so don't require that we're bound. 210 target_.reset(); 211 } 212 // It should actually be safe to hold our lock while calling into 213 // the PortDispatcher, but there's no reason to. 214 215 // Unlink ourselves from the PortDispatcher's list. 216 // No-op if this method was ultimately called from 217 // PortDispatcher:on_zero_handles (via ::OnPortZeroHandles). 218 port->UnlinkExceptionPort(this); 219 220 LTRACE_EXIT_OBJ; 221} 222 223bool ExceptionPort::PortMatches(const PortDispatcher *port, bool allow_null) { 224 Guard<fbl::Mutex> guard{&lock_}; 225 return (allow_null && port_ == nullptr) || port_.get() == port; 226} 227 228zx_status_t ExceptionPort::SendPacketWorker(uint32_t type, zx_koid_t pid, zx_koid_t tid) { 229 Guard<fbl::Mutex> guard{&lock_}; 230 LTRACEF("%s, type %u, pid %" PRIu64 ", tid %" PRIu64 "\n", 231 port_ == nullptr ? "Not sending exception report on unbound port" 232 : "Sending exception report", 233 type, pid, tid); 234 if (port_ == nullptr) { 235 // The port has been unbound. 236 return ZX_ERR_PEER_CLOSED; 237 } 238 239 auto iopk = MakePacket(port_key_, type, pid, tid); 240 if (!iopk) 241 return ZX_ERR_NO_MEMORY; 242 243 zx_status_t status = port_->Queue(iopk, 0, 0); 244 if (status != ZX_OK) { 245 iopk->Free(); 246 } 247 return status; 248} 249 250zx_status_t ExceptionPort::SendPacket(ThreadDispatcher* thread, uint32_t type) { 251 canary_.Assert(); 252 253 zx_koid_t pid = thread->process()->get_koid(); 254 zx_koid_t tid = thread->get_koid(); 255 return SendPacketWorker(type, pid, tid); 256} 257 258void ExceptionPort::BuildReport(zx_exception_report_t* report, uint32_t type) { 259 memset(report, 0, sizeof(*report)); 260 report->header.size = sizeof(*report); 261 report->header.type = type; 262} 263 264void ExceptionPort::BuildArchReport(zx_exception_report_t* report, uint32_t type, 265 const arch_exception_context_t* arch_context) { 266 BuildReport(report, type); 267 arch_fill_in_exception_context(arch_context, report); 268} 269 270void ExceptionPort::OnThreadStartForDebugger(ThreadDispatcher* thread) { 271 canary_.Assert(); 272 273 DEBUG_ASSERT(type_ == Type::DEBUGGER); 274 275 zx_koid_t pid = thread->process()->get_koid(); 276 zx_koid_t tid = thread->get_koid(); 277 LTRACEF("thread %" PRIu64 ".%" PRIu64 " started\n", pid, tid); 278 279 zx_exception_report_t report; 280 BuildReport(&report, ZX_EXCP_THREAD_STARTING); 281 arch_exception_context_t context; 282 // There is no iframe at the moment. We'll need one (or equivalent) if/when 283 // we want to make $pc, $sp available. 284 memset(&context, 0, sizeof(context)); 285 ThreadDispatcher::ExceptionStatus estatus; 286 auto status = thread->ExceptionHandlerExchange(fbl::RefPtr<ExceptionPort>(this), &report, &context, &estatus); 287 if (status != ZX_OK) { 288 // Ignore any errors. There's nothing we can do here, and 289 // we still want the thread to run. It's possible the thread was 290 // killed (status == ZX_ERR_INTERNAL_INTR_KILLED), the kernel will kill the 291 // thread shortly. 292 } 293} 294 295void ExceptionPort::OnProcessStartForDebugger(ThreadDispatcher* thread) { 296 canary_.Assert(); 297 298 DEBUG_ASSERT(type_ == Type::JOB_DEBUGGER); 299 300 zx_koid_t pid = thread->process()->get_koid(); 301 zx_koid_t tid = thread->get_koid(); 302 LTRACEF("process %" PRIu64 ".%" PRIu64 " started\n", pid, tid); 303 304 zx_exception_report_t report; 305 BuildReport(&report, ZX_EXCP_PROCESS_STARTING); 306 arch_exception_context_t context; 307 // There is no iframe at the moment. We'll need one (or equivalent) if/when 308 // we want to make $pc, $sp available. 309 memset(&context, 0, sizeof(context)); 310 ThreadDispatcher::ExceptionStatus estatus; 311 auto status = thread->ExceptionHandlerExchange(fbl::RefPtr<ExceptionPort>(this), &report, &context, &estatus); 312 if (status != ZX_OK) { 313 // Ignore any errors. There's nothing we can do here, and 314 // we still want the thread to run. It's possible the thread was 315 // killed (status == ZX_ERR_INTERNAL_INTR_KILLED), the kernel will kill the 316 // thread shortly. 317 } 318} 319 320// This isn't called for every thread's destruction, only when a debugger 321// is attached. 322 323void ExceptionPort::OnThreadExitForDebugger(ThreadDispatcher* thread) { 324 canary_.Assert(); 325 326 DEBUG_ASSERT(type_ == Type::DEBUGGER); 327 328 zx_koid_t pid = thread->process()->get_koid(); 329 zx_koid_t tid = thread->get_koid(); 330 LTRACEF("thread %" PRIu64 ".%" PRIu64 " exited\n", pid, tid); 331 332 zx_exception_report_t report; 333 BuildReport(&report, ZX_EXCP_THREAD_EXITING); 334 arch_exception_context_t context; 335 // There is no iframe at the moment. We'll need one (or equivalent) if/when 336 // we want to make $pc, $sp available. 337 memset(&context, 0, sizeof(context)); 338 ThreadDispatcher::ExceptionStatus estatus; 339 // N.B. If the process is exiting it will have killed all threads. That 340 // means all threads get marked with THREAD_SIGNAL_KILL which means this 341 // exchange will return immediately with ZX_ERR_INTERNAL_INTR_KILLED. 342 auto status = thread->ExceptionHandlerExchange(fbl::RefPtr<ExceptionPort>(this), &report, &context, &estatus); 343 if (status != ZX_OK) { 344 // Ignore any errors, we still want the thread to continue exiting. 345 } 346} 347