blob: 0d397fde84a30850b2195700b2cb7041c642c772 [file] [log] [blame]
/*
* 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/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
#include <unwindstack/RegsGetLocal.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::LocalMaps maps;
if (!maps.Parse()) {
return 0;
}
auto process_memory = unwindstack::Memory::CreateProcessMemoryThreadCached(getpid());
std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::CreateFromLocal());
unwindstack::RegsGetLocal(regs.get());
unwindstack::Unwinder unwinder(Size, &maps, regs.get(), process_memory);
unwinder.Unwind();
for (const auto &frame : unwinder.frames()) {
*TraceBuffer = frame.pc;
TraceBuffer++;
}
return unwinder.NumFrames();
}
// 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