Merge 34497757b0a2017190c44b270920ef793a2b0aad on remote branch
Change-Id: I9fd1b6dad0e73ddf1a8efeff9073fee33ea99b77
diff --git a/Android.bp b/Android.bp
index aeb1c77..5fa7e11 100644
--- a/Android.bp
+++ b/Android.bp
@@ -129,14 +129,29 @@
"gwp_asan",
"gwp_asan_crash_handler",
"libasync_safe",
+ "liblog",
+ "libunwindstack",
+ "liblzma", // Dependency from libunwindstack.
+ ],
+
+ ldflags: [
+ // Ensure that ICF doesn't clobber DeallocateMemory2 into
+ // DeallocateMemory in tests/backtrace.cpp. This is done in the linker,
+ // so `optnone` in the function declaration doesn't help.
+ "-Wl,--icf=none",
],
srcs: [
+ "android/test_backtrace.cpp",
"android/test_printf.cpp",
+ "gwp_asan/optional/segv_handler_posix.cpp",
"gwp_asan/tests/alignment.cpp",
+ "gwp_asan/tests/backtrace.cpp",
"gwp_asan/tests/basic.cpp",
"gwp_asan/tests/compression.cpp",
"gwp_asan/tests/crash_handler_api.cpp",
+ "gwp_asan/tests/enable_disable.cpp",
"gwp_asan/tests/harness.cpp",
+ "gwp_asan/tests/iterate.cpp",
"gwp_asan/tests/late_init.cpp",
"gwp_asan/tests/mutex_test.cpp",
"gwp_asan/tests/slot_reuse.cpp",
@@ -144,7 +159,19 @@
],
include_dirs: ["bionic/libc/async_safe/include"],
test_suites: ["general-tests"],
- cflags: ["-fno-emulated-tls"],
+ cflags: [
+ // GWP-ASan requires anything that uses GuardedPoolAllocator headers to
+ // use platform (ELF) TLS.
+ "-fno-emulated-tls",
+
+ // Ensure that the helper functions in test/backtrace.cpp don't get
+ // tail-call optimised, as this breaks the unwind chain.
+ "-fno-optimize-sibling-calls",
+
+ // The experimental pass manager seems to kill __attribute__((optnone)),
+ // and so we disable it (as we rely on optnone for tests/backtrace.cpp).
+ "-fno-experimental-new-pass-manager",
+ ],
// Late initialisation tests should run isolated, as the platform IE TLS
// PRNG should be initialised to its default state.
diff --git a/android/test_backtrace.cpp b/android/test_backtrace.cpp
new file mode 100644
index 0000000..193f490
--- /dev/null
+++ b/android/test_backtrace.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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 gwp_asan {
+namespace options {
+
+// 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();
+}
+
+Backtrace_t getBacktraceFunction() {
+ return BacktraceUnwindstack;
+}
+
+// Build a frame for symbolization using the maps from the provided unwinder.
+// The constructed frame contains just enough information to be used to
+// symbolize a GWP-ASan stack trace.
+static unwindstack::FrameData BuildFrame(unwindstack::Unwinder* unwinder, uintptr_t pc) {
+ unwindstack::FrameData frame;
+
+ unwindstack::Maps* maps = unwinder->GetMaps();
+ unwindstack::MapInfo* map_info = maps->Find(pc);
+ if (!map_info) {
+ frame.rel_pc = pc;
+ return frame;
+ }
+
+ unwindstack::Elf* elf =
+ map_info->GetElf(unwinder->GetProcessMemory(), unwindstack::Regs::CurrentArch());
+
+ uint64_t relative_pc = elf->GetRelPc(pc, map_info);
+
+ // Create registers just to get PC adjustment. Doesn't matter what they point
+ // to.
+ unwindstack::Regs* regs = unwindstack::Regs::CreateFromLocal();
+ uint64_t pc_adjustment = regs->GetPcAdjustment(relative_pc, elf);
+ relative_pc -= pc_adjustment;
+ // The debug PC may be different if the PC comes from the JIT.
+ uint64_t debug_pc = relative_pc;
+
+ // If we don't have a valid ELF file, check the JIT.
+ if (!elf->valid()) {
+ unwindstack::JitDebug jit_debug(unwinder->GetProcessMemory());
+ uint64_t jit_pc = pc - pc_adjustment;
+ unwindstack::Elf* jit_elf = jit_debug.GetElf(maps, jit_pc);
+ if (jit_elf != nullptr) {
+ debug_pc = jit_pc;
+ elf = jit_elf;
+ }
+ }
+
+ // Copy all the things we need into the frame for symbolization.
+ frame.rel_pc = relative_pc;
+ frame.pc = pc - pc_adjustment;
+ frame.map_name = map_info->name;
+ frame.map_elf_start_offset = map_info->elf_start_offset;
+ frame.map_exact_offset = map_info->offset;
+ frame.map_start = map_info->start;
+ frame.map_end = map_info->end;
+ frame.map_flags = map_info->flags;
+ frame.map_load_bias = elf->GetLoadBias();
+
+ if (!elf->GetFunctionName(relative_pc, &frame.function_name, &frame.function_offset)) {
+ frame.function_name = "";
+ frame.function_offset = 0;
+ }
+ return frame;
+}
+
+// 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,
+ crash_handler::Printf_t Print) {
+ unwindstack::UnwinderFromPid unwinder(
+ AllocationMetadata::kMaxTraceLengthToCollect, getpid());
+ unwinder.Init(unwindstack::Regs::CurrentArch());
+ unwinder.SetRegs(unwindstack::Regs::CreateFromLocal());
+
+ for (size_t i = 0; i < TraceLength; ++i) {
+ unwindstack::FrameData frame_data = BuildFrame(&unwinder, TraceBuffer[i]);
+ frame_data.num = i;
+ Print(" %s\n", unwinder.FormatFrame(frame_data).c_str());
+ }
+}
+
+crash_handler::PrintBacktrace_t getPrintBacktraceFunction() {
+ return PrintBacktraceUnwindstack;
+}
+} // namespace options
+} // namespace gwp_asan
diff --git a/android/test_printf.cpp b/android/test_printf.cpp
index 086b0aa..27bb0de 100644
--- a/android/test_printf.cpp
+++ b/android/test_printf.cpp
@@ -9,7 +9,7 @@
void PrintfWrapper(const char *Format, ...) {
va_list List;
va_start(List, Format);
- async_safe_format_log_va_list(ANDROID_LOG_FATAL, "GWP-ASan", Format, List);
+ async_safe_fatal_va_list("GWP-ASan", Format, List);
va_end(List);
}
}; // anonymous namespace
diff --git a/gwp_asan/common.cpp b/gwp_asan/common.cpp
index 4493581..3438c4b 100644
--- a/gwp_asan/common.cpp
+++ b/gwp_asan/common.cpp
@@ -59,6 +59,11 @@
uintptr_t UncompressedBuffer[kMaxTraceLengthToCollect];
size_t BacktraceLength =
Backtrace(UncompressedBuffer, kMaxTraceLengthToCollect);
+ // Backtrace() returns the number of available frames, which may be greater
+ // than the number of frames in the buffer. In this case, we need to only pack
+ // the number of frames that are in the buffer.
+ if (BacktraceLength > kMaxTraceLengthToCollect)
+ BacktraceLength = kMaxTraceLengthToCollect;
TraceSize =
compression::pack(UncompressedBuffer, BacktraceLength, CompressedTrace,
AllocationMetadata::kStackFrameStorageBytes);
diff --git a/gwp_asan/tests/backtrace.cpp b/gwp_asan/tests/backtrace.cpp
index bc81f35..b3d4427 100644
--- a/gwp_asan/tests/backtrace.cpp
+++ b/gwp_asan/tests/backtrace.cpp
@@ -8,34 +8,77 @@
#include <string>
+#include "gwp_asan/crash_handler.h"
#include "gwp_asan/tests/harness.h"
-TEST_F(BacktraceGuardedPoolAllocator, DoubleFree) {
- void *Ptr = GPA.allocate(1);
+// Optnone to ensure that the calls to these functions are not optimized away,
+// as we're looking for them in the backtraces.
+__attribute((optnone)) void *
+AllocateMemory(gwp_asan::GuardedPoolAllocator &GPA) {
+ return GPA.allocate(1);
+}
+__attribute((optnone)) void
+DeallocateMemory(gwp_asan::GuardedPoolAllocator &GPA, void *Ptr) {
GPA.deallocate(Ptr);
+}
+__attribute((optnone)) void
+DeallocateMemory2(gwp_asan::GuardedPoolAllocator &GPA, void *Ptr) {
+ GPA.deallocate(Ptr);
+}
+__attribute__((optnone)) void TouchMemory(void *Ptr) {
+ *(reinterpret_cast<volatile char *>(Ptr)) = 7;
+}
+
+TEST_F(BacktraceGuardedPoolAllocator, DoubleFree) {
+ void *Ptr = AllocateMemory(GPA);
+ DeallocateMemory(GPA, Ptr);
std::string DeathRegex = "Double Free.*";
- DeathRegex.append("backtrace\\.cpp:25.*");
+ DeathRegex.append("DeallocateMemory2.*");
DeathRegex.append("was deallocated.*");
- DeathRegex.append("backtrace\\.cpp:15.*");
+ DeathRegex.append("DeallocateMemory.*");
DeathRegex.append("was allocated.*");
- DeathRegex.append("backtrace\\.cpp:14.*");
- ASSERT_DEATH(GPA.deallocate(Ptr), DeathRegex);
+ DeathRegex.append("AllocateMemory.*");
+ ASSERT_DEATH(DeallocateMemory2(GPA, Ptr), DeathRegex);
}
TEST_F(BacktraceGuardedPoolAllocator, UseAfterFree) {
- char *Ptr = static_cast<char *>(GPA.allocate(1));
- GPA.deallocate(Ptr);
+ void *Ptr = AllocateMemory(GPA);
+ DeallocateMemory(GPA, Ptr);
std::string DeathRegex = "Use After Free.*";
- DeathRegex.append("backtrace\\.cpp:40.*");
+ DeathRegex.append("TouchMemory.*");
DeathRegex.append("was deallocated.*");
- DeathRegex.append("backtrace\\.cpp:30.*");
+ DeathRegex.append("DeallocateMemory.*");
DeathRegex.append("was allocated.*");
- DeathRegex.append("backtrace\\.cpp:29.*");
- ASSERT_DEATH({ *Ptr = 7; }, DeathRegex);
+ DeathRegex.append("AllocateMemory.*");
+ ASSERT_DEATH(TouchMemory(Ptr), DeathRegex);
+}
+
+TEST(Backtrace, Short) {
+ gwp_asan::AllocationMetadata Meta;
+ Meta.AllocationTrace.RecordBacktrace(
+ [](uintptr_t *TraceBuffer, size_t /* Size */) -> size_t {
+ TraceBuffer[0] = 123u;
+ TraceBuffer[1] = 321u;
+ return 2u;
+ });
+ uintptr_t TraceOutput[2] = {};
+ EXPECT_EQ(2u, __gwp_asan_get_allocation_trace(&Meta, TraceOutput, 2));
+ EXPECT_EQ(TraceOutput[0], 123u);
+ EXPECT_EQ(TraceOutput[1], 321u);
+}
+
+TEST(Backtrace, ExceedsStorableLength) {
+ gwp_asan::AllocationMetadata Meta;
+ Meta.AllocationTrace.RecordBacktrace(
+ [](uintptr_t * /* TraceBuffer */, size_t /* Size */) -> size_t {
+ return SIZE_MAX; // Wow, that's big!
+ });
+ uintptr_t TraceOutput;
+ EXPECT_EQ(1u, __gwp_asan_get_allocation_trace(&Meta, &TraceOutput, 1));
}