[kernel][object] Fix port/eport lock order violation

Link/unlink eports from their ports under the eport lock and have the
port call a new locked variant of PortMatches so the eport lock is
always acquired first.

Test: CQ
ZX-2517 #done

Change-Id: I96c24ac61052794113513a0b0aec266c5c78a704
diff --git a/kernel/object/excp_port.cpp b/kernel/object/excp_port.cpp
index 914f5cd..06dcdbe 100644
--- a/kernel/object/excp_port.cpp
+++ b/kernel/object/excp_port.cpp
@@ -60,7 +60,9 @@
     : type_(type), port_key_(port_key), port_(port) {
     LTRACE_ENTRY_OBJ;
     DEBUG_ASSERT(port_ != nullptr);
-    port_->LinkExceptionPort(this);
+
+    Guard<fbl::Mutex> guard{&lock_};
+    port_->LinkExceptionPortEportLocked(this);
 }
 
 ExceptionPort::~ExceptionPort() {
@@ -204,20 +206,22 @@
         // We may not have a target if the binding (Set*Target) never happened,
         // so don't require that we're bound.
         target_.reset();
-    }
-    // It should actually be safe to hold our lock while calling into
-    // the PortDispatcher, but there's no reason to.
 
-    // Unlink ourselves from the PortDispatcher's list.
-    // No-op if this method was ultimately called from
-    // PortDispatcher:on_zero_handles (via ::OnPortZeroHandles).
-    port->UnlinkExceptionPort(this);
+        // Unlink ourselves from the PortDispatcher's list.
+        // No-op if this method was ultimately called from
+        // PortDispatcher:on_zero_handles (via ::OnPortZeroHandles).
+        port->UnlinkExceptionPortEportLocked(this);
+    }
 
     LTRACE_EXIT_OBJ;
 }
 
 bool ExceptionPort::PortMatches(const PortDispatcher *port, bool allow_null) {
     Guard<fbl::Mutex> guard{&lock_};
+    return PortMatchesLocked(port, allow_null);
+}
+
+bool ExceptionPort::PortMatchesLocked(const PortDispatcher *port, bool allow_null) {
     return (allow_null && port_ == nullptr) || port_.get() == port;
 }
 
diff --git a/kernel/object/include/object/excp_port.h b/kernel/object/include/object/excp_port.h
index 97a6a26..b84e445 100644
--- a/kernel/object/include/object/excp_port.h
+++ b/kernel/object/include/object/excp_port.h
@@ -59,6 +59,7 @@
 
     // Validates that this eport is associated with the given instance.
     bool PortMatches(const PortDispatcher* port, bool allow_null);
+    bool PortMatchesLocked(const PortDispatcher* port, bool allow_null) TA_REQ(lock_);
 
     static void BuildArchReport(zx_exception_report_t* report, uint32_t type,
                                 const arch_exception_context_t* arch_context);
diff --git a/kernel/object/include/object/port_dispatcher.h b/kernel/object/include/object/port_dispatcher.h
index e7e25ed..a7a8bd5 100644
--- a/kernel/object/include/object/port_dispatcher.h
+++ b/kernel/object/include/object/port_dispatcher.h
@@ -7,6 +7,7 @@
 #pragma once
 
 #include <object/dispatcher.h>
+#include <object/excp_port.h>
 #include <object/semaphore.h>
 #include <object/state_observer.h>
 
@@ -77,7 +78,6 @@
 //   when cancellation happens and the port still owns the packet.
 //
 
-class ExceptionPort;
 class PortDispatcher;
 class PortObserver;
 struct PortPacket;
@@ -199,18 +199,18 @@
     bool CancelQueued(PortPacket* port_packet);
 
 private:
-    friend class ExceptionPort;
+    friend ExceptionPort;
 
     explicit PortDispatcher(uint32_t options);
 
     // Adopts a RefPtr to |eport|, and adds it to |eports_|.
-    // Called by ExceptionPort.
-    void LinkExceptionPort(ExceptionPort* eport);
+    // Called by ExceptionPort under |eport|'s lock.
+    void LinkExceptionPortEportLocked(ExceptionPort* eport) TA_REQ(eport->lock_);
 
     // Removes |eport| from |eports_|, dropping its RefPtr.
     // Does nothing if |eport| is not on the list.
-    // Called by ExceptionPort.
-    void UnlinkExceptionPort(ExceptionPort* eport);
+    // Called by ExceptionPort under |eport|'s lock.
+    void UnlinkExceptionPortEportLocked(ExceptionPort* eport) TA_REQ(eport->lock_);
 
     fbl::Canary<fbl::magic("PORT")> canary_;
     const uint32_t options_;
diff --git a/kernel/object/port_dispatcher.cpp b/kernel/object/port_dispatcher.cpp
index 9fc3cc7..7aefae8 100644
--- a/kernel/object/port_dispatcher.cpp
+++ b/kernel/object/port_dispatcher.cpp
@@ -451,20 +451,20 @@
     return false;
 }
 
-void PortDispatcher::LinkExceptionPort(ExceptionPort* eport) {
+void PortDispatcher::LinkExceptionPortEportLocked(ExceptionPort* eport) {
     canary_.Assert();
 
     Guard<fbl::Mutex> guard{get_lock()};
-    DEBUG_ASSERT_COND(eport->PortMatches(this, /* allow_null */ false));
+    DEBUG_ASSERT_COND(eport->PortMatchesLocked(this, /* allow_null */ false));
     DEBUG_ASSERT(!eport->InContainer());
     eports_.push_back(ktl::move(AdoptRef(eport)));
 }
 
-void PortDispatcher::UnlinkExceptionPort(ExceptionPort* eport) {
+void PortDispatcher::UnlinkExceptionPortEportLocked(ExceptionPort* eport) {
     canary_.Assert();
 
     Guard<fbl::Mutex> guard{get_lock()};
-    DEBUG_ASSERT_COND(eport->PortMatches(this, /* allow_null */ true));
+    DEBUG_ASSERT_COND(eport->PortMatchesLocked(this, /* allow_null */ true));
     if (eport->InContainer()) {
         eports_.erase(*eport);
     }