am 4f8d5b01: Merge "adb: disconnect: fix write-after-free memory corruption and crash."

* commit '4f8d5b01281e751168718c7b0a74db34352aaf1e':
  adb: disconnect: fix write-after-free memory corruption and crash.
diff --git a/adb/adb.cpp b/adb/adb.cpp
index a0501a6..d6e6d91 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -972,8 +972,7 @@
     if (!strncmp(service, "disconnect:", 11)) {
         const std::string address(service + 11);
         if (address.empty()) {
-            // disconnect from all TCP devices
-            unregister_all_tcp_transports();
+            kick_all_tcp_devices();
             return SendOkay(reply_fd, "disconnected everything");
         }
 
@@ -990,7 +989,7 @@
             return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'",
                                                                   serial.c_str()));
         }
-        unregister_transport(t);
+        kick_transport(t);
         return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
     }
 
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 20d5d71..46c709f 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -28,6 +28,7 @@
 
 #include <list>
 
+#include <base/logging.h>
 #include <base/stringprintf.h>
 #include <base/strings.h>
 
@@ -41,23 +42,6 @@
 
 ADB_MUTEX_DEFINE( transport_lock );
 
-void kick_transport(atransport* t)
-{
-    if (t != nullptr)
-    {
-        int  kicked;
-
-        adb_mutex_lock(&transport_lock);
-        kicked = t->kicked;
-        if (!kicked)
-            t->kicked = 1;
-        adb_mutex_unlock(&transport_lock);
-
-        if (!kicked)
-            t->kick(t);
-    }
-}
-
 // Each atransport contains a list of adisconnects (t->disconnects).
 // An adisconnect contains a link to the next/prev adisconnect, a function
 // pointer to a disconnect callback which takes a void* piece of user data and
@@ -336,6 +320,19 @@
     return 0;
 }
 
+static void kick_transport_locked(atransport* t) {
+    CHECK(t != nullptr);
+    if (!t->kicked) {
+        t->kicked = true;
+        t->kick(t);
+    }
+}
+
+void kick_transport(atransport* t) {
+    adb_mutex_lock(&transport_lock);
+    kick_transport_locked(t);
+    adb_mutex_unlock(&transport_lock);
+}
 
 static int transport_registration_send = -1;
 static int transport_registration_recv = -1;
@@ -642,29 +639,20 @@
 }
 
 
-static void transport_unref_locked(atransport *t)
-{
+static void transport_unref(atransport* t) {
+    CHECK(t != nullptr);
+    adb_mutex_lock(&transport_lock);
+    CHECK_GT(t->ref_count, 0u);
     t->ref_count--;
     if (t->ref_count == 0) {
         D("transport: %s unref (kicking and closing)\n", t->serial);
-        if (!t->kicked) {
-            t->kicked = 1;
-            t->kick(t);
-        }
+        kick_transport_locked(t);
         t->close(t);
         remove_transport(t);
     } else {
-        D("transport: %s unref (count=%d)\n", t->serial, t->ref_count);
+        D("transport: %s unref (count=%zu)\n", t->serial, t->ref_count);
     }
-}
-
-static void transport_unref(atransport *t)
-{
-    if (t) {
-        adb_mutex_lock(&transport_lock);
-        transport_unref_locked(t);
-        adb_mutex_unlock(&transport_lock);
-    }
+    adb_mutex_unlock(&transport_lock);
 }
 
 void add_transport_disconnect(atransport*  t, adisconnect*  dis)
@@ -967,7 +955,7 @@
     atransport* result = nullptr;
 
     adb_mutex_lock(&transport_lock);
-    for (auto t : transport_list) {
+    for (auto& t : transport_list) {
         if (t->serial && strcmp(serial, t->serial) == 0) {
             result = t;
             break;
@@ -978,35 +966,18 @@
     return result;
 }
 
-void unregister_transport(atransport *t)
-{
+void kick_all_tcp_devices() {
     adb_mutex_lock(&transport_lock);
-    transport_list.remove(t);
-    adb_mutex_unlock(&transport_lock);
-
-    kick_transport(t);
-    transport_unref(t);
-}
-
-// Unregisters all non-emulator TCP transports.
-void unregister_all_tcp_transports() {
-    adb_mutex_lock(&transport_lock);
-    for (auto it = transport_list.begin(); it != transport_list.end(); ) {
-        atransport* t = *it;
+    for (auto& t : transport_list) {
+        // TCP/IP devices have adb_port == 0.
         if (t->type == kTransportLocal && t->adb_port == 0) {
-            // We cannot call kick_transport when holding transport_lock.
-            if (!t->kicked) {
-                t->kicked = 1;
-                t->kick(t);
-            }
-            transport_unref_locked(t);
-
-            it = transport_list.erase(it);
-        } else {
-            ++it;
+            // Kicking breaks the output thread of this transport out of any read, then
+            // the output thread will notify the main thread to make this transport
+            // offline. Then the main thread will notify the input thread to exit.
+            // Finally, this transport will be closed and freed in the main thread.
+            kick_transport_locked(t);
         }
     }
-
     adb_mutex_unlock(&transport_lock);
 }
 
diff --git a/adb/transport.h b/adb/transport.h
index e809407..abb26a7 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -52,7 +52,7 @@
     int fd = -1;
     int transport_socket = -1;
     fdevent transport_fde;
-    int ref_count = 0;
+    size_t ref_count = 0;
     uint32_t sync_token = 0;
     ConnectionState connection_state = kCsOffline;
     bool online = false;
@@ -120,12 +120,10 @@
 void run_transport_disconnects(atransport* t);
 void update_transports(void);
 
-/* transports are ref-counted
-** get_device_transport does an acquire on your behalf before returning
-*/
 void init_transport_registration(void);
 std::string list_transports(bool long_listing);
 atransport* find_transport(const char* serial);
+void kick_all_tcp_devices();
 
 void register_usb_transport(usb_handle* h, const char* serial,
                             const char* devpath, unsigned writeable);
@@ -136,10 +134,6 @@
 // This should only be used for transports with connection_state == kCsNoPerm.
 void unregister_usb_transport(usb_handle* usb);
 
-/* these should only be used for the "adb disconnect" command */
-void unregister_transport(atransport* t);
-void unregister_all_tcp_transports();
-
 int check_header(apacket* p, atransport* t);
 int check_data(apacket* p);