Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bb7fc92e authored by Josh Gao's avatar Josh Gao
Browse files

Move jdwp connection abstraction from ART apex to adbd apex.

Test: ./art/tools/run-jdwp-tests.sh
Change-Id: I3114b8403f3548700a45df5d7bb72ebe727adec1
parent be27975f
Loading
Loading
Loading
Loading
+60 −5
Original line number Diff line number Diff line
@@ -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",
    ],
}
+1 −0
Original line number Diff line number Diff line
../../.clang-format-2
 No newline at end of file
+148 −0
Original line number Diff line number Diff line
/*
 * 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 <pwd.h>
#include <stddef.h>
#include <stdint.h>
#include <sys/socket.h>
#include <sys/un.h>

#include <memory>
#include <optional>

#include <android-base/cmsg.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>

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

  std::optional<uint64_t> pid;
  std::optional<bool> 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<sockaddr*>(&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<uint32_t>(*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();
}
+129 −0
Original line number Diff line number Diff line
/*
 * 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 <sys/epoll.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>

#include <algorithm>
#include <array>
#include <vector>

#include <android-base/logging.h>
#include <android-base/unique_fd.h>

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<sockaddr_un*>(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<sockaddr*>(&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<unique_fd> pending_connections;

  unique_fd epfd(epoll_create1(EPOLL_CLOEXEC));
  std::array<epoll_event, 16> 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(), &register_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_t>(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);
      }
    }
  }
}
+55 −0
Original line number Diff line number Diff line
/*
 * 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 <stdint.h>
#include <sys/types.h>

#include <android-base/unique_fd.h>

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