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