mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
ios: Add support for ScopedVMRead and RAWLOG
Adds wrapper to vm_read and vm_deallocate memory to allow for safe in-process memory reads during crashes. Also adds a logging utility safe for in-process exception handling. Bug: crashpad: 31 Change-Id: I658f3181cbec40a79e304b7306466e10c003564f Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2875349 Reviewed-by: Mark Mentovai <mark@chromium.org> Commit-Queue: Justin Cohen <justincohen@chromium.org>
This commit is contained in:
parent
0c3d6e5fa1
commit
d5b3a1d531
@ -384,6 +384,10 @@ crashpad_static_library("util") {
|
||||
"ios/exception_processor.mm",
|
||||
"ios/ios_system_data_collector.h",
|
||||
"ios/ios_system_data_collector.mm",
|
||||
"ios/raw_logging.cc",
|
||||
"ios/raw_logging.h",
|
||||
"ios/scoped_vm_read.cc",
|
||||
"ios/scoped_vm_read.h",
|
||||
]
|
||||
}
|
||||
|
||||
@ -787,7 +791,10 @@ source_set("util_test") {
|
||||
}
|
||||
|
||||
if (crashpad_is_ios) {
|
||||
sources += [ "ios/exception_processor_test.mm" ]
|
||||
sources += [
|
||||
"ios/exception_processor_test.mm",
|
||||
"ios/scoped_vm_read_test.cc",
|
||||
]
|
||||
|
||||
sources -= [
|
||||
"process/process_memory_range_test.cc",
|
||||
|
66
util/ios/raw_logging.cc
Normal file
66
util/ios/raw_logging.cc
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2021 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/ios/raw_logging.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
void RawLogString(const char* message) {
|
||||
const size_t message_len = strlen(message);
|
||||
size_t bytes_written = 0;
|
||||
while (bytes_written < message_len) {
|
||||
int rv = HANDLE_EINTR(write(
|
||||
STDERR_FILENO, message + bytes_written, message_len - bytes_written));
|
||||
if (rv < 0) {
|
||||
// Give up, nothing we can do now.
|
||||
break;
|
||||
}
|
||||
bytes_written += rv;
|
||||
}
|
||||
}
|
||||
|
||||
void RawLogInt(unsigned int number) {
|
||||
char buffer[20];
|
||||
char* digit = &buffer[sizeof(buffer) - 1];
|
||||
*digit = '\0';
|
||||
do {
|
||||
*(--digit) = (number % 10) + '0';
|
||||
number /= 10;
|
||||
} while (number != 0);
|
||||
RawLogString(digit);
|
||||
}
|
||||
|
||||
// Prints `path:linenum message:error` (with optional `:error`).
|
||||
void RawLog(const char* file, int line, const char* message, int error) {
|
||||
RawLogString(file);
|
||||
HANDLE_EINTR(write(STDERR_FILENO, ":", 1));
|
||||
RawLogInt(line);
|
||||
HANDLE_EINTR(write(STDERR_FILENO, " ", 1));
|
||||
RawLogString(message);
|
||||
if (error) {
|
||||
RawLogString(": ");
|
||||
RawLogInt(error);
|
||||
}
|
||||
HANDLE_EINTR(write(STDERR_FILENO, "\n", 1));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
37
util/ios/raw_logging.h
Normal file
37
util/ios/raw_logging.h
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#ifndef CRASHPAD_UTIL_IOS_EXCEPTION_LOGGING_H_
|
||||
#define CRASHPAD_UTIL_IOS_EXCEPTION_LOGGING_H_
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
//! \brief Log \a message to stderr in a way that is safe to run during an
|
||||
//! in-process crash. Also prints the given file, line number and an
|
||||
//! optional error code.
|
||||
//!
|
||||
//! Note: RUNS-DURING-CRASH.
|
||||
void RawLog(const char* file, int line, const char* message, int error);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
||||
#define RAW_LOG(message) \
|
||||
::crashpad::internal::RawLog(__FILE__, __LINE__, message, 0)
|
||||
|
||||
#define RAW_LOG_ERROR(error, message) \
|
||||
::crashpad::internal::RawLog(__FILE__, __LINE__, message, error)
|
||||
|
||||
#endif // CRASHPAD_UTIL_IOS_EXCEPTION_LOGGING_H_
|
62
util/ios/scoped_vm_read.cc
Normal file
62
util/ios/scoped_vm_read.cc
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright 2021 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/ios/scoped_vm_read.h"
|
||||
|
||||
#include "util/ios/raw_logging.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
ScopedVMReadInternal::ScopedVMReadInternal()
|
||||
: data_(0), vm_read_data_(0), vm_read_data_count_(0) {}
|
||||
|
||||
ScopedVMReadInternal::~ScopedVMReadInternal() {
|
||||
if (data_) {
|
||||
kern_return_t kr =
|
||||
vm_deallocate(mach_task_self(), vm_read_data_, vm_read_data_count_);
|
||||
if (kr != KERN_SUCCESS)
|
||||
RAW_LOG_ERROR(kr, "vm_deallocate");
|
||||
}
|
||||
}
|
||||
|
||||
bool ScopedVMReadInternal::Read(const void* data, const size_t data_length) {
|
||||
if (data_) {
|
||||
kern_return_t kr =
|
||||
vm_deallocate(mach_task_self(), vm_read_data_, vm_read_data_count_);
|
||||
if (kr != KERN_SUCCESS)
|
||||
RAW_LOG_ERROR(kr, "vm_deallocate");
|
||||
data_ = 0;
|
||||
}
|
||||
vm_address_t data_address = reinterpret_cast<vm_address_t>(data);
|
||||
vm_address_t page_region_address = trunc_page(data_address);
|
||||
vm_size_t page_region_size =
|
||||
round_page(data_address - page_region_address + data_length);
|
||||
kern_return_t kr = vm_read(mach_task_self(),
|
||||
page_region_address,
|
||||
page_region_size,
|
||||
&vm_read_data_,
|
||||
&vm_read_data_count_);
|
||||
|
||||
if (kr == KERN_SUCCESS) {
|
||||
data_ = vm_read_data_ + (data_address - page_region_address);
|
||||
return true;
|
||||
} else {
|
||||
RAW_LOG_ERROR(kr, "vm_read");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
96
util/ios/scoped_vm_read.h
Normal file
96
util/ios/scoped_vm_read.h
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#ifndef CRASHPAD_UTIL_IOS_SCOPED_VM_READ_H_
|
||||
#define CRASHPAD_UTIL_IOS_SCOPED_VM_READ_H_
|
||||
|
||||
#include <mach/mach.h>
|
||||
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
//! \brief Non-templated internal class to be used by ScopedVMRead.
|
||||
//!
|
||||
//! Note: RUNS-DURING-CRASH.
|
||||
class ScopedVMReadInternal {
|
||||
public:
|
||||
ScopedVMReadInternal();
|
||||
~ScopedVMReadInternal();
|
||||
|
||||
//! \brief Releases any previously read data and vm_reads \a data. Logs an
|
||||
//! error on failure.
|
||||
//!
|
||||
//! \param[in] data Memory to be read by vm_read.
|
||||
//! \param[in] data_length Length of \a data.
|
||||
//!
|
||||
//! \return `true` if all the data was read. Logs an error and returns false
|
||||
//! on failure
|
||||
bool Read(const void* data, size_t data_length);
|
||||
|
||||
vm_address_t data() const { return data_; }
|
||||
|
||||
private:
|
||||
// The address of the requested data.
|
||||
vm_address_t data_;
|
||||
|
||||
// The rounded down page boundary of the requested data.
|
||||
vm_address_t vm_read_data_;
|
||||
|
||||
// The size of the pages that were actually read.
|
||||
mach_msg_type_number_t vm_read_data_count_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedVMReadInternal);
|
||||
};
|
||||
|
||||
//! \brief A scoped wrapper for calls to `vm_read` and `vm_deallocate`. Allows
|
||||
//! in-process handler to safely read memory for the intermediate dump.
|
||||
//!
|
||||
//! Note: RUNS-DURING-CRASH.
|
||||
template <typename T>
|
||||
class ScopedVMRead {
|
||||
public:
|
||||
ScopedVMRead() : internal_() {}
|
||||
~ScopedVMRead() {}
|
||||
|
||||
//! \brief Releases any previously read data and vm_reads data.
|
||||
//!
|
||||
//! \param[in] data Memory to be read by vm_read.
|
||||
//! \param[in] count Length of \a data.
|
||||
//!
|
||||
//! \return `true` if all the data was read. Logs an error and returns false
|
||||
//! on failure
|
||||
bool Read(const void* data, size_t count = 1) {
|
||||
size_t data_length = count * sizeof(T);
|
||||
return internal_.Read(data, data_length);
|
||||
}
|
||||
|
||||
//! \brief Returns the pointer to memory safe to read during the in-process
|
||||
//! crash handler.
|
||||
T* operator->() const { return get(); }
|
||||
|
||||
//! \brief Returns the pointer to memory safe to read during the in-process
|
||||
//! crash handler.
|
||||
T* get() const { return reinterpret_cast<T*>(internal_.data()); }
|
||||
|
||||
private:
|
||||
ScopedVMReadInternal internal_;
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedVMRead);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_IOS_SCOPED_VM_READ_H_
|
83
util/ios/scoped_vm_read_test.cc
Normal file
83
util/ios/scoped_vm_read_test.cc
Normal file
@ -0,0 +1,83 @@
|
||||
// Copyright 2021 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/ios/scoped_vm_read.h"
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "base/mac/scoped_mach_vm.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
TEST(ScopedVMReadTest, BasicFunctionality) {
|
||||
// bad data or count.
|
||||
internal::ScopedVMRead<vm_address_t> vmread_bad;
|
||||
ASSERT_FALSE(vmread_bad.Read(nullptr, 100));
|
||||
ASSERT_FALSE(vmread_bad.Read(reinterpret_cast<void*>(0x1000), 100));
|
||||
vm_address_t address = 1;
|
||||
ASSERT_FALSE(vmread_bad.Read(&address, 1000000000));
|
||||
|
||||
// array
|
||||
constexpr char read_me[] = "read me";
|
||||
internal::ScopedVMRead<char> vmread_string;
|
||||
ASSERT_TRUE(vmread_string.Read(read_me, strlen(read_me)));
|
||||
EXPECT_STREQ(read_me, vmread_string.get());
|
||||
|
||||
// struct
|
||||
timeval time_of_day;
|
||||
EXPECT_TRUE(gettimeofday(&time_of_day, nullptr) == 0);
|
||||
internal::ScopedVMRead<timeval> vmread_time;
|
||||
ASSERT_TRUE(vmread_time.Read(&time_of_day));
|
||||
EXPECT_EQ(time_of_day.tv_sec, vmread_time->tv_sec);
|
||||
EXPECT_EQ(time_of_day.tv_usec, vmread_time->tv_usec);
|
||||
|
||||
// reset.
|
||||
timeval time_of_day2;
|
||||
EXPECT_TRUE(gettimeofday(&time_of_day2, nullptr) == 0);
|
||||
ASSERT_TRUE(vmread_time.Read(&time_of_day2));
|
||||
EXPECT_EQ(time_of_day2.tv_sec, vmread_time->tv_sec);
|
||||
EXPECT_EQ(time_of_day2.tv_usec, vmread_time->tv_usec);
|
||||
}
|
||||
|
||||
TEST(ScopedVMReadTest, MissingMiddleVM) {
|
||||
char* region;
|
||||
vm_size_t page_size = getpagesize();
|
||||
vm_size_t region_size = page_size * 3;
|
||||
ASSERT_EQ(vm_allocate(mach_task_self(),
|
||||
reinterpret_cast<vm_address_t*>(®ion),
|
||||
region_size,
|
||||
VM_FLAGS_ANYWHERE),
|
||||
0);
|
||||
base::mac::ScopedMachVM vm_owner(reinterpret_cast<vm_address_t>(region),
|
||||
region_size);
|
||||
|
||||
internal::ScopedVMRead<char> vmread_missing_middle;
|
||||
ASSERT_TRUE(vmread_missing_middle.Read(region, region_size));
|
||||
|
||||
// Dealloc middle page.
|
||||
ASSERT_EQ(vm_deallocate(mach_task_self(),
|
||||
reinterpret_cast<vm_address_t>(region + page_size),
|
||||
page_size),
|
||||
0);
|
||||
|
||||
ASSERT_FALSE(vmread_missing_middle.Read(region, region_size));
|
||||
ASSERT_TRUE(vmread_missing_middle.Read(region, page_size));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
Loading…
x
Reference in New Issue
Block a user