1cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao/* 2cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * Copyright 2016, The Android Open Source Project 3cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * 4cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * Licensed under the Apache License, Version 2.0 (the "License"); 5cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * you may not use this file except in compliance with the License. 6cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * You may obtain a copy of the License at 7cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * 8cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * http://www.apache.org/licenses/LICENSE-2.0 9cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * 10cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * Unless required by applicable law or agreed to in writing, software 11cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * distributed under the License is distributed on an "AS IS" BASIS, 12cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * See the License for the specific language governing permissions and 14cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao * limitations under the License. 15cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao */ 16cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 17cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include "intercept_manager.h" 18cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 19cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <inttypes.h> 20cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <sys/types.h> 21cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 22cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <unordered_map> 23cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 24cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <event2/event.h> 25cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <event2/listener.h> 26cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 27cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <android-base/logging.h> 28cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <android-base/unique_fd.h> 29cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao#include <cutils/sockets.h> 30cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 312d377cd6888775fe682e49a1ac34a3a6feb78708Narayan Kamath#include "protocol.h" 322d377cd6888775fe682e49a1ac34a3a6feb78708Narayan Kamath#include "util.h" 33cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 34cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gaousing android::base::unique_fd; 35cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 36cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gaostatic void intercept_close_cb(evutil_socket_t sockfd, short event, void* arg) { 37cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao auto intercept = reinterpret_cast<Intercept*>(arg); 38cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao InterceptManager* intercept_manager = intercept->intercept_manager; 39cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 40cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao CHECK_EQ(sockfd, intercept->sockfd.get()); 41cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 42cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao // If we can read, either we received unexpected data from the other side, or the other side 43cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao // closed their end of the socket. Either way, kill the intercept. 44cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 45cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao // Ownership of intercept differs based on whether we've registered it with InterceptManager. 46cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao if (!intercept->registered) { 47cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao delete intercept; 48cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } else { 49cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao auto it = intercept_manager->intercepts.find(intercept->intercept_pid); 50cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao if (it == intercept_manager->intercepts.end()) { 51cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao LOG(FATAL) << "intercept close callback called after intercept was already removed?"; 52cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } 53cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao if (it->second.get() != intercept) { 54cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao LOG(FATAL) << "intercept close callback has different Intercept from InterceptManager?"; 55cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } 56cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 57cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao const char* reason; 58cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao if ((event & EV_TIMEOUT) != 0) { 59cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao reason = "due to timeout"; 60cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } else { 61cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao reason = "due to input"; 62cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } 63cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 64a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath LOG(INFO) << "intercept for pid " << intercept->intercept_pid << " and type " 65a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath << intercept->dump_type << " terminated: " << reason; 66cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao intercept_manager->intercepts.erase(it); 67cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } 68cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao} 69cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 70a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamathstatic bool is_intercept_request_valid(const InterceptRequest& request) { 71a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath if (request.pid <= 0 || request.pid > std::numeric_limits<pid_t>::max()) { 72a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath return false; 73a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath } 74a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath 75a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath if (request.dump_type < 0 || request.dump_type > kDebuggerdJavaBacktrace) { 76a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath return false; 77a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath } 78a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath 79a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath return true; 80a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath} 81a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath 82cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gaostatic void intercept_request_cb(evutil_socket_t sockfd, short ev, void* arg) { 83cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao auto intercept = reinterpret_cast<Intercept*>(arg); 84cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao InterceptManager* intercept_manager = intercept->intercept_manager; 85cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 86cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao CHECK_EQ(sockfd, intercept->sockfd.get()); 87cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 88cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao if ((ev & EV_TIMEOUT) != 0) { 89cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao LOG(WARNING) << "tombstoned didn't receive InterceptRequest before timeout"; 90cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao goto fail; 91cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } else if ((ev & EV_READ) == 0) { 92cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao LOG(WARNING) << "tombstoned received unexpected event on intercept socket"; 93cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao goto fail; 94cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } 95cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 96cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao { 97cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao unique_fd rcv_fd; 98cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao InterceptRequest intercept_request; 99cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao ssize_t result = recv_fd(sockfd, &intercept_request, sizeof(intercept_request), &rcv_fd); 100cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 101cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao if (result == -1) { 102cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao PLOG(WARNING) << "failed to read from intercept socket"; 103cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao goto fail; 104cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } else if (result != sizeof(intercept_request)) { 105cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao LOG(WARNING) << "intercept socket received short read of length " << result << " (expected " 106cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao << sizeof(intercept_request) << ")"; 107cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao goto fail; 108cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } 109cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 110cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao // Move the received FD to the upper half, in order to more easily notice FD leaks. 111cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao int moved_fd = fcntl(rcv_fd.get(), F_DUPFD, 512); 112cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao if (moved_fd == -1) { 113cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao LOG(WARNING) << "failed to move received fd (" << rcv_fd.get() << ")"; 114cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao goto fail; 115cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } 116cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao rcv_fd.reset(moved_fd); 117cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 118cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao // We trust the other side, so only do minimal validity checking. 119a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath if (!is_intercept_request_valid(intercept_request)) { 120cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao InterceptResponse response = {}; 121460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao response.status = InterceptStatus::kFailed; 122a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath snprintf(response.error_message, sizeof(response.error_message), "invalid intercept request"); 123cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))); 124cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao goto fail; 125cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } 126cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 127cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao intercept->intercept_pid = intercept_request.pid; 128a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath intercept->dump_type = intercept_request.dump_type; 129cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 130460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao // Check if it's already registered. 131cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao if (intercept_manager->intercepts.count(intercept_request.pid) > 0) { 132cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao InterceptResponse response = {}; 133a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath response.status = InterceptStatus::kFailedAlreadyRegistered; 134cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao snprintf(response.error_message, sizeof(response.error_message), 135a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath "pid %" PRId32 " already intercepted, type %d", intercept_request.pid, 136a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath intercept_request.dump_type); 137cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))); 138cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao LOG(WARNING) << response.error_message; 139cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao goto fail; 140cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } 141cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 142460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao // Let the other side know that the intercept has been registered, now that we know we can't 143460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao // fail. tombstoned is single threaded, so this isn't racy. 144460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao InterceptResponse response = {}; 145460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao response.status = InterceptStatus::kRegistered; 146460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) { 147460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao PLOG(WARNING) << "failed to notify interceptor of registration"; 148460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao goto fail; 149460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao } 150460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao 151cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao intercept->output_fd = std::move(rcv_fd); 152cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept); 153cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao intercept->registered = true; 154cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 155a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath LOG(INFO) << "registered intercept for pid " << intercept_request.pid << " and type " 156a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath << intercept_request.dump_type; 157cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 158cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao // Register a different read event on the socket so that we can remove intercepts if the socket 159cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao // closes (e.g. if a user CTRL-C's the process that requested the intercept). 160cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao event_assign(intercept->intercept_event, intercept_manager->base, sockfd, EV_READ | EV_TIMEOUT, 161cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao intercept_close_cb, arg); 162cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 163cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao struct timeval timeout = { .tv_sec = 10, .tv_usec = 0 }; 164cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao event_add(intercept->intercept_event, &timeout); 165cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } 166cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 167cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao return; 168cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 169cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gaofail: 170cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao delete intercept; 171cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao} 172cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 173cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gaostatic void intercept_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int, 174cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao void* arg) { 175cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao Intercept* intercept = new Intercept(); 176cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao intercept->intercept_manager = static_cast<InterceptManager*>(arg); 177cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao intercept->sockfd.reset(sockfd); 178cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 179cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao struct timeval timeout = { 1, 0 }; 180cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao event_base* base = evconnlistener_get_base(listener); 181cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao event* intercept_event = 182cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao event_new(base, sockfd, EV_TIMEOUT | EV_READ, intercept_request_cb, intercept); 183cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao intercept->intercept_event = intercept_event; 184cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao event_add(intercept_event, &timeout); 185cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao} 186cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 187cbe70cb0a8cb0171f3802273050e851a47b090edJosh GaoInterceptManager::InterceptManager(event_base* base, int intercept_socket) : base(base) { 188cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao this->listener = evconnlistener_new(base, intercept_accept_cb, this, -1, LEV_OPT_CLOSE_ON_FREE, 189cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao intercept_socket); 190cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao} 191cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 192a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamathbool InterceptManager::GetIntercept(pid_t pid, DebuggerdDumpType dump_type, 193a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath android::base::unique_fd* out_fd) { 194cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao auto it = this->intercepts.find(pid); 195cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao if (it == this->intercepts.end()) { 196cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao return false; 197cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao } 198cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 199a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath if (dump_type == kDebuggerdAnyIntercept) { 200a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath LOG(INFO) << "found registered intercept of type " << it->second->dump_type 201a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath << " for requested type kDebuggerdAnyIntercept"; 202a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath } else if (it->second->dump_type != dump_type) { 203a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath LOG(WARNING) << "found non-matching intercept of type " << it->second->dump_type 204a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath << " for requested type: " << dump_type; 205a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath return false; 206a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath } 207a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath 208cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao auto intercept = std::move(it->second); 209cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao this->intercepts.erase(it); 210cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 211a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid 212a73df601b7fe192001f4b9b5ddeb17b8efe3981bNarayan Kamath << " and type " << intercept->dump_type; 213cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao InterceptResponse response = {}; 214460b336d6a05a8527633ab5c4509988ebb18ed30Josh Gao response.status = InterceptStatus::kStarted; 215cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response))); 216cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao *out_fd = std::move(intercept->output_fd); 217cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao 218cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao return true; 219cbe70cb0a8cb0171f3802273050e851a47b090edJosh Gao} 220