Home | History | Annotate | Download | only in tombstoned
      1 /*
      2  * Copyright 2016, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "intercept_manager.h"
     18 
     19 #include <inttypes.h>
     20 #include <sys/types.h>
     21 
     22 #include <unordered_map>
     23 
     24 #include <event2/event.h>
     25 #include <event2/listener.h>
     26 
     27 #include <android-base/logging.h>
     28 #include <android-base/unique_fd.h>
     29 #include <cutils/sockets.h>
     30 
     31 #include "protocol.h"
     32 #include "util.h"
     33 
     34 using android::base::unique_fd;
     35 
     36 static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
     37   auto intercept = reinterpret_cast<Intercept*>(arg);
     38   InterceptManager* intercept_manager = intercept->intercept_manager;
     39 
     40   CHECK_EQ(sockfd, intercept->sockfd.get());
     41 
     42   // If we can read, either we received unexpected data from the other side, or the other side
     43   // closed their end of the socket. Either way, kill the intercept.
     44 
     45   // Ownership of intercept differs based on whether we've registered it with InterceptManager.
     46   if (!intercept->registered) {
     47     delete intercept;
     48   } else {
     49     auto it = intercept_manager->intercepts.find(intercept->intercept_pid);
     50     if (it == intercept_manager->intercepts.end()) {
     51       LOG(FATAL) << "intercept close callback called after intercept was already removed?";
     52     }
     53     if (it->second.get() != intercept) {
     54       LOG(FATAL) << "intercept close callback has different Intercept from InterceptManager?";
     55     }
     56 
     57     const char* reason;
     58     if ((event & EV_TIMEOUT) != 0) {
     59       reason = "due to timeout";
     60     } else {
     61       reason = "due to input";
     62     }
     63 
     64     LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " and type "
     65               << intercept->dump_type << " terminated: " << reason;
     66     intercept_manager->intercepts.erase(it);
     67   }
     68 }
     69 
     70 static bool is_intercept_request_valid(const InterceptRequest& request) {
     71   if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) {
     72     return false;
     73   }
     74 
     75   if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) {
     76     return false;
     77   }
     78 
     79   return true;
     80 }
     81 
     82 static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
     83   auto intercept = reinterpret_cast<Intercept*>(arg);
     84   InterceptManager* intercept_manager = intercept->intercept_manager;
     85 
     86   CHECK_EQ(sockfd, intercept->sockfd.get());
     87 
     88   if ((ev & EV_TIMEOUT) != 0) {
     89     LOG(WARNING) << "tombstoned didn't receive InterceptRequest before timeout";
     90     goto fail;
     91   } else if ((ev & EV_READ) == 0) {
     92     LOG(WARNING) << "tombstoned received unexpected event on intercept socket";
     93     goto fail;
     94   }
     95 
     96   {
     97     unique_fd rcv_fd;
     98     InterceptRequest intercept_request;
     99     ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
    100 
    101     if (result == -1) {
    102       PLOG(WARNING) << "failed to read from intercept socket";
    103       goto fail;
    104     } else if (result != sizeof(intercept_request)) {
    105       LOG(WARNING) << "intercept socket received short read of length " << result << " (expected "
    106                    << sizeof(intercept_request) << ")";
    107       goto fail;
    108     }
    109 
    110     // Move the received FD to the upper half, in order to more easily notice FD leaks.
    111     int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512);
    112     if (moved_fd == -1) {
    113       LOG(WARNING) << "failed to move received fd (" << rcv_fd.get() << ")";
    114       goto fail;
    115     }
    116     rcv_fd.reset(moved_fd);
    117 
    118     // We trust the other side, so only do minimal validity checking.
    119     if (!is_intercept_request_valid(intercept_request)) {
    120       InterceptResponse response = {};
    121       response.status = InterceptStatus::kFailed;
    122       snprintf(response.error_message, sizeof(response.error_message), "invalid intercept request");
    123       TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
    124       goto fail;
    125     }
    126 
    127     intercept->intercept_pid = intercept_request.pid;
    128     intercept->dump_type = intercept_request.dump_type;
    129 
    130     // Check if it's already registered.
    131     if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
    132       InterceptResponse response = {};
    133       response.status = InterceptStatus::kFailedAlreadyRegistered;
    134       snprintf(response.error_message, sizeof(response.error_message),
    135                "pid %" PRId32 " already intercepted, type %d", intercept_request.pid,
    136                intercept_request.dump_type);
    137       TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
    138       LOG(WARNING) << response.error_message;
    139       goto fail;
    140     }
    141 
    142     // Let the other side know that the intercept has been registered, now that we know we can't
    143     // fail. tombstoned is single threaded, so this isn't racy.
    144     InterceptResponse response = {};
    145     response.status = InterceptStatus::kRegistered;
    146     if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) {
    147       PLOG(WARNING) << "failed to notify interceptor of registration";
    148       goto fail;
    149     }
    150 
    151     intercept->output_fd = std::move(rcv_fd);
    152     intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
    153     intercept->registered = true;
    154 
    155     LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type "
    156               << intercept_request.dump_type;
    157 
    158     // Register a different read event on the socket so that we can remove intercepts if the socket
    159     // closes (e.g. if a user CTRL-C's the process that requested the intercept).
    160     event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
    161                  intercept_close_cb, arg);
    162 
    163     struct timeval timeout = { .tv_sec = 10, .tv_usec = 0 };
    164     event_add(intercept->intercept_event, &timeout);
    165   }
    166 
    167   return;
    168 
    169 fail:
    170   delete intercept;
    171 }
    172 
    173 static void intercept_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
    174                                 void* arg) {
    175   Intercept* intercept = new Intercept();
    176   intercept->intercept_manager = static_cast<InterceptManager*>(arg);
    177   intercept->sockfd.reset(sockfd);
    178 
    179   struct timeval timeout = { 1, 0 };
    180   event_base* base = evconnlistener_get_base(listener);
    181   event* intercept_event =
    182     event_new(base, sockfd, EV_TIMEOUT | EV_READ, intercept_request_cb, intercept);
    183   intercept->intercept_event = intercept_event;
    184   event_add(intercept_event, &timeout);
    185 }
    186 
    187 InterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {
    188   this->listener = evconnlistener_new(base, intercept_accept_cb, this, LEV_OPT_CLOSE_ON_FREE,
    189                                       /* backlog */ -1, intercept_socket);
    190 }
    191 
    192 bool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type,
    193                                     android::base::unique_fd* out_fd) {
    194   auto it = this->intercepts.find(pid);
    195   if (it == this->intercepts.end()) {
    196     return false;
    197   }
    198 
    199   if (dump_type == kDebuggerdAnyIntercept) {
    200     LOG(INFO) << "found registered intercept of type " << it->second->dump_type
    201               << " for requested type kDebuggerdAnyIntercept";
    202   } else if (it->second->dump_type != dump_type) {
    203     LOG(WARNING) << "found non-matching intercept of type " << it->second->dump_type
    204                  << " for requested type: " << dump_type;
    205     return false;
    206   }
    207 
    208   auto intercept = std::move(it->second);
    209   this->intercepts.erase(it);
    210 
    211   LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid
    212             << " and type " << intercept->dump_type;
    213   InterceptResponse response = {};
    214   response.status = InterceptStatus::kStarted;
    215   TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
    216   *out_fd = std::move(intercept->output_fd);
    217 
    218   return true;
    219 }
    220