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