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 "debuggerd/protocol.h"
32 #include "debuggerd/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 << " terminated " << reason;
65     intercept_manager->intercepts.erase(it);
66   }
67 }
68 
intercept_request_cb(evutil_socket_t sockfd,short ev,void * arg)69 static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
70   auto intercept = reinterpret_cast<Intercept*>(arg);
71   InterceptManager* intercept_manager = intercept->intercept_manager;
72 
73   CHECK_EQ(sockfd, intercept->sockfd.get());
74 
75   if ((ev & EV_TIMEOUT) != 0) {
76     LOG(WARNING) << "tombstoned didn't receive InterceptRequest before timeout";
77     goto fail;
78   } else if ((ev & EV_READ) == 0) {
79     LOG(WARNING) << "tombstoned received unexpected event on intercept socket";
80     goto fail;
81   }
82 
83   {
84     unique_fd rcv_fd;
85     InterceptRequest intercept_request;
86     ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
87 
88     if (result == -1) {
89       PLOG(WARNING) << "failed to read from intercept socket";
90       goto fail;
91     } else if (result != sizeof(intercept_request)) {
92       LOG(WARNING) << "intercept socket received short read of length " << result << " (expected "
93                    << sizeof(intercept_request) << ")";
94       goto fail;
95     }
96 
97     // Move the received FD to the upper half, in order to more easily notice FD leaks.
98     int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512);
99     if (moved_fd == -1) {
100       LOG(WARNING) << "failed to move received fd (" << rcv_fd.get() << ")";
101       goto fail;
102     }
103     rcv_fd.reset(moved_fd);
104 
105     // We trust the other side, so only do minimal validity checking.
106     if (intercept_request.pid <= 0 || intercept_request.pid > std::numeric_limits<pid_t>::max()) {
107       InterceptResponse response = {};
108       response.status = InterceptStatus::kFailed;
109       snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
110                intercept_request.pid);
111       TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
112       goto fail;
113     }
114 
115     intercept->intercept_pid = intercept_request.pid;
116 
117     // Check if it's already registered.
118     if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
119       InterceptResponse response = {};
120       response.status = InterceptStatus::kFailed;
121       snprintf(response.error_message, sizeof(response.error_message),
122                "pid %" PRId32 " already intercepted", intercept_request.pid);
123       TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
124       LOG(WARNING) << response.error_message;
125       goto fail;
126     }
127 
128     // Let the other side know that the intercept has been registered, now that we know we can't
129     // fail. tombstoned is single threaded, so this isn't racy.
130     InterceptResponse response = {};
131     response.status = InterceptStatus::kRegistered;
132     if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) {
133       PLOG(WARNING) << "failed to notify interceptor of registration";
134       goto fail;
135     }
136 
137     intercept->output_fd = std::move(rcv_fd);
138     intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
139     intercept->registered = true;
140 
141     LOG(INFO) << "tombstoned registered intercept for pid " << intercept_request.pid;
142 
143     // Register a different read event on the socket so that we can remove intercepts if the socket
144     // closes (e.g. if a user CTRL-C's the process that requested the intercept).
145     event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
146                  intercept_close_cb, arg);
147 
148     struct timeval timeout = { .tv_sec = 10, .tv_usec = 0 };
149     event_add(intercept->intercept_event, &timeout);
150   }
151 
152   return;
153 
154 fail:
155   delete intercept;
156 }
157 
intercept_accept_cb(evconnlistener * listener,evutil_socket_t sockfd,sockaddr *,int,void * arg)158 static void intercept_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
159                                 void* arg) {
160   Intercept* intercept = new Intercept();
161   intercept->intercept_manager = static_cast<InterceptManager*>(arg);
162   intercept->sockfd.reset(sockfd);
163 
164   struct timeval timeout = { 1, 0 };
165   event_base* base = evconnlistener_get_base(listener);
166   event* intercept_event =
167     event_new(base, sockfd, EV_TIMEOUT | EV_READ, intercept_request_cb, intercept);
168   intercept->intercept_event = intercept_event;
169   event_add(intercept_event, &timeout);
170 }
171 
InterceptManager(event_base * base,int intercept_socket)172 InterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {
173   this->listener = evconnlistener_new(base, intercept_accept_cb, this, -1, LEV_OPT_CLOSE_ON_FREE,
174                                       intercept_socket);
175 }
176 
GetIntercept(pid_t pid,android::base::unique_fd * out_fd)177 bool InterceptManager::GetIntercept(pid_t pid, android::base::unique_fd* out_fd) {
178   auto it = this->intercepts.find(pid);
179   if (it == this->intercepts.end()) {
180     return false;
181   }
182 
183   auto intercept = std::move(it->second);
184   this->intercepts.erase(it);
185 
186   LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
187   InterceptResponse response = {};
188   response.status = InterceptStatus::kStarted;
189   TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
190   *out_fd = std::move(intercept->output_fd);
191 
192   return true;
193 }
194