/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define DEBUG false #include "Log.h" #include "incidentd_util.h" #include #include #include #include #include "section_list.h" namespace android { namespace os { namespace incidentd { const Privacy* get_privacy_of_section(int id) { int l = 0; int r = PRIVACY_POLICY_COUNT - 1; while (l <= r) { int mid = (l + r) >> 1; const Privacy* p = PRIVACY_POLICY_LIST[mid]; if (p->field_id < (uint32_t)id) { l = mid + 1; } else if (p->field_id > (uint32_t)id) { r = mid - 1; } else { return p; } } return NULL; } std::vector> gBufferPool; std::mutex gBufferPoolLock; sp get_buffer_from_pool() { std::scoped_lock lock(gBufferPoolLock); if (gBufferPool.size() == 0) { return new EncodedBuffer(); } sp buffer = gBufferPool.back(); gBufferPool.pop_back(); return buffer; } void return_buffer_to_pool(sp buffer) { buffer->clear(); std::scoped_lock lock(gBufferPoolLock); gBufferPool.push_back(buffer); } void clear_buffer_pool() { std::scoped_lock lock(gBufferPoolLock); gBufferPool.clear(); } // ================================================================================ Fpipe::Fpipe() : mRead(), mWrite() {} Fpipe::~Fpipe() { close(); } bool Fpipe::close() { mRead.reset(); mWrite.reset(); return true; } bool Fpipe::init() { return Pipe(&mRead, &mWrite); } unique_fd& Fpipe::readFd() { return mRead; } unique_fd& Fpipe::writeFd() { return mWrite; } pid_t fork_execute_cmd(char* const argv[], Fpipe* input, Fpipe* output, int* status) { int in = -1; if (input != nullptr) { in = input->readFd().release(); // Auto close write end of the input pipe on exec to prevent leaking fd in child process fcntl(input->writeFd().get(), F_SETFD, FD_CLOEXEC); } int out = output->writeFd().release(); // Auto close read end of the output pipe on exec fcntl(output->readFd().get(), F_SETFD, FD_CLOEXEC); return fork_execute_cmd(argv, in, out, status); } pid_t fork_execute_cmd(char* const argv[], int in, int out, int* status) { int dummy_status = 0; if (status == nullptr) { status = &dummy_status; } *status = 0; pid_t pid = vfork(); if (pid < 0) { *status = -errno; return -1; } if (pid == 0) { // In child if (in >= 0 && (TEMP_FAILURE_RETRY(dup2(in, STDIN_FILENO)) < 0 || close(in))) { ALOGW("Failed to dup2 stdin."); _exit(EXIT_FAILURE); } if (TEMP_FAILURE_RETRY(dup2(out, STDOUT_FILENO)) < 0 || close(out)) { ALOGW("Failed to dup2 stdout."); _exit(EXIT_FAILURE); } // Make sure the child dies when incidentd dies prctl(PR_SET_PDEATHSIG, SIGKILL); execvp(argv[0], argv); _exit(errno); // always exits with failure if any } // In parent if ((in >= 0 && close(in) < 0) || close(out) < 0) { ALOGW("Failed to close pd. Killing child process"); *status = -errno; kill_child(pid); return -1; } return pid; } // ================================================================================ const char** varargs(const char* first, va_list rest) { va_list copied_rest; int numOfArgs = 1; // first is already count. va_copy(copied_rest, rest); while (va_arg(copied_rest, const char*) != NULL) { numOfArgs++; } va_end(copied_rest); // allocate extra 1 for NULL terminator const char** ret = (const char**)malloc(sizeof(const char*) * (numOfArgs + 1)); ret[0] = first; for (int i = 1; i < numOfArgs; i++) { const char* arg = va_arg(rest, const char*); ret[i] = arg; } ret[numOfArgs] = NULL; return ret; } // ================================================================================ const uint64_t NANOS_PER_SEC = 1000000000; uint64_t Nanotime() { timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return static_cast(ts.tv_sec * NANOS_PER_SEC + ts.tv_nsec); } // ================================================================================ static status_t statusCode(int status) { if (WIFSIGNALED(status)) { VLOG("return by signal: %s", strerror(WTERMSIG(status))); return -WTERMSIG(status); } else if (WIFEXITED(status) && WEXITSTATUS(status) > 0) { VLOG("return by exit: %s", strerror(WEXITSTATUS(status))); return -WEXITSTATUS(status); } return NO_ERROR; } static bool waitpid_with_timeout(pid_t pid, int timeout_ms, int* status) { sigset_t child_mask, old_mask; sigemptyset(&child_mask); sigaddset(&child_mask, SIGCHLD); if (sigprocmask(SIG_BLOCK, &child_mask, &old_mask) == -1) { ALOGW("sigprocmask failed: %s", strerror(errno)); return false; } timespec ts; ts.tv_sec = timeout_ms / 1000; ts.tv_nsec = (timeout_ms % 1000) * 1000000; int ret = TEMP_FAILURE_RETRY(sigtimedwait(&child_mask, nullptr, &ts)); int saved_errno = errno; // Set the signals back the way they were. if (sigprocmask(SIG_SETMASK, &old_mask, nullptr) == -1) { ALOGW("sigprocmask failed: %s", strerror(errno)); if (ret == 0) { return false; } } if (ret == -1) { errno = saved_errno; if (errno == EAGAIN) { errno = ETIMEDOUT; } else { ALOGW("sigtimedwait failed: %s", strerror(errno)); } return false; } pid_t child_pid = waitpid(pid, status, WNOHANG); if (child_pid == pid) { return true; } if (child_pid == -1) { ALOGW("waitpid failed: %s", strerror(errno)); } else { ALOGW("Waiting for pid %d, got pid %d instead", pid, child_pid); } return false; } status_t kill_child(pid_t pid) { int status; kill(pid, SIGKILL); if (waitpid(pid, &status, 0) == -1) return -1; return statusCode(status); } status_t wait_child(pid_t pid, int timeout_ms) { int status; if (waitpid_with_timeout(pid, timeout_ms, &status)) { return statusCode(status); } return kill_child(pid); } } // namespace incidentd } // namespace os } // namespace android