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);