// Copyright 2017 The Crashpad Authors. All rights reserved. // // 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. #include "util/linux/exception_handler_client.h" #include #include #include #include #include #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "build/build_config.h" #include "util/file/file_io.h" #include "util/linux/ptrace_broker.h" #include "util/posix/signals.h" namespace crashpad { ExceptionHandlerClient::ExceptionHandlerClient(int sock) : server_sock_(sock), ptracer_(-1), can_set_ptracer_(true) {} ExceptionHandlerClient::~ExceptionHandlerClient() = default; int ExceptionHandlerClient::RequestCrashDump(const ClientInformation& info) { int status = SendCrashDumpRequest(info); if (status != 0) { return status; } return WaitForCrashDumpComplete(); } int ExceptionHandlerClient::SetPtracer(pid_t pid) { if (ptracer_ == pid) { return 0; } if (!can_set_ptracer_) { return EPERM; } if (prctl(PR_SET_PTRACER, pid, 0, 0, 0) == 0) { return 0; } return errno; } void ExceptionHandlerClient::SetCanSetPtracer(bool can_set_ptracer) { can_set_ptracer_ = can_set_ptracer; } int ExceptionHandlerClient::SendCrashDumpRequest( const ClientInformation& info) { ClientToServerMessage message; message.type = ClientToServerMessage::kCrashDumpRequest; message.client_info = info; iovec iov; iov.iov_base = &message; iov.iov_len = sizeof(message); msghdr msg; msg.msg_name = nullptr; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; ucred creds; creds.pid = getpid(); creds.uid = geteuid(); creds.gid = getegid(); char cmsg_buf[CMSG_SPACE(sizeof(creds))]; msg.msg_control = cmsg_buf; msg.msg_controllen = sizeof(cmsg_buf); cmsghdr* cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_CREDENTIALS; cmsg->cmsg_len = CMSG_LEN(sizeof(creds)); *reinterpret_cast(CMSG_DATA(cmsg)) = creds; if (HANDLE_EINTR(sendmsg(server_sock_, &msg, MSG_NOSIGNAL)) < 0) { PLOG(ERROR) << "sendmsg"; return errno; } return 0; } int ExceptionHandlerClient::WaitForCrashDumpComplete() { ServerToClientMessage message; // If the server hangs up, ReadFileExactly will return false without setting // errno. errno = 0; while (ReadFileExactly(server_sock_, &message, sizeof(message))) { switch (message.type) { case ServerToClientMessage::kTypeForkBroker: { Signals::InstallDefaultHandler(SIGCHLD); pid_t pid = fork(); if (pid <= 0) { Errno error = pid < 0 ? errno : 0; if (!WriteFile(server_sock_, &error, sizeof(error))) { return errno; } } if (pid < 0) { continue; } if (pid == 0) { #if defined(ARCH_CPU_64_BITS) constexpr bool am_64_bit = true; #else constexpr bool am_64_bit = false; #endif // ARCH_CPU_64_BITS PtraceBroker broker(server_sock_, am_64_bit); _exit(broker.Run()); } int status = 0; pid_t child = HANDLE_EINTR(waitpid(pid, &status, 0)); DCHECK_EQ(child, pid); if (child == pid && status != 0) { return status; } continue; } case ServerToClientMessage::kTypeSetPtracer: { Errno result = SetPtracer(message.pid); if (!WriteFile(server_sock_, &result, sizeof(result))) { return errno; } continue; } case ServerToClientMessage::kTypeCrashDumpComplete: case ServerToClientMessage::kTypeCrashDumpFailed: return 0; } DCHECK(false); } return errno; } } // namespace crashpad