mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
linux: setup a signal stack
Bug: crashpad:340 Change-Id: I035d988bc8e76dbf80c07f0c92b07dbefeba8bd1 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2209768 Commit-Queue: Joshua Peraza <jperaza@chromium.org> Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
ef8a063055
commit
06a688ddc1
@ -175,3 +175,11 @@ source_set("client_test") {
|
||||
data_deps += [ "../handler:crashpad_handler_console" ]
|
||||
}
|
||||
}
|
||||
|
||||
if (crashpad_is_linux || crashpad_is_android) {
|
||||
source_set("pthread_create") {
|
||||
sources = [ "pthread_create_linux.cc" ]
|
||||
|
||||
deps = [ ":client" ]
|
||||
}
|
||||
}
|
||||
|
@ -143,6 +143,29 @@ class CrashpadClient {
|
||||
//! handler as this process' ptracer. -1 indicates that the handler's
|
||||
//! process ID should be determined by communicating over the socket.
|
||||
bool SetHandlerSocket(ScopedFileHandle sock, pid_t pid);
|
||||
|
||||
//! \brief Uses `sigaltstack()` to allocate a signal stack for the calling
|
||||
//! thread.
|
||||
//!
|
||||
//! This method allocates an alternate stack to handle signals delivered to
|
||||
//! the calling thread and should be called early in the lifetime of each
|
||||
//! thread. Installing an alternate stack allows signals to be delivered in
|
||||
//! the event that the call stack's stack pointer points to invalid memory,
|
||||
//! as in the case of stack overflow.
|
||||
//!
|
||||
//! This method is called automatically by SetHandlerSocket() and
|
||||
//! the various StartHandler() methods. It is harmless to call multiple times.
|
||||
//! A new signal stack will be allocated only if there is no existing stack or
|
||||
//! the existing stack is too small. The stack will be automatically freed
|
||||
//! when the thread exits.
|
||||
//!
|
||||
//! An application might choose to diligently call this method from the start
|
||||
//! routine for each thread, call it from a `pthread_create()` wrapper which
|
||||
//! the application provides, or link the provided "client:pthread_create"
|
||||
//! target.
|
||||
//!
|
||||
//! \return `true` on success. Otherwise `false` with a message logged.
|
||||
static bool InitializeSignalStackForThread();
|
||||
#endif // OS_ANDROID || OS_LINUX || DOXYGEN
|
||||
|
||||
#if defined(OS_ANDROID) || DOXYGEN
|
||||
|
@ -14,8 +14,11 @@
|
||||
|
||||
#include "client/crashpad_client.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/syscall.h>
|
||||
@ -36,6 +39,7 @@
|
||||
#include "util/linux/socket.h"
|
||||
#include "util/misc/from_pointer_cast.h"
|
||||
#include "util/posix/double_fork_and_exec.h"
|
||||
#include "util/posix/scoped_mmap.h"
|
||||
#include "util/posix/signals.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -167,10 +171,14 @@ class SignalHandler {
|
||||
~SignalHandler() = default;
|
||||
|
||||
bool Install(const std::set<int>* unhandled_signals) {
|
||||
bool signal_stack_initialized =
|
||||
CrashpadClient::InitializeSignalStackForThread();
|
||||
DCHECK(signal_stack_initialized);
|
||||
|
||||
DCHECK(!handler_);
|
||||
handler_ = this;
|
||||
return Signals::InstallCrashHandlers(
|
||||
HandleOrReraiseSignal, 0, &old_actions_, unhandled_signals);
|
||||
HandleOrReraiseSignal, SA_ONSTACK, &old_actions_, unhandled_signals);
|
||||
}
|
||||
|
||||
const ExceptionInformation& GetExceptionInfo() {
|
||||
@ -413,6 +421,90 @@ bool CrashpadClient::SetHandlerSocket(ScopedFileHandle sock, pid_t pid) {
|
||||
auto signal_handler = RequestCrashDumpHandler::Get();
|
||||
return signal_handler->Initialize(std::move(sock), pid, &unhandled_signals_);
|
||||
}
|
||||
|
||||
// static
|
||||
bool CrashpadClient::InitializeSignalStackForThread() {
|
||||
stack_t stack;
|
||||
if (sigaltstack(nullptr, &stack) != 0) {
|
||||
PLOG(ERROR) << "sigaltstack";
|
||||
return false;
|
||||
}
|
||||
|
||||
DCHECK_EQ(stack.ss_flags & SS_ONSTACK, 0);
|
||||
|
||||
const size_t page_size = getpagesize();
|
||||
const size_t kStackSize = (SIGSTKSZ + page_size - 1) & ~(page_size - 1);
|
||||
if (stack.ss_flags & SS_DISABLE || stack.ss_size < kStackSize) {
|
||||
const size_t kGuardPageSize = page_size;
|
||||
const size_t kStackAllocSize = kStackSize + 2 * kGuardPageSize;
|
||||
|
||||
static void (*stack_destructor)(void*) = [](void* stack_mem) {
|
||||
const size_t page_size = getpagesize();
|
||||
const size_t kGuardPageSize = page_size;
|
||||
const size_t kStackSize = (SIGSTKSZ + page_size - 1) & ~(page_size - 1);
|
||||
const size_t kStackAllocSize = kStackSize + 2 * kGuardPageSize;
|
||||
|
||||
stack_t stack;
|
||||
stack.ss_flags = SS_DISABLE;
|
||||
if (sigaltstack(&stack, &stack) != 0) {
|
||||
PLOG(ERROR) << "sigaltstack";
|
||||
} else if (stack.ss_sp !=
|
||||
static_cast<char*>(stack_mem) + kGuardPageSize) {
|
||||
PLOG_IF(ERROR, sigaltstack(&stack, nullptr) != 0) << "sigaltstack";
|
||||
}
|
||||
|
||||
if (munmap(stack_mem, kStackAllocSize) != 0) {
|
||||
PLOG(ERROR) << "munmap";
|
||||
}
|
||||
};
|
||||
|
||||
static pthread_key_t stack_key;
|
||||
static int key_error = []() {
|
||||
errno = pthread_key_create(&stack_key, stack_destructor);
|
||||
PLOG_IF(ERROR, errno) << "pthread_key_create";
|
||||
return errno;
|
||||
}();
|
||||
if (key_error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto old_stack = static_cast<char*>(pthread_getspecific(stack_key));
|
||||
if (old_stack) {
|
||||
stack.ss_sp = old_stack + kGuardPageSize;
|
||||
} else {
|
||||
ScopedMmap stack_mem;
|
||||
if (!stack_mem.ResetMmap(nullptr,
|
||||
kStackAllocSize,
|
||||
PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1,
|
||||
0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mprotect(stack_mem.addr_as<char*>() + kGuardPageSize,
|
||||
kStackSize,
|
||||
PROT_READ | PROT_WRITE) != 0) {
|
||||
PLOG(ERROR) << "mprotect";
|
||||
return false;
|
||||
}
|
||||
|
||||
stack.ss_sp = stack_mem.addr_as<char*>() + kGuardPageSize;
|
||||
|
||||
errno = pthread_setspecific(stack_key, stack_mem.release());
|
||||
PCHECK(errno == 0) << "pthread_setspecific";
|
||||
}
|
||||
|
||||
stack.ss_size = kStackSize;
|
||||
stack.ss_flags =
|
||||
(stack.ss_flags & SS_DISABLE) ? 0 : stack.ss_flags & SS_AUTODISARM;
|
||||
if (sigaltstack(&stack, nullptr) != 0) {
|
||||
PLOG(ERROR) << "sigaltstack";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif // OS_ANDROID || OS_LINUX
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "client/crashpad_client.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
@ -30,6 +31,7 @@
|
||||
#include "snapshot/annotation_snapshot.h"
|
||||
#include "snapshot/minidump/process_snapshot_minidump.h"
|
||||
#include "snapshot/sanitized/sanitization_information.h"
|
||||
#include "test/errors.h"
|
||||
#include "test/multiprocess.h"
|
||||
#include "test/multiprocess_exec.h"
|
||||
#include "test/scoped_temp_dir.h"
|
||||
@ -41,7 +43,9 @@
|
||||
#include "util/linux/socket.h"
|
||||
#include "util/misc/address_types.h"
|
||||
#include "util/misc/from_pointer_cast.h"
|
||||
#include "util/posix/scoped_mmap.h"
|
||||
#include "util/posix/signals.h"
|
||||
#include "util/thread/thread.h"
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
#include <android/set_abort_message.h>
|
||||
@ -56,22 +60,33 @@ namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
enum class CrashType : uint32_t {
|
||||
kSimulated,
|
||||
kBuiltinTrap,
|
||||
kInfiniteRecursion,
|
||||
};
|
||||
|
||||
struct StartHandlerForSelfTestOptions {
|
||||
bool start_handler_at_crash;
|
||||
bool simulate_crash;
|
||||
bool set_first_chance_handler;
|
||||
bool crash_non_main_thread;
|
||||
bool client_uses_signals;
|
||||
CrashType crash_type;
|
||||
};
|
||||
|
||||
class StartHandlerForSelfTest
|
||||
: public testing::TestWithParam<std::tuple<bool, bool, bool>> {
|
||||
: public testing::TestWithParam<
|
||||
std::tuple<bool, bool, bool, bool, CrashType>> {
|
||||
public:
|
||||
StartHandlerForSelfTest() = default;
|
||||
~StartHandlerForSelfTest() = default;
|
||||
|
||||
void SetUp() override {
|
||||
std::tie(options_.start_handler_at_crash,
|
||||
options_.simulate_crash,
|
||||
options_.set_first_chance_handler) = GetParam();
|
||||
options_.set_first_chance_handler,
|
||||
options_.crash_non_main_thread,
|
||||
options_.client_uses_signals,
|
||||
options_.crash_type) = GetParam();
|
||||
}
|
||||
|
||||
const StartHandlerForSelfTestOptions& Options() const { return options_; }
|
||||
@ -164,6 +179,100 @@ void ValidateDump(const CrashReportDatabase::UploadReport* report) {
|
||||
ADD_FAILURE();
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Winfinite-recursion"
|
||||
int RecurseInfinitely(int* ptr) {
|
||||
int buf[1 << 20];
|
||||
return *ptr + RecurseInfinitely(buf);
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
void DoCrash(const StartHandlerForSelfTestOptions& options,
|
||||
CrashpadClient* client) {
|
||||
switch (options.crash_type) {
|
||||
case CrashType::kSimulated:
|
||||
if (options.set_first_chance_handler) {
|
||||
client->SetFirstChanceExceptionHandler(HandleCrashSuccessfully);
|
||||
}
|
||||
CRASHPAD_SIMULATE_CRASH();
|
||||
break;
|
||||
|
||||
case CrashType::kBuiltinTrap:
|
||||
__builtin_trap();
|
||||
break;
|
||||
|
||||
case CrashType::kInfiniteRecursion:
|
||||
int val = 42;
|
||||
exit(RecurseInfinitely(&val));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class ScopedAltSignalStack {
|
||||
public:
|
||||
ScopedAltSignalStack() = default;
|
||||
|
||||
~ScopedAltSignalStack() {
|
||||
if (stack_mem_.is_valid()) {
|
||||
stack_t stack;
|
||||
stack.ss_flags = SS_DISABLE;
|
||||
if (sigaltstack(&stack, nullptr) != 0) {
|
||||
ADD_FAILURE() << ErrnoMessage("sigaltstack");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Initialize() {
|
||||
ScopedMmap local_stack_mem;
|
||||
constexpr size_t stack_size = MINSIGSTKSZ;
|
||||
ASSERT_TRUE(local_stack_mem.ResetMmap(nullptr,
|
||||
stack_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS,
|
||||
-1,
|
||||
0));
|
||||
|
||||
stack_t stack;
|
||||
stack.ss_sp = local_stack_mem.addr();
|
||||
stack.ss_size = stack_size;
|
||||
stack.ss_flags = 0;
|
||||
ASSERT_EQ(sigaltstack(&stack, nullptr), 0) << ErrnoMessage("sigaltstack");
|
||||
stack_mem_.ResetAddrLen(local_stack_mem.release(), stack_size);
|
||||
}
|
||||
|
||||
private:
|
||||
ScopedMmap stack_mem_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedAltSignalStack);
|
||||
};
|
||||
|
||||
class CrashThread : public Thread {
|
||||
public:
|
||||
CrashThread(const StartHandlerForSelfTestOptions& options,
|
||||
CrashpadClient* client)
|
||||
: client_signal_stack_(), options_(options), client_(client) {}
|
||||
|
||||
private:
|
||||
void ThreadMain() override {
|
||||
// It is only necessary to call InitializeSignalStackForThread() once, but
|
||||
// should be harmless to call multiple times and durable against the client
|
||||
// using sigaltstack() either before or after it is called.
|
||||
CrashpadClient::InitializeSignalStackForThread();
|
||||
if (options_.client_uses_signals) {
|
||||
client_signal_stack_.Initialize();
|
||||
}
|
||||
CrashpadClient::InitializeSignalStackForThread();
|
||||
|
||||
DoCrash(options_, client_);
|
||||
}
|
||||
|
||||
ScopedAltSignalStack client_signal_stack_;
|
||||
const StartHandlerForSelfTestOptions& options_;
|
||||
CrashpadClient* client_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashThread);
|
||||
};
|
||||
|
||||
CRASHPAD_CHILD_TEST_MAIN(StartHandlerForSelfTestChild) {
|
||||
FileHandle in = StdioFileHandle(StdioStream::kStandardInput);
|
||||
|
||||
@ -176,6 +285,25 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerForSelfTestChild) {
|
||||
StartHandlerForSelfTestOptions options;
|
||||
CheckedReadFileExactly(in, &options, sizeof(options));
|
||||
|
||||
ScopedAltSignalStack client_signal_stack;
|
||||
if (options.client_uses_signals) {
|
||||
client_signal_stack.Initialize();
|
||||
|
||||
static Signals::OldActions old_actions;
|
||||
static Signals::Handler client_handler =
|
||||
[](int signo, siginfo_t* siginfo, void*) {
|
||||
FileHandle out = StdioFileHandle(StdioStream::kStandardOutput);
|
||||
char c;
|
||||
WriteFile(out, &c, sizeof(c));
|
||||
|
||||
Signals::RestoreHandlerAndReraiseSignalOnReturn(
|
||||
siginfo, old_actions.ActionForSignal(signo));
|
||||
};
|
||||
|
||||
CHECK(Signals::InstallCrashHandlers(
|
||||
client_handler, SA_ONSTACK, &old_actions));
|
||||
}
|
||||
|
||||
base::FilePath handler_path = TestPaths::Executable().DirName().Append(
|
||||
FILE_PATH_LITERAL("crashpad_handler"));
|
||||
|
||||
@ -202,17 +330,14 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerForSelfTestChild) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (options.simulate_crash) {
|
||||
if (options.set_first_chance_handler) {
|
||||
client.SetFirstChanceExceptionHandler(HandleCrashSuccessfully);
|
||||
}
|
||||
CRASHPAD_SIMULATE_CRASH();
|
||||
return EXIT_SUCCESS;
|
||||
if (options.crash_non_main_thread) {
|
||||
CrashThread thread(options, &client);
|
||||
thread.Start();
|
||||
thread.Join();
|
||||
} else {
|
||||
DoCrash(options, &client);
|
||||
}
|
||||
|
||||
__builtin_trap();
|
||||
|
||||
NOTREACHED();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -221,8 +346,17 @@ class StartHandlerForSelfInChildTest : public MultiprocessExec {
|
||||
StartHandlerForSelfInChildTest(const StartHandlerForSelfTestOptions& options)
|
||||
: MultiprocessExec(), options_(options) {
|
||||
SetChildTestMainFunction("StartHandlerForSelfTestChild");
|
||||
if (!options.simulate_crash) {
|
||||
SetExpectedChildTerminationBuiltinTrap();
|
||||
switch (options.crash_type) {
|
||||
case CrashType::kSimulated:
|
||||
// kTerminationNormal, EXIT_SUCCESS
|
||||
break;
|
||||
case CrashType::kBuiltinTrap:
|
||||
SetExpectedChildTerminationBuiltinTrap();
|
||||
break;
|
||||
case CrashType::kInfiniteRecursion:
|
||||
SetExpectedChildTermination(TerminationReason::kTerminationSignal,
|
||||
SIGSEGV);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -247,6 +381,13 @@ class StartHandlerForSelfInChildTest : public MultiprocessExec {
|
||||
ASSERT_TRUE(
|
||||
LoggingWriteFile(WritePipeHandle(), &options_, sizeof(options_)));
|
||||
|
||||
if (options_.client_uses_signals && !options_.set_first_chance_handler &&
|
||||
options_.crash_type != CrashType::kSimulated) {
|
||||
// Wait for child's client signal handler.
|
||||
char c;
|
||||
EXPECT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, sizeof(c)));
|
||||
}
|
||||
|
||||
// Wait for child to finish.
|
||||
CheckedReadFileAtEOF(ReadPipeHandle());
|
||||
|
||||
@ -279,7 +420,8 @@ class StartHandlerForSelfInChildTest : public MultiprocessExec {
|
||||
};
|
||||
|
||||
TEST_P(StartHandlerForSelfTest, StartHandlerInChild) {
|
||||
if (Options().set_first_chance_handler && !Options().simulate_crash) {
|
||||
if (Options().set_first_chance_handler &&
|
||||
Options().crash_type != CrashType::kSimulated) {
|
||||
// TODO(jperaza): test first chance handlers with real crashes.
|
||||
return;
|
||||
}
|
||||
@ -287,11 +429,16 @@ TEST_P(StartHandlerForSelfTest, StartHandlerInChild) {
|
||||
test.Run();
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(StartHandlerForSelfTestSuite,
|
||||
StartHandlerForSelfTest,
|
||||
testing::Combine(testing::Bool(),
|
||||
testing::Bool(),
|
||||
testing::Bool()));
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
StartHandlerForSelfTestSuite,
|
||||
StartHandlerForSelfTest,
|
||||
testing::Combine(testing::Bool(),
|
||||
testing::Bool(),
|
||||
testing::Bool(),
|
||||
testing::Bool(),
|
||||
testing::Values(CrashType::kSimulated,
|
||||
CrashType::kBuiltinTrap,
|
||||
CrashType::kInfiniteRecursion)));
|
||||
|
||||
// Test state for starting the handler for another process.
|
||||
class StartHandlerForClientTest {
|
||||
|
71
client/pthread_create_linux.cc
Normal file
71
client/pthread_create_linux.cc
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2020 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 <dlfcn.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "client/crashpad_client.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using StartRoutineType = void* (*)(void*);
|
||||
|
||||
struct StartParams {
|
||||
StartRoutineType start_routine;
|
||||
void* arg;
|
||||
};
|
||||
|
||||
void* InitializeSignalStackAndStart(StartParams* params) {
|
||||
crashpad::CrashpadClient::InitializeSignalStackForThread();
|
||||
|
||||
StartParams local_params = *params;
|
||||
delete params;
|
||||
|
||||
return local_params.start_routine(local_params.arg);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((visibility("default"))) int pthread_create(
|
||||
pthread_t* thread,
|
||||
const pthread_attr_t* attr,
|
||||
StartRoutineType start_routine,
|
||||
void* arg) {
|
||||
static const auto next_pthread_create = []() {
|
||||
const auto next_pthread_create =
|
||||
reinterpret_cast<decltype(pthread_create)*>(
|
||||
dlsym(RTLD_NEXT, "pthread_create"));
|
||||
CHECK(next_pthread_create) << "dlsym: " << dlerror();
|
||||
return next_pthread_create;
|
||||
}();
|
||||
|
||||
StartParams* params = new StartParams;
|
||||
params->start_routine = start_routine;
|
||||
params->arg = arg;
|
||||
|
||||
int result = next_pthread_create(
|
||||
thread,
|
||||
attr,
|
||||
reinterpret_cast<StartRoutineType>(InitializeSignalStackAndStart),
|
||||
params);
|
||||
if (result != 0) {
|
||||
delete params;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // extern "C"
|
@ -17,6 +17,11 @@
|
||||
|
||||
#include_next <signal.h>
|
||||
|
||||
// Missing from glibc and bionic
|
||||
#if !defined(SS_AUTODISARM)
|
||||
#define SS_AUTODISARM (1u << 31)
|
||||
#endif
|
||||
|
||||
// Missing from glibc and bionic-x86_64
|
||||
|
||||
#if defined(__x86_64__) || defined(__i386__)
|
||||
|
@ -156,6 +156,10 @@ if (!crashpad_is_ios) {
|
||||
[ "//third_party/mini_chromium/mini_chromium/build:win_windowed" ]
|
||||
}
|
||||
}
|
||||
|
||||
if (crashpad_is_linux) {
|
||||
deps += [ "../client:pthread_create" ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,4 +121,11 @@ bool ScopedMmap::Mprotect(int prot) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void* ScopedMmap::release() {
|
||||
void* retval = addr_;
|
||||
addr_ = MAP_FAILED;
|
||||
len_ = 0;
|
||||
return retval;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
||||
|
@ -77,6 +77,10 @@ class ScopedMmap {
|
||||
//! \return `true` on success. `false` on failure, with a message logged.
|
||||
bool Mprotect(int prot);
|
||||
|
||||
//! \brief Returns the base address of the memory-mapped region and releases
|
||||
//! ownership.
|
||||
void* release();
|
||||
|
||||
//! \return Whether this object is managing a valid memory-mapped region.
|
||||
bool is_valid() const { return addr_ != MAP_FAILED; }
|
||||
|
||||
|
@ -403,6 +403,19 @@ TEST(ScopedMmapDeathTest, Mprotect) {
|
||||
*addr = 2;
|
||||
}
|
||||
|
||||
TEST(ScopedMmapTest, Release) {
|
||||
ScopedMmap mapping;
|
||||
|
||||
const size_t kPageSize = base::checked_cast<size_t>(getpagesize());
|
||||
ASSERT_TRUE(ScopedMmapResetMmap(&mapping, kPageSize));
|
||||
ASSERT_TRUE(mapping.is_valid());
|
||||
|
||||
ScopedMmap mapping2;
|
||||
ASSERT_TRUE(mapping2.ResetAddrLen(mapping.release(), kPageSize));
|
||||
EXPECT_TRUE(mapping2.is_valid());
|
||||
EXPECT_FALSE(mapping.is_valid());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
||||
|
Loading…
x
Reference in New Issue
Block a user