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 <limits>
23 #include <memory>
24 #include <unordered_map>
25 #include <utility>
26 
27 #include <event2/event.h>
28 #include <event2/listener.h>
29 
30 #include <android-base/cmsg.h>
31 #include <android-base/logging.h>
32 #include <android-base/properties.h>
33 #include <android-base/unique_fd.h>
34 
35 #include "protocol.h"
36 #include "util.h"
37 
38 using android::base::ReceiveFileDescriptors;
39 using android::base::unique_fd;
40 
intercept_close_cb(evutil_socket_t sockfd,short event,void * arg)41 static void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) {
42   std::unique_ptr<Intercept> intercept(reinterpret_cast<Intercept*>(arg));
43 
44   CHECK_EQ(sockfd, intercept->sockfd.get());
45 
46   // If we can read, either we received unexpected data from the other side, or the other side
47   // closed their end of the socket. Either way, kill the intercept.
48 
49   // Ownership of intercept differs based on whether we've registered it with InterceptManager.
50   if (!intercept->registered) {
51     LOG(WARNING) << "intercept for pid " << intercept->pid << " and type " << intercept->dump_type
52                  << " closed before being registered.";
53     return;
54   }
55 
56   const char* reason = (event & EV_TIMEOUT) ? "due to timeout" : "due to input";
57   LOG(INFO) << "intercept for pid " << intercept->pid << " and type " << intercept->dump_type
58             << " terminated: " << reason;
59 }
60 
Unregister(Intercept * intercept)61 void InterceptManager::Unregister(Intercept* intercept) {
62   CHECK(intercept->registered);
63   auto pid_entry = intercepts.find(intercept->pid);
64   if (pid_entry == intercepts.end()) {
65     LOG(FATAL) << "No intercepts found for pid " << intercept->pid;
66   }
67   auto& dump_type_hash = pid_entry->second;
68   auto dump_type_entry = dump_type_hash.find(intercept->dump_type);
69   if (dump_type_entry == dump_type_hash.end()) {
70     LOG(FATAL) << "Unknown intercept " << intercept->pid << " " << intercept->dump_type;
71   }
72   if (intercept != dump_type_entry->second) {
73     LOG(FATAL) << "Mismatch pointer trying to unregister intercept " << intercept->pid << " "
74                << intercept->dump_type;
75   }
76 
77   dump_type_hash.erase(dump_type_entry);
78   if (dump_type_hash.empty()) {
79     intercepts.erase(pid_entry);
80   }
81 }
82 
intercept_request_cb(evutil_socket_t sockfd,short ev,void * arg)83 static void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) {
84   std::unique_ptr<Intercept> intercept(reinterpret_cast<Intercept*>(arg));
85   InterceptManager* intercept_manager = intercept->intercept_manager;
86 
87   CHECK_EQ(sockfd, intercept->sockfd.get());
88 
89   if ((ev & EV_TIMEOUT) != 0) {
90     LOG(WARNING) << "tombstoned didn't receive InterceptRequest before timeout";
91     return;
92   } else if ((ev & EV_READ) == 0) {
93     LOG(WARNING) << "tombstoned received unexpected event on intercept socket";
94     return;
95   }
96 
97   unique_fd rcv_fd;
98   InterceptRequest intercept_request;
99   ssize_t result =
100       ReceiveFileDescriptors(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd);
101 
102   if (result == -1) {
103     PLOG(WARNING) << "failed to read from intercept socket";
104     return;
105   }
106   if (result != sizeof(intercept_request)) {
107     LOG(WARNING) << "intercept socket received short read of length " << result << " (expected "
108                  << sizeof(intercept_request) << ")";
109     return;
110   }
111 
112   // Move the received FD to the upper half, in order to more easily notice FD leaks.
113   int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512);
114   if (moved_fd == -1) {
115     LOG(WARNING) << "failed to move received fd (" << rcv_fd.get() << ")";
116     return;
117   }
118   rcv_fd.reset(moved_fd);
119 
120   // See if we can properly register the intercept.
121   InterceptResponse response = {};
122   if (!intercept_manager->CanRegister(intercept_request, response)) {
123     TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
124     LOG(WARNING) << response.error_message;
125     return;
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   response.status = InterceptStatus::kRegistered;
131   if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) {
132     PLOG(WARNING) << "failed to notify interceptor of registration";
133     return;
134   }
135 
136   intercept->pid = intercept_request.pid;
137   intercept->dump_type = intercept_request.dump_type;
138   intercept->output_fd = std::move(rcv_fd);
139   intercept_manager->Register(intercept.get());
140 
141   LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type "
142             << intercept_request.dump_type;
143 
144   // Register a different read event on the socket so that we can remove intercepts if the socket
145   // closes (e.g. if a user CTRL-C's the process that requested the intercept).
146   event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT,
147                intercept_close_cb, arg);
148 
149   // If no request comes in, then this will close the intercept and free the pointer.
150   struct timeval timeout = {.tv_sec = 10 * android::base::HwTimeoutMultiplier(), .tv_usec = 0};
151   event_add(intercept->intercept_event, &timeout);
152   intercept.release();
153 }
154 
intercept_accept_cb(evconnlistener * listener,evutil_socket_t sockfd,sockaddr *,int,void * arg)155 static void intercept_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
156                                 void* arg) {
157   Intercept* intercept = new Intercept();
158   intercept->intercept_manager = static_cast<InterceptManager*>(arg);
159   intercept->sockfd.reset(sockfd);
160 
161   struct timeval timeout = {1 * android::base::HwTimeoutMultiplier(), 0};
162   event_base* base = evconnlistener_get_base(listener);
163   event* intercept_event =
164     event_new(base, sockfd, EV_TIMEOUT | EV_READ, intercept_request_cb, intercept);
165   intercept->intercept_event = intercept_event;
166   event_add(intercept_event, &timeout);
167 }
168 
~Intercept()169 Intercept::~Intercept() {
170   event_free(intercept_event);
171   if (registered) {
172     CHECK(intercept_manager != nullptr);
173     intercept_manager->Unregister(this);
174   }
175 }
176 
InterceptManager(event_base * base,int intercept_socket)177 InterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) {
178   this->listener = evconnlistener_new(base, intercept_accept_cb, this, LEV_OPT_CLOSE_ON_FREE,
179                                       /* backlog */ -1, intercept_socket);
180 }
181 
canonical_dump_type(const DebuggerdDumpType dump_type)182 static DebuggerdDumpType canonical_dump_type(const DebuggerdDumpType dump_type) {
183   // kDebuggerdTombstone and kDebuggerdTombstoneProto should be treated as
184   // a single dump_type for intercepts (kDebuggerdTombstone).
185   if (dump_type == kDebuggerdTombstoneProto) {
186     return kDebuggerdTombstone;
187   }
188   return dump_type;
189 }
190 
Get(const pid_t pid,const DebuggerdDumpType dump_type)191 Intercept* InterceptManager::Get(const pid_t pid, const DebuggerdDumpType dump_type) {
192   auto pid_entry = intercepts.find(pid);
193   if (pid_entry == intercepts.end()) {
194     return nullptr;
195   }
196 
197   const auto& dump_type_hash = pid_entry->second;
198   auto dump_type_entry = dump_type_hash.find(canonical_dump_type(dump_type));
199   if (dump_type_entry == dump_type_hash.end()) {
200     if (dump_type != kDebuggerdAnyIntercept) {
201       return nullptr;
202     }
203     // If doing a dump with an any intercept, only allow an any to match
204     // a single intercept. If there are multiple dump types with intercepts
205     // then there would be no way to figure out which to use.
206     if (dump_type_hash.size() != 1) {
207       LOG(WARNING) << "Cannot intercept using kDebuggerdAnyIntercept: there is more than one "
208                       "intercept registered for pid "
209                    << pid;
210       return nullptr;
211     }
212     dump_type_entry = dump_type_hash.begin();
213   }
214   return dump_type_entry->second;
215 }
216 
CanRegister(const InterceptRequest & request,InterceptResponse & response)217 bool InterceptManager::CanRegister(const InterceptRequest& request, InterceptResponse& response) {
218   if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) {
219     response.status = InterceptStatus::kFailed;
220     snprintf(response.error_message, sizeof(response.error_message),
221              "invalid intercept request: bad pid %" PRId32, request.pid);
222     return false;
223   }
224   if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) {
225     response.status = InterceptStatus::kFailed;
226     snprintf(response.error_message, sizeof(response.error_message),
227              "invalid intercept request: bad dump type %s", get_dump_type_name(request.dump_type));
228     return false;
229   }
230 
231   if (Get(request.pid, request.dump_type) != nullptr) {
232     response.status = InterceptStatus::kFailedAlreadyRegistered;
233     snprintf(response.error_message, sizeof(response.error_message),
234              "pid %" PRId32 " already registered, type %s", request.pid,
235              get_dump_type_name(request.dump_type));
236     return false;
237   }
238 
239   return true;
240 }
241 
Register(Intercept * intercept)242 void InterceptManager::Register(Intercept* intercept) {
243   CHECK(!intercept->registered);
244   auto& dump_type_hash = intercepts[intercept->pid];
245   dump_type_hash[canonical_dump_type(intercept->dump_type)] = intercept;
246   intercept->registered = true;
247 }
248 
FindIntercept(pid_t pid,DebuggerdDumpType dump_type,android::base::unique_fd * out_fd)249 bool InterceptManager::FindIntercept(pid_t pid, DebuggerdDumpType dump_type,
250                                      android::base::unique_fd* out_fd) {
251   Intercept* intercept = Get(pid, dump_type);
252   if (intercept == nullptr) {
253     return false;
254   }
255 
256   if (dump_type != intercept->dump_type) {
257     LOG(INFO) << "found registered intercept of type " << intercept->dump_type
258               << " for requested type " << dump_type;
259   }
260 
261   LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid
262             << " and type " << intercept->dump_type;
263   InterceptResponse response = {};
264   response.status = InterceptStatus::kStarted;
265   TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
266   *out_fd = std::move(intercept->output_fd);
267 
268   // Delete the intercept data, which will unregister the intercept and remove the timeout event.
269   delete intercept;
270 
271   return true;
272 }
273