summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 06:58:08 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2022-05-10 06:58:08 +0000
commit1d6dcb5ed4e62d56c21deecb68a7394172b1a8df (patch)
tree2552b2460f512b2a12eda5710d8a596f3dcb9b01
parentd4625122c3db5003390cfa5f1d6ffe3ea0e30afa (diff)
parent08d235034c8721d98a323ebad3af1329c58e62ec (diff)
downloadgwp_asan-android13-mainline-tethering-release.tar.gz
Change-Id: Iccedcf83c32ee183f4042797f4f42af2cfc56101
-rw-r--r--Android.bp14
-rw-r--r--android/test_backtrace.cpp23
-rw-r--r--gwp_asan/common.h57
-rw-r--r--gwp_asan/guarded_pool_allocator.cpp12
-rwxr-xr-xgwp_asan/scripts/symbolize.sh8
-rw-r--r--gwp_asan/tests/alignment.cpp42
-rw-r--r--gwp_asan/tests/backtrace.cpp9
-rw-r--r--gwp_asan/tests/enable_disable.cpp2
-rw-r--r--gwp_asan/tests/harness.h5
-rw-r--r--gwp_asan/tests/iterate.cpp1
10 files changed, 127 insertions, 46 deletions
diff --git a/Android.bp b/Android.bp
index 4b57e62..5f8c925 100644
--- a/Android.bp
+++ b/Android.bp
@@ -71,7 +71,12 @@ cc_defaults {
// GWP-ASan is used by bionic libc, and should have no libc/libc++
// dependencies.
- system_shared_libs: [],
+ target: {
+ bionic: {
+ system_shared_libs: [],
+ header_libs: ["libc_headers"],
+ },
+ },
stl: "none",
}
@@ -95,6 +100,7 @@ cc_library_headers {
"com.android.art.debug",
"com.android.media",
"com.android.media.swcodec",
+ "com.android.virt",
],
}
@@ -104,7 +110,6 @@ cc_library_static {
defaults: ["gwp_asan_no_libs_defaults"],
header_libs: [
"gwp_asan_headers",
- "libc_headers", // Required for pthread.h in mutex.h.
],
srcs: [
"gwp_asan/common.cpp",
@@ -139,7 +144,6 @@ cc_library {
defaults: ["gwp_asan_defaults"],
header_libs: [
"gwp_asan_headers",
- "libc_headers", // Required for assert.h
],
srcs: [
"gwp_asan/common.cpp",
@@ -222,9 +226,5 @@ cc_test {
// 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",
],
}
diff --git a/android/test_backtrace.cpp b/android/test_backtrace.cpp
index 4a6d20d..0d397fd 100644
--- a/android/test_backtrace.cpp
+++ b/android/test_backtrace.cpp
@@ -18,7 +18,10 @@
#include "gwp_asan/optional/backtrace.h"
#include "gwp_asan/optional/segv_handler.h"
-#include <unwindstack/LocalUnwinder.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+#include <unwindstack/Regs.h>
+#include <unwindstack/RegsGetLocal.h>
#include <unwindstack/Unwinder.h>
namespace {
@@ -32,19 +35,21 @@ namespace {
// 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)) {
+ unwindstack::LocalMaps maps;
+ if (!maps.Parse()) {
return 0;
}
- for (const auto &frame : frames) {
+
+ 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 frames.size();
+ return unwinder.NumFrames();
}
// We don't need any custom handling for the Segv backtrace - the unwindstack
diff --git a/gwp_asan/common.h b/gwp_asan/common.h
index 7ce367e..6b238ad 100644
--- a/gwp_asan/common.h
+++ b/gwp_asan/common.h
@@ -19,7 +19,28 @@
#include <stdint.h>
namespace gwp_asan {
-enum class Error {
+
+// Magic header that resides in the AllocatorState so that GWP-ASan bugreports
+// can be understood by tools at different versions. Out-of-process crash
+// handlers, like crashpad on Fuchsia, take the raw contents of the
+// AllocationMetatada array and the AllocatorState, and shove them into the
+// minidump. Online unpacking of these structs needs to know from which version
+// of GWP-ASan it's extracting the information, as the structures are not
+// stable.
+struct AllocatorVersionMagic {
+ // The values are copied into the structure at runtime, during
+ // `GuardedPoolAllocator::init()` so that GWP-ASan remains completely in the
+ // `.bss` segment.
+ static constexpr uint8_t kAllocatorVersionMagic[4] = {'A', 'S', 'A', 'N'};
+ uint8_t Magic[4] = {};
+ // Update the version number when the AllocatorState or AllocationMetadata
+ // change.
+ static constexpr uint16_t kAllocatorVersion = 1;
+ uint16_t Version = 0;
+ uint16_t Reserved = 0;
+};
+
+enum class Error : uint8_t {
UNKNOWN,
USE_AFTER_FREE,
DOUBLE_FREE,
@@ -84,6 +105,7 @@ struct AllocationMetadata {
// set of information required for understanding a GWP-ASan crash.
struct AllocatorState {
constexpr AllocatorState() {}
+ AllocatorVersionMagic VersionMagic{};
// Returns whether the provided pointer is a current sampled allocation that
// is owned by this pool.
@@ -123,5 +145,38 @@ struct AllocatorState {
uintptr_t FailureAddress = 0;
};
+// Below are various compile-time checks that the layout of the internal
+// GWP-ASan structures are undisturbed. If they are disturbed, the version magic
+// number needs to be increased by one, and the asserts need to be updated.
+// Out-of-process crash handlers, like breakpad/crashpad, may copy the internal
+// GWP-ASan structures into a minidump for offline reconstruction of the crash.
+// In order to accomplish this, the offline reconstructor needs to know the
+// version of GWP-ASan internal structures that it's unpacking (along with the
+// architecture-specific layout info, which is left as an exercise to the crash
+// handler).
+static_assert(offsetof(AllocatorState, VersionMagic) == 0, "");
+static_assert(sizeof(AllocatorVersionMagic) == 8, "");
+#if defined(__x86_64__)
+static_assert(sizeof(AllocatorState) == 56, "");
+static_assert(offsetof(AllocatorState, FailureAddress) == 48, "");
+static_assert(sizeof(AllocationMetadata) == 568, "");
+static_assert(offsetof(AllocationMetadata, IsDeallocated) == 560, "");
+#elif defined(__aarch64__)
+static_assert(sizeof(AllocatorState) == 56, "");
+static_assert(offsetof(AllocatorState, FailureAddress) == 48, "");
+static_assert(sizeof(AllocationMetadata) == 568, "");
+static_assert(offsetof(AllocationMetadata, IsDeallocated) == 560, "");
+#elif defined(__i386__)
+static_assert(sizeof(AllocatorState) == 32, "");
+static_assert(offsetof(AllocatorState, FailureAddress) == 28, "");
+static_assert(sizeof(AllocationMetadata) == 548, "");
+static_assert(offsetof(AllocationMetadata, IsDeallocated) == 544, "");
+#elif defined(__arm__)
+static_assert(sizeof(AllocatorState) == 32, "");
+static_assert(offsetof(AllocatorState, FailureAddress) == 28, "");
+static_assert(sizeof(AllocationMetadata) == 560, "");
+static_assert(offsetof(AllocationMetadata, IsDeallocated) == 552, "");
+#endif // defined($ARCHITECTURE)
+
} // namespace gwp_asan
#endif // GWP_ASAN_COMMON_H_
diff --git a/gwp_asan/guarded_pool_allocator.cpp b/gwp_asan/guarded_pool_allocator.cpp
index d784927..7096b42 100644
--- a/gwp_asan/guarded_pool_allocator.cpp
+++ b/gwp_asan/guarded_pool_allocator.cpp
@@ -59,6 +59,13 @@ void GuardedPoolAllocator::init(const options::Options &Opts) {
SingletonPtr = this;
Backtrace = Opts.Backtrace;
+ State.VersionMagic = {{AllocatorVersionMagic::kAllocatorVersionMagic[0],
+ AllocatorVersionMagic::kAllocatorVersionMagic[1],
+ AllocatorVersionMagic::kAllocatorVersionMagic[2],
+ AllocatorVersionMagic::kAllocatorVersionMagic[3]},
+ AllocatorVersionMagic::kAllocatorVersion,
+ 0};
+
State.MaxSimultaneousAllocations = Opts.MaxSimultaneousAllocations;
const size_t PageSize = getPlatformPageSize();
@@ -258,7 +265,10 @@ void GuardedPoolAllocator::trapOnAddress(uintptr_t Address, Error E) {
// Raise a SEGV by touching first guard page.
volatile char *p = reinterpret_cast<char *>(State.GuardedPagePool);
*p = 0;
- __builtin_unreachable();
+ // Normally, would be __builtin_unreachable(), but because of
+ // https://bugs.llvm.org/show_bug.cgi?id=47480, unreachable will DCE the
+ // volatile store above, even though it has side effects.
+ __builtin_trap();
}
void GuardedPoolAllocator::stop() {
diff --git a/gwp_asan/scripts/symbolize.sh b/gwp_asan/scripts/symbolize.sh
index fad9620..0027fa0 100755
--- a/gwp_asan/scripts/symbolize.sh
+++ b/gwp_asan/scripts/symbolize.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
# The lines that we're looking to symbolize look like this:
#0 ./a.out(_foo+0x3e6) [0x55a52e64c696]
@@ -25,7 +25,7 @@ while read -r line; do
if [ -z "$function_name" ]; then
# If the offset is binary-relative, just resolve that.
- symbolized="$(echo $function_offset | addr2line -e $binary_name)"
+ symbolized="$(echo $function_offset | addr2line -ie $binary_name)"
else
# Otherwise, the offset is function-relative. Get the address of the
# function, and add it to the offset, then symbolize.
@@ -41,7 +41,7 @@ while read -r line; do
# Add the function address and offset to get the offset into the binary.
binary_offset="$(printf "0x%X" "$((function_addr+function_offset))")"
- symbolized="$(echo $binary_offset | addr2line -e $binary_name)"
+ symbolized="$(echo $binary_offset | addr2line -ie $binary_name)"
fi
# Check that it symbolized properly. If it didn't, output the old line.
@@ -52,4 +52,4 @@ while read -r line; do
else
echo "${frame_number}${symbolized}"
fi
-done
+done 2> >(grep -v "addr2line: DWARF error: could not find variable specification")
diff --git a/gwp_asan/tests/alignment.cpp b/gwp_asan/tests/alignment.cpp
index 5f24a9a..6d1e912 100644
--- a/gwp_asan/tests/alignment.cpp
+++ b/gwp_asan/tests/alignment.cpp
@@ -34,81 +34,81 @@ public:
// numerics of the testing.
TEST(AlignmentTest, LeftAlignedAllocs) {
// Alignment < Page Size.
- EXPECT_EQ(0x4000, AlignmentTestGPA::alignUp(
+ EXPECT_EQ(0x4000u, AlignmentTestGPA::alignUp(
/* Ptr */ 0x4000, /* Alignment */ 0x1));
// Alignment == Page Size.
- EXPECT_EQ(0x4000, AlignmentTestGPA::alignUp(
+ EXPECT_EQ(0x4000u, AlignmentTestGPA::alignUp(
/* Ptr */ 0x4000, /* Alignment */ 0x1000));
// Alignment > Page Size.
- EXPECT_EQ(0x4000, AlignmentTestGPA::alignUp(
+ EXPECT_EQ(0x4000u, AlignmentTestGPA::alignUp(
/* Ptr */ 0x4000, /* Alignment */ 0x4000));
}
TEST(AlignmentTest, SingleByteAllocs) {
// Alignment < Page Size.
- EXPECT_EQ(0x1,
+ EXPECT_EQ(0x1u,
AlignmentTestGPA::getRequiredBackingSize(
/* Size */ 0x1, /* Alignment */ 0x1, /* PageSize */ 0x1000));
- EXPECT_EQ(0x7fff, AlignmentTestGPA::alignDown(
+ EXPECT_EQ(0x7fffu, AlignmentTestGPA::alignDown(
/* Ptr */ 0x8000 - 0x1, /* Alignment */ 0x1));
// Alignment == Page Size.
- EXPECT_EQ(0x1,
+ EXPECT_EQ(0x1u,
AlignmentTestGPA::getRequiredBackingSize(
/* Size */ 0x1, /* Alignment */ 0x1000, /* PageSize */ 0x1000));
- EXPECT_EQ(0x7000, AlignmentTestGPA::alignDown(
+ EXPECT_EQ(0x7000u, AlignmentTestGPA::alignDown(
/* Ptr */ 0x8000 - 0x1, /* Alignment */ 0x1000));
// Alignment > Page Size.
- EXPECT_EQ(0x3001,
+ EXPECT_EQ(0x3001u,
AlignmentTestGPA::getRequiredBackingSize(
/* Size */ 0x1, /* Alignment */ 0x4000, /* PageSize */ 0x1000));
- EXPECT_EQ(0x4000, AlignmentTestGPA::alignDown(
+ EXPECT_EQ(0x4000u, AlignmentTestGPA::alignDown(
/* Ptr */ 0x8000 - 0x1, /* Alignment */ 0x4000));
}
TEST(AlignmentTest, PageSizedAllocs) {
// Alignment < Page Size.
- EXPECT_EQ(0x1000,
+ EXPECT_EQ(0x1000u,
AlignmentTestGPA::getRequiredBackingSize(
/* Size */ 0x1000, /* Alignment */ 0x1, /* PageSize */ 0x1000));
- EXPECT_EQ(0x7000, AlignmentTestGPA::alignDown(
+ EXPECT_EQ(0x7000u, AlignmentTestGPA::alignDown(
/* Ptr */ 0x8000 - 0x1000, /* Alignment */ 0x1));
// Alignment == Page Size.
- EXPECT_EQ(0x1000, AlignmentTestGPA::getRequiredBackingSize(
+ EXPECT_EQ(0x1000u, AlignmentTestGPA::getRequiredBackingSize(
/* Size */ 0x1000, /* Alignment */ 0x1000,
/* PageSize */ 0x1000));
- EXPECT_EQ(0x7000, AlignmentTestGPA::alignDown(
+ EXPECT_EQ(0x7000u, AlignmentTestGPA::alignDown(
/* Ptr */ 0x8000 - 0x1000, /* Alignment */ 0x1000));
// Alignment > Page Size.
- EXPECT_EQ(0x4000, AlignmentTestGPA::getRequiredBackingSize(
+ EXPECT_EQ(0x4000u, AlignmentTestGPA::getRequiredBackingSize(
/* Size */ 0x1000, /* Alignment */ 0x4000,
/* PageSize */ 0x1000));
- EXPECT_EQ(0x4000, AlignmentTestGPA::alignDown(
+ EXPECT_EQ(0x4000u, AlignmentTestGPA::alignDown(
/* Ptr */ 0x8000 - 0x1000, /* Alignment */ 0x4000));
}
TEST(AlignmentTest, MoreThanPageAllocs) {
// Alignment < Page Size.
- EXPECT_EQ(0x2fff,
+ EXPECT_EQ(0x2fffu,
AlignmentTestGPA::getRequiredBackingSize(
/* Size */ 0x2fff, /* Alignment */ 0x1, /* PageSize */ 0x1000));
- EXPECT_EQ(0x5001, AlignmentTestGPA::alignDown(
+ EXPECT_EQ(0x5001u, AlignmentTestGPA::alignDown(
/* Ptr */ 0x8000 - 0x2fff, /* Alignment */ 0x1));
// Alignment == Page Size.
- EXPECT_EQ(0x2fff, AlignmentTestGPA::getRequiredBackingSize(
+ EXPECT_EQ(0x2fffu, AlignmentTestGPA::getRequiredBackingSize(
/* Size */ 0x2fff, /* Alignment */ 0x1000,
/* PageSize */ 0x1000));
- EXPECT_EQ(0x5000, AlignmentTestGPA::alignDown(
+ EXPECT_EQ(0x5000u, AlignmentTestGPA::alignDown(
/* Ptr */ 0x8000 - 0x2fff, /* Alignment */ 0x1000));
// Alignment > Page Size.
- EXPECT_EQ(0x5fff, AlignmentTestGPA::getRequiredBackingSize(
+ EXPECT_EQ(0x5fffu, AlignmentTestGPA::getRequiredBackingSize(
/* Size */ 0x2fff, /* Alignment */ 0x4000,
/* PageSize */ 0x1000));
- EXPECT_EQ(0x4000, AlignmentTestGPA::alignDown(
+ EXPECT_EQ(0x4000u, AlignmentTestGPA::alignDown(
/* Ptr */ 0x8000 - 0x2fff, /* Alignment */ 0x4000));
}
diff --git a/gwp_asan/tests/backtrace.cpp b/gwp_asan/tests/backtrace.cpp
index 9515065..a4eb8eb 100644
--- a/gwp_asan/tests/backtrace.cpp
+++ b/gwp_asan/tests/backtrace.cpp
@@ -30,7 +30,7 @@ __attribute__((optnone)) void TouchMemory(void *Ptr) {
*(reinterpret_cast<volatile char *>(Ptr)) = 7;
}
-TEST_F(BacktraceGuardedPoolAllocator, DoubleFree) {
+TEST_F(BacktraceGuardedPoolAllocatorDeathTest, DoubleFree) {
void *Ptr = AllocateMemory(GPA);
DeallocateMemory(GPA, Ptr);
@@ -45,7 +45,12 @@ TEST_F(BacktraceGuardedPoolAllocator, DoubleFree) {
ASSERT_DEATH(DeallocateMemory2(GPA, Ptr), DeathRegex);
}
-TEST_F(BacktraceGuardedPoolAllocator, UseAfterFree) {
+TEST_F(BacktraceGuardedPoolAllocatorDeathTest, UseAfterFree) {
+#if defined(__linux__) && __ARM_ARCH == 7
+ // Incomplete backtrace on Armv7 Linux
+ GTEST_SKIP();
+#endif
+
void *Ptr = AllocateMemory(GPA);
DeallocateMemory(GPA, Ptr);
diff --git a/gwp_asan/tests/enable_disable.cpp b/gwp_asan/tests/enable_disable.cpp
index 2c6ba51..98da591 100644
--- a/gwp_asan/tests/enable_disable.cpp
+++ b/gwp_asan/tests/enable_disable.cpp
@@ -10,7 +10,7 @@
constexpr size_t Size = 100;
-TEST_F(DefaultGuardedPoolAllocator, Fork) {
+TEST_F(DefaultGuardedPoolAllocatorDeathTest, Fork) {
void *P;
pid_t Pid = fork();
EXPECT_GE(Pid, 0);
diff --git a/gwp_asan/tests/harness.h b/gwp_asan/tests/harness.h
index a61b856..ed91e64 100644
--- a/gwp_asan/tests/harness.h
+++ b/gwp_asan/tests/harness.h
@@ -106,4 +106,9 @@ protected:
gwp_asan::GuardedPoolAllocator GPA;
};
+// https://github.com/google/googletest/blob/master/docs/advanced.md#death-tests-and-threads
+using DefaultGuardedPoolAllocatorDeathTest = DefaultGuardedPoolAllocator;
+using CustomGuardedPoolAllocatorDeathTest = CustomGuardedPoolAllocator;
+using BacktraceGuardedPoolAllocatorDeathTest = BacktraceGuardedPoolAllocator;
+
#endif // GWP_ASAN_TESTS_HARNESS_H_
diff --git a/gwp_asan/tests/iterate.cpp b/gwp_asan/tests/iterate.cpp
index 2b8635d..49953f3 100644
--- a/gwp_asan/tests/iterate.cpp
+++ b/gwp_asan/tests/iterate.cpp
@@ -8,6 +8,7 @@
#include "gwp_asan/tests/harness.h"
+#include <algorithm>
#include <set>
#include <vector>