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
intercept_close_cb(evutil_socket_t sockfd,short event,void * arg)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
is_intercept_request_valid(const InterceptRequest & request)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
intercept_request_cb(evutil_socket_t sockfd,short ev,void * arg)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
intercept_accept_cb(evconnlistener * listener,evutil_socket_t sockfd,sockaddr *,int,void * arg)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
InterceptManager(event_base * base,int intercept_socket)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
GetIntercept(pid_t pid,DebuggerdDumpType dump_type,android::base::unique_fd * out_fd)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