You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
92 lines
3.5 KiB
92 lines
3.5 KiB
/*
|
|
* Copyright (C) 2020 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.
|
|
*/
|
|
|
|
#include "gwp_asan/common.h"
|
|
#include "gwp_asan/optional/backtrace.h"
|
|
#include "gwp_asan/optional/segv_handler.h"
|
|
|
|
#include <unwindstack/LocalUnwinder.h>
|
|
#include <unwindstack/Unwinder.h>
|
|
|
|
namespace {
|
|
// In reality, on Android, we use two separate unwinders. GWP-ASan internally
|
|
// uses a fast, frame-pointer unwinder for allocation/deallocation stack traces
|
|
// (android_unsafe_frame_pointer_chase, provided by bionic libc). When a process
|
|
// crashes, debuggerd unwinds the access trace using libunwindstack, which is a
|
|
// slow CFI-based unwinder. We don't split the unwinders in the test harness,
|
|
// and frame-pointer unwinding doesn't work properly though a signal handler, so
|
|
// we opt to use libunwindstack in this test. This has the effect that we get
|
|
// potentially more detailed stack frames in the allocation/deallocation traces
|
|
// (as we don't use the production unwinder), but that's fine for test-only.
|
|
size_t BacktraceUnwindstack(uintptr_t *TraceBuffer, size_t Size) {
|
|
unwindstack::LocalUnwinder unwinder;
|
|
if (!unwinder.Init()) {
|
|
return 0;
|
|
}
|
|
std::vector<unwindstack::LocalFrameData> frames;
|
|
if (!unwinder.Unwind(&frames, Size)) {
|
|
return 0;
|
|
}
|
|
for (const auto &frame : frames) {
|
|
*TraceBuffer = frame.pc;
|
|
TraceBuffer++;
|
|
}
|
|
return frames.size();
|
|
}
|
|
|
|
// We don't need any custom handling for the Segv backtrace - the unwindstack
|
|
// unwinder has no problems with unwinding through a signal handler.
|
|
size_t SegvBacktrace(uintptr_t *TraceBuffer, size_t Size, void * /*Context*/) {
|
|
return BacktraceUnwindstack(TraceBuffer, Size);
|
|
}
|
|
|
|
// This function is a good mimic as to what's happening in the out-of-process
|
|
// tombstone daemon (see debuggerd for more information). In our case, we want
|
|
// to provide symbolized backtraces during ***testing only*** here. This
|
|
// function called from a signal handler, and is extraordinarily not
|
|
// signal-safe, but works for our purposes.
|
|
void PrintBacktraceUnwindstack(uintptr_t *TraceBuffer, size_t TraceLength,
|
|
gwp_asan::Printf_t Print) {
|
|
unwindstack::UnwinderFromPid unwinder(
|
|
gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect, getpid());
|
|
unwinder.SetRegs(unwindstack::Regs::CreateFromLocal());
|
|
if (!unwinder.Init()) {
|
|
Print(" Unable to init unwinder: %s\n", unwinder.LastErrorCodeString());
|
|
return;
|
|
}
|
|
|
|
for (size_t i = 0; i < TraceLength; ++i) {
|
|
unwindstack::FrameData frame_data =
|
|
unwinder.BuildFrameFromPcOnly(TraceBuffer[i]);
|
|
frame_data.num = i;
|
|
Print(" %s\n", unwinder.FormatFrame(frame_data).c_str());
|
|
}
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
namespace gwp_asan {
|
|
namespace backtrace {
|
|
options::Backtrace_t getBacktraceFunction() { return BacktraceUnwindstack; }
|
|
|
|
PrintBacktrace_t getPrintBacktraceFunction() {
|
|
return PrintBacktraceUnwindstack;
|
|
}
|
|
|
|
SegvBacktrace_t getSegvBacktraceFunction() { return SegvBacktrace; }
|
|
} // namespace backtrace
|
|
} // namespace gwp_asan
|