mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-29 00:32:35 +08:00
0924e56751
A PtraceBroker/Client pair implement a PtraceConnection over a socket. The broker runs in a process with `ptrace` capabilities for the target process and serves requests for the client over a socket. Bug: crashpad:30 Change-Id: Ied19bcedf84b46c8f68440fd1c284b2126470e5e Reviewed-on: https://chromium-review.googlesource.com/780397 Commit-Queue: Joshua Peraza <jperaza@chromium.org> Reviewed-by: Mark Mentovai <mark@chromium.org>
188 lines
4.7 KiB
C++
188 lines
4.7 KiB
C++
// 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/ptrace_broker.h"
|
|
|
|
#include <unistd.h>
|
|
|
|
#include "base/logging.h"
|
|
#include "util/file/file_io.h"
|
|
|
|
namespace crashpad {
|
|
|
|
PtraceBroker::PtraceBroker(int sock, bool is_64_bit)
|
|
: ptracer_(is_64_bit, /* can_log= */ false),
|
|
attachments_(nullptr),
|
|
attach_count_(0),
|
|
attach_capacity_(0),
|
|
sock_(sock) {
|
|
AllocateAttachments();
|
|
}
|
|
|
|
PtraceBroker::~PtraceBroker() = default;
|
|
|
|
int PtraceBroker::Run() {
|
|
int result = RunImpl();
|
|
ReleaseAttachments();
|
|
return result;
|
|
}
|
|
|
|
int PtraceBroker::RunImpl() {
|
|
while (true) {
|
|
Request request = {};
|
|
if (!ReadFileExactly(sock_, &request, sizeof(request))) {
|
|
return errno;
|
|
}
|
|
|
|
if (request.version != Request::kVersion) {
|
|
return EINVAL;
|
|
}
|
|
|
|
switch (request.type) {
|
|
case Request::kTypeAttach: {
|
|
ScopedPtraceAttach* attach;
|
|
ScopedPtraceAttach stack_attach;
|
|
bool attach_on_stack = false;
|
|
|
|
if (attach_capacity_ > attach_count_ || AllocateAttachments()) {
|
|
attach = new (&attachments_[attach_count_]) ScopedPtraceAttach;
|
|
} else {
|
|
attach = &stack_attach;
|
|
attach_on_stack = true;
|
|
}
|
|
|
|
Bool status = kBoolFalse;
|
|
if (attach->ResetAttach(request.tid)) {
|
|
status = kBoolTrue;
|
|
if (!attach_on_stack) {
|
|
++attach_count_;
|
|
}
|
|
}
|
|
|
|
if (!WriteFile(sock_, &status, sizeof(status))) {
|
|
return errno;
|
|
}
|
|
|
|
if (status == kBoolFalse) {
|
|
Errno error = errno;
|
|
if (!WriteFile(sock_, &error, sizeof(error))) {
|
|
return errno;
|
|
}
|
|
}
|
|
|
|
if (attach_on_stack && status == kBoolTrue) {
|
|
return RunImpl();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case Request::kTypeIs64Bit: {
|
|
Bool is_64_bit = ptracer_.Is64Bit() ? kBoolTrue : kBoolFalse;
|
|
if (!WriteFile(sock_, &is_64_bit, sizeof(is_64_bit))) {
|
|
return errno;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case Request::kTypeGetThreadInfo: {
|
|
GetThreadInfoResponse response;
|
|
response.success = ptracer_.GetThreadInfo(request.tid, &response.info)
|
|
? kBoolTrue
|
|
: kBoolFalse;
|
|
|
|
if (!WriteFile(sock_, &response, sizeof(response))) {
|
|
return errno;
|
|
}
|
|
|
|
if (response.success == kBoolFalse) {
|
|
Errno error = errno;
|
|
if (!WriteFile(sock_, &error, sizeof(error))) {
|
|
return errno;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case Request::kTypeReadMemory: {
|
|
int result =
|
|
SendMemory(request.tid, request.iov.base, request.iov.size);
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
case Request::kTypeExit:
|
|
return 0;
|
|
}
|
|
|
|
DCHECK(false);
|
|
return EINVAL;
|
|
}
|
|
}
|
|
|
|
int PtraceBroker::SendMemory(pid_t pid, VMAddress address, VMSize size) {
|
|
char buffer[4096];
|
|
while (size > 0) {
|
|
VMSize bytes_read = std::min(size, VMSize{sizeof(buffer)});
|
|
|
|
if (!ptracer_.ReadMemory(pid, address, bytes_read, buffer)) {
|
|
bytes_read = 0;
|
|
Errno error = errno;
|
|
if (!WriteFile(sock_, &bytes_read, sizeof(bytes_read)) ||
|
|
!WriteFile(sock_, &error, sizeof(error))) {
|
|
return errno;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
if (!WriteFile(sock_, &bytes_read, sizeof(bytes_read))) {
|
|
return errno;
|
|
}
|
|
|
|
if (!WriteFile(sock_, buffer, bytes_read)) {
|
|
return errno;
|
|
}
|
|
|
|
size -= bytes_read;
|
|
address += bytes_read;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool PtraceBroker::AllocateAttachments() {
|
|
constexpr size_t page_size = 4096;
|
|
constexpr size_t alloc_size =
|
|
(sizeof(ScopedPtraceAttach) + page_size - 1) & ~(page_size - 1);
|
|
void* alloc = sbrk(alloc_size);
|
|
if (reinterpret_cast<intptr_t>(alloc) == -1) {
|
|
return false;
|
|
}
|
|
|
|
if (attachments_ == nullptr) {
|
|
attachments_ = reinterpret_cast<ScopedPtraceAttach*>(alloc);
|
|
}
|
|
|
|
attach_capacity_ += alloc_size / sizeof(ScopedPtraceAttach);
|
|
return true;
|
|
}
|
|
|
|
void PtraceBroker::ReleaseAttachments() {
|
|
for (size_t index = 0; index < attach_count_; ++index) {
|
|
attachments_[index].Reset();
|
|
}
|
|
}
|
|
|
|
} // namespace crashpad
|