diff --git a/adb/Android.bp b/adb/Android.bp index b39defee8..7c5e3ea69 100644 --- a/adb/Android.bp +++ b/adb/Android.bp @@ -114,6 +114,61 @@ cc_defaults { }, } +// libadbconnection +// ========================================================= +// libadbconnection_client/server implement the socket handling for jdwp +// forwarding and the track-jdwp service. +cc_library { + name: "libadbconnection_server", + srcs: ["adbconnection/adbconnection_server.cpp"], + + export_include_dirs: ["adbconnection/include"], + + stl: "libc++_static", + shared_libs: ["liblog"], + static_libs: ["libbase"], + + defaults: ["adbd_defaults", "host_adbd_supported"], + + // Avoid getting duplicate symbol of android::build::getbuildnumber(). + use_version_lib: false, + + recovery_available: true, + compile_multilib: "both", +} + +cc_library { + name: "libadbconnection_client", + srcs: ["adbconnection/adbconnection_client.cpp"], + + export_include_dirs: ["adbconnection/include"], + + stl: "libc++_static", + shared_libs: ["liblog"], + static_libs: ["libbase"], + + defaults: ["adbd_defaults"], + visibility: [ + "//art:__subpackages__", + "//system/core/adb/apex:__subpackages__", + ], + apex_available: [ + "com.android.adbd", + "test_com.android.adbd", + ], + + // libadbconnection_client doesn't need an embedded build number. + use_version_lib: false, + + stubs: { + symbol_file: "adbconnection/libadbconnection_client.map.txt", + versions: ["1"], + }, + + host_supported: true, + compile_multilib: "both", +} + // libadb // ========================================================= // These files are compiled for both the host and the device. @@ -358,11 +413,11 @@ cc_library_static { generated_headers: ["platform_tools_version"], static_libs: [ + "libadbconnection_server", "libdiagnose_usb", ], shared_libs: [ - "libadbconnection_server", "libadbd_auth", "libasyncio", "libbase", @@ -411,12 +466,12 @@ cc_library { ], static_libs: [ + "libadbconnection_server", "libadbd_core", "libdiagnose_usb", ], shared_libs: [ - "libadbconnection_server", "libadbd_auth", "libasyncio", "libbase", @@ -452,7 +507,7 @@ cc_library { defaults: ["adbd_defaults", "host_adbd_supported"], recovery_available: true, - // Avoid getting duplicate symbol of android::build::GetBuildNumber(). + // avoid getting duplicate symbol of android::build::getbuildnumber(). use_version_lib: false, // libminadbd wants both, as it's used to build native tests. @@ -460,11 +515,11 @@ cc_library { // libadbd doesn't build any additional source, but to expose libadbd_core as a shared library. whole_static_libs: [ + "libadbconnection_server", "libadbd_core", ], shared_libs: [ - "libadbconnection_server", "libadbd_auth", "libadbd_services", "libasyncio", @@ -500,6 +555,7 @@ cc_binary { stl: "libc++_static", static_libs: [ + "libadbconnection_server", "libadbd", "libadbd_auth", "libadbd_services", @@ -516,7 +572,6 @@ cc_binary { ], shared_libs: [ - "libadbconnection_server", "libcrypto", ], } diff --git a/adb/adbconnection/.clang-format b/adb/adbconnection/.clang-format new file mode 120000 index 000000000..e545823f4 --- /dev/null +++ b/adb/adbconnection/.clang-format @@ -0,0 +1 @@ +../../.clang-format-2 \ No newline at end of file diff --git a/adb/adbconnection/adbconnection_client.cpp b/adb/adbconnection/adbconnection_client.cpp new file mode 100644 index 000000000..ee48abb03 --- /dev/null +++ b/adb/adbconnection/adbconnection_client.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "adbconnection/client.h" + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +using android::base::unique_fd; + +static constexpr char kJdwpControlName[] = "\0jdwp-control"; + +struct AdbConnectionClientContext { + unique_fd control_socket_; +}; + +bool SocketPeerIsTrusted(int fd) { + ucred cr; + socklen_t cr_length = sizeof(cr); + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &cr_length) != 0) { + PLOG(ERROR) << "couldn't get socket credentials"; + return false; + } + + passwd* shell = getpwnam("shell"); + if (cr.uid != 0 && cr.uid != shell->pw_uid) { + LOG(ERROR) << "untrusted uid " << cr.uid << " on other end of socket"; + return false; + } + + return true; +} + +AdbConnectionClientContext* adbconnection_client_new( + const AdbConnectionClientInfo* const* info_elems, size_t info_count) { + auto ctx = std::make_unique(); + + std::optional pid; + std::optional debuggable; + + for (size_t i = 0; i < info_count; ++i) { + auto info = info_elems[i]; + switch (info->type) { + case AdbConnectionClientInfoType::pid: + if (pid) { + LOG(ERROR) << "multiple pid entries in AdbConnectionClientInfo, ignoring"; + continue; + } + pid = info->data.pid; + break; + + case AdbConnectionClientInfoType::debuggable: + if (debuggable) { + LOG(ERROR) << "multiple debuggable entries in AdbConnectionClientInfo, ignoring"; + continue; + } + debuggable = info->data.pid; + break; + } + } + + if (!pid) { + LOG(ERROR) << "AdbConnectionClientInfo missing required field pid"; + return nullptr; + } + + if (!debuggable) { + LOG(ERROR) << "AdbConnectionClientInfo missing required field debuggable"; + return nullptr; + } + + ctx->control_socket_.reset(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0)); + if (ctx->control_socket_ < 0) { + PLOG(ERROR) << "failed to create Unix domain socket"; + return nullptr; + } + + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + setsockopt(ctx->control_socket_.get(), SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); + + sockaddr_un addr = {}; + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, kJdwpControlName, sizeof(kJdwpControlName)); + size_t addr_len = offsetof(sockaddr_un, sun_path) + sizeof(kJdwpControlName) - 1; + + int rc = connect(ctx->control_socket_.get(), reinterpret_cast(&addr), addr_len); + if (rc != 0) { + PLOG(ERROR) << "failed to connect to jdwp control socket"; + return nullptr; + } + + bool trusted = SocketPeerIsTrusted(ctx->control_socket_.get()); + if (!trusted) { + LOG(ERROR) << "adb socket is not trusted, aborting connection"; + return nullptr; + } + + uint32_t pid_u32 = static_cast(*pid); + rc = TEMP_FAILURE_RETRY(write(ctx->control_socket_.get(), &pid_u32, sizeof(pid_u32))); + if (rc != sizeof(pid_u32)) { + PLOG(ERROR) << "failed to send JDWP process pid to adbd"; + } + + return ctx.release(); +} + +void adbconnection_client_destroy(AdbConnectionClientContext* ctx) { + delete ctx; +} + +int adbconnection_client_pollfd(AdbConnectionClientContext* ctx) { + return ctx->control_socket_.get(); +} + +int adbconnection_client_receive_jdwp_fd(AdbConnectionClientContext* ctx) { + char dummy; + unique_fd jdwp_fd; + ssize_t rc = android::base::ReceiveFileDescriptors(ctx->control_socket_, &dummy, 1, &jdwp_fd); + if (rc != 1) { + return rc; + } + return jdwp_fd.release(); +} diff --git a/adb/adbconnection/adbconnection_server.cpp b/adb/adbconnection/adbconnection_server.cpp new file mode 100644 index 000000000..939da2f64 --- /dev/null +++ b/adb/adbconnection/adbconnection_server.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "adbconnection/server.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +using android::base::unique_fd; + +#define JDWP_CONTROL_NAME "\0jdwp-control" +#define JDWP_CONTROL_NAME_LEN (sizeof(JDWP_CONTROL_NAME) - 1) + +static_assert(JDWP_CONTROL_NAME_LEN <= sizeof(reinterpret_cast(0)->sun_path)); + +// Listen for incoming jdwp clients forever. +void adbconnection_listen(void (*callback)(int fd, pid_t pid)) { + sockaddr_un addr = {}; + socklen_t addrlen = JDWP_CONTROL_NAME_LEN + sizeof(addr.sun_family); + + addr.sun_family = AF_UNIX; + memcpy(addr.sun_path, JDWP_CONTROL_NAME, JDWP_CONTROL_NAME_LEN); + + unique_fd s(socket(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, 0)); + if (s < 0) { + PLOG(ERROR) << "failed to create JDWP control socket"; + return; + } + + if (bind(s.get(), reinterpret_cast(&addr), addrlen) < 0) { + PLOG(ERROR) << "failed to bind JDWP control socket"; + return; + } + + if (listen(s.get(), 4) < 0) { + PLOG(ERROR) << "failed to listen on JDWP control socket"; + return; + } + + std::vector pending_connections; + + unique_fd epfd(epoll_create1(EPOLL_CLOEXEC)); + std::array events; + + events[0].events = EPOLLIN; + events[0].data.fd = -1; + if (epoll_ctl(epfd.get(), EPOLL_CTL_ADD, s.get(), &events[0]) != 0) { + LOG(FATAL) << "failed to register event with epoll fd"; + } + + while (true) { + int epoll_rc = TEMP_FAILURE_RETRY(epoll_wait(epfd.get(), events.data(), events.size(), -1)); + if (epoll_rc == -1) { + PLOG(FATAL) << "epoll_wait failed"; + } + + for (int i = 0; i < epoll_rc; ++i) { + const epoll_event& event = events[i]; + if (event.data.fd == -1) { + unique_fd client( + TEMP_FAILURE_RETRY(accept4(s.get(), nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC))); + + if (client == -1) { + PLOG(WARNING) << "failed to accept client on JDWP control socket"; + continue; + } + + epoll_event register_event; + register_event.events = EPOLLIN; + register_event.data.fd = client.get(); + + if (epoll_ctl(epfd.get(), EPOLL_CTL_ADD, client.get(), ®ister_event) != 0) { + PLOG(FATAL) << "failed to register JDWP client with epoll"; + } + + pending_connections.emplace_back(std::move(client)); + } else { + // n^2, but the backlog should be short. + auto it = std::find_if(pending_connections.begin(), pending_connections.end(), + [&](const unique_fd& fd) { return fd.get() == event.data.fd; }); + + if (it == pending_connections.end()) { + LOG(FATAL) << "failed to find JDWP client (" << event.data.fd + << ") in pending connections"; + } + + // Massively oversized buffer: we're expecting an int32_t from the other end. + char buf[32]; + int rc = TEMP_FAILURE_RETRY(recv(it->get(), buf, sizeof(buf), MSG_DONTWAIT)); + if (rc != 4) { + LOG(ERROR) << "received data of incorrect size from JDWP client: read " << rc + << ", expected 4"; + } else { + int32_t pid; + memcpy(&pid, buf, sizeof(pid)); + callback(it->release(), static_cast(pid)); + } + + if (epoll_ctl(epfd.get(), EPOLL_CTL_DEL, event.data.fd, nullptr) != 0) { + LOG(FATAL) << "failed to delete fd from JDWP epoll fd"; + } + + pending_connections.erase(it); + } + } + } +} diff --git a/adb/adbconnection/include/adbconnection/client.h b/adb/adbconnection/include/adbconnection/client.h new file mode 100644 index 000000000..692fea00d --- /dev/null +++ b/adb/adbconnection/include/adbconnection/client.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include + +extern "C" { + +struct AdbConnectionClientContext; + +enum AdbConnectionClientInfoType { + pid, + debuggable, +}; + +struct AdbConnectionClientInfo { + AdbConnectionClientInfoType type; + union { + uint64_t pid; + bool debuggable; + } data; +}; + +// Construct a context and connect to adbd. +// Returns null if we fail to connect to adbd. +AdbConnectionClientContext* adbconnection_client_new( + const AdbConnectionClientInfo* const* info_elems, size_t info_count); + +void adbconnection_client_destroy(AdbConnectionClientContext* ctx); + +// Get an fd which can be polled upon to detect when a jdwp socket is available. +// You do not own this fd. Do not close it. +int adbconnection_client_pollfd(AdbConnectionClientContext* ctx); + +// Receive a jdwp client fd. +// Ownership is transferred to the caller of this function. +int adbconnection_client_receive_jdwp_fd(AdbConnectionClientContext* ctx); +} diff --git a/adb/adbconnection/include/adbconnection/server.h b/adb/adbconnection/include/adbconnection/server.h new file mode 100644 index 000000000..57ca6cdee --- /dev/null +++ b/adb/adbconnection/include/adbconnection/server.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include + +extern "C" { + +void adbconnection_listen(void (*callback)(int fd, pid_t pid)); +} diff --git a/adb/adbconnection/libadbconnection_client.map.txt b/adb/adbconnection/libadbconnection_client.map.txt new file mode 100644 index 000000000..153a0e41b --- /dev/null +++ b/adb/adbconnection/libadbconnection_client.map.txt @@ -0,0 +1,25 @@ +# +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LIBADBCONNECTION_CLIENT_1 { + global: + adbconnection_client_new; + adbconnection_client_destroy; + adbconnection_client_pollfd; + adbconnection_client_receive_jdwp_fd; + local: + *; +}; diff --git a/adb/apex/Android.bp b/adb/apex/Android.bp index 40ea448db..75c4ed9db 100644 --- a/adb/apex/Android.bp +++ b/adb/apex/Android.bp @@ -2,6 +2,12 @@ apex_defaults { name: "com.android.adbd-defaults", binaries: ["adbd"], + compile_multilib: "both", + multilib: { + both: { + native_shared_libs: ["libadbconnection_client"], + }, + }, prebuilts: ["com.android.adbd.init.rc", "com.android.adbd.ld.config.txt"], key: "com.android.adbd.key",