summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-07-15 01:25:49 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2021-07-15 01:25:49 +0000
commit4a1e82a80aa08dc780a6f82af736df159325eae0 (patch)
tree5e1e85016be16b395eef3d2c25a902ee8fa01c58
parent50332c5d6f14f080bcba05cc580776aa710a6851 (diff)
parentde43ad5cc600ece030fb75f7e3318ab0b01386d5 (diff)
downloadgwp_asan-android12-mainline-conscrypt-release.tar.gz
Snap for 7550844 from de43ad5cc600ece030fb75f7e3318ab0b01386d5 to mainline-conscrypt-releaseandroid-mainline-12.0.0_r8android-mainline-12.0.0_r25android12-mainline-conscrypt-release
Change-Id: Ia718c6bf290c8ac6840e2d7ce4dc2bc3294b741e
-rw-r--r--Android.bp59
-rw-r--r--METADATA1
l---------NOTICE1
-rw-r--r--TEST_MAPPING11
-rw-r--r--android/test_backtrace.cpp91
-rw-r--r--android/test_printf.cpp13
-rw-r--r--gwp_asan/common.cpp5
-rw-r--r--gwp_asan/common.h6
-rw-r--r--gwp_asan/crash_handler.cpp26
-rw-r--r--gwp_asan/crash_handler.h2
-rw-r--r--gwp_asan/definitions.h2
-rw-r--r--gwp_asan/guarded_pool_allocator.cpp217
-rw-r--r--gwp_asan/guarded_pool_allocator.h106
-rw-r--r--gwp_asan/mutex.h14
-rw-r--r--gwp_asan/optional/backtrace.h48
-rw-r--r--gwp_asan/optional/backtrace_fuchsia.cpp27
-rw-r--r--gwp_asan/optional/backtrace_linux_libc.cpp25
-rw-r--r--gwp_asan/optional/backtrace_sanitizer_common.cpp49
-rw-r--r--gwp_asan/optional/options_parser.cpp249
-rw-r--r--gwp_asan/optional/options_parser.h12
-rw-r--r--gwp_asan/optional/printf.h33
-rw-r--r--gwp_asan/optional/segv_handler.h70
-rw-r--r--gwp_asan/optional/segv_handler_fuchsia.cpp22
-rw-r--r--gwp_asan/optional/segv_handler_posix.cpp182
-rw-r--r--gwp_asan/options.inc38
-rw-r--r--gwp_asan/platform_specific/common_fuchsia.cpp15
-rw-r--r--gwp_asan/platform_specific/common_posix.cpp6
-rw-r--r--gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.cpp104
-rw-r--r--gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.h22
-rw-r--r--gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp64
-rw-r--r--gwp_asan/platform_specific/guarded_pool_allocator_posix.h18
-rw-r--r--gwp_asan/platform_specific/guarded_pool_allocator_tls.h55
-rw-r--r--gwp_asan/platform_specific/mutex_fuchsia.cpp21
-rw-r--r--gwp_asan/platform_specific/mutex_fuchsia.h23
-rw-r--r--gwp_asan/platform_specific/mutex_posix.h23
-rw-r--r--gwp_asan/platform_specific/utilities_fuchsia.cpp19
-rw-r--r--gwp_asan/platform_specific/utilities_posix.cpp65
-rw-r--r--gwp_asan/random.cpp31
-rw-r--r--gwp_asan/random.h23
-rw-r--r--gwp_asan/tests/alignment.cpp126
-rw-r--r--gwp_asan/tests/backtrace.cpp44
-rw-r--r--gwp_asan/tests/basic.cpp31
-rw-r--r--gwp_asan/tests/compression.cpp2
-rw-r--r--gwp_asan/tests/crash_handler_api.cpp7
-rw-r--r--gwp_asan/tests/driver.cpp2
-rw-r--r--gwp_asan/tests/harness.cpp10
-rw-r--r--gwp_asan/tests/harness.h24
-rw-r--r--gwp_asan/tests/iterate.cpp3
-rw-r--r--gwp_asan/tests/late_init.cpp2
-rw-r--r--gwp_asan/tests/mutex_test.cpp2
-rw-r--r--gwp_asan/tests/options.cpp63
-rw-r--r--gwp_asan/tests/platform_specific/printf_sanitizer_common.cpp (renamed from gwp_asan/tests/optional/printf_sanitizer_common.cpp)15
-rw-r--r--gwp_asan/tests/slot_reuse.cpp2
-rw-r--r--gwp_asan/utilities.h29
-rw-r--r--tools/options_parser_fuzzer.cpp12
55 files changed, 1503 insertions, 669 deletions
diff --git a/Android.bp b/Android.bp
index dacc40f..4b57e62 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,13 +14,47 @@
// limitations under the License.
//
+package {
+ default_applicable_licenses: ["external_gwp_asan_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+ name: "external_gwp_asan_license",
+ visibility: [":__subpackages__"],
+ license_kinds: [
+ "SPDX-license-identifier-Apache-2.0",
+ "SPDX-license-identifier-BSD",
+ "SPDX-license-identifier-MIT",
+ "SPDX-license-identifier-NCSA",
+ ],
+ license_text: [
+ "LICENSE.TXT",
+ ],
+}
+
cc_defaults {
name: "gwp_asan_defaults",
host_supported: true,
vendor_available: true,
+ product_available: true,
recovery_available: true,
native_bridge_supported: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
// GWP-ASan currently has no support for darwin.
target: {
@@ -28,6 +62,7 @@ cc_defaults {
enabled: false,
},
},
+ min_sdk_version: "S",
}
cc_defaults {
@@ -56,8 +91,8 @@ cc_library_headers {
"//apex_available:platform",
"com.android.runtime",
// GWP-ASan headers are currently referenced by the following additional APEXes
+ "com.android.art",
"com.android.art.debug",
- "com.android.art.release",
"com.android.media",
"com.android.media.swcodec",
],
@@ -78,7 +113,6 @@ cc_library_static {
"gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp",
"gwp_asan/platform_specific/mutex_posix.cpp",
"gwp_asan/platform_specific/utilities_posix.cpp",
- "gwp_asan/random.cpp",
"gwp_asan/stack_trace_compressor.cpp",
],
// GWP-ASan requires platform (non-emulated) TLS. We use thread local
@@ -127,6 +161,16 @@ cc_fuzz {
static_libs: ["gwp_asan"],
}
+cc_fuzz {
+ name: "options_parser_fuzzer",
+ host_supported: true,
+ srcs: [
+ "tools/options_parser_fuzzer.cpp",
+ "gwp_asan/optional/options_parser.cpp",
+ ],
+ static_libs: ["gwp_asan"],
+}
+
// These unit tests are also continuously run upstream in LLVM's buildbots.
cc_test {
name: "gwp_asan_unittest",
@@ -136,9 +180,12 @@ cc_test {
"gwp_asan_crash_handler",
"libasync_safe",
"liblog",
- "libunwindstack",
+ "libunwindstack_no_dex",
"liblzma", // Dependency from libunwindstack.
],
+ shared_libs: [
+ "libbase",
+ ],
ldflags: [
// Ensure that ICF doesn't clobber DeallocateMemory2 into
@@ -149,6 +196,7 @@ cc_test {
srcs: [
"android/test_backtrace.cpp",
"android/test_printf.cpp",
+ "gwp_asan/optional/options_parser.cpp",
"gwp_asan/optional/segv_handler_posix.cpp",
"gwp_asan/tests/alignment.cpp",
"gwp_asan/tests/backtrace.cpp",
@@ -160,6 +208,7 @@ cc_test {
"gwp_asan/tests/iterate.cpp",
"gwp_asan/tests/late_init.cpp",
"gwp_asan/tests/mutex_test.cpp",
+ "gwp_asan/tests/options.cpp",
"gwp_asan/tests/slot_reuse.cpp",
"gwp_asan/tests/thread_contention.cpp",
],
@@ -178,8 +227,4 @@ cc_test {
// 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.
- isolated: true,
}
diff --git a/METADATA b/METADATA
index 9d842c9..aa8159e 100644
--- a/METADATA
+++ b/METADATA
@@ -16,5 +16,6 @@ third_party {
value: "https://github.com/llvm/llvm-project.git"
}
version: "8f5e1755ca385566c0352a9bd292218cebfd3d0b"
+ license_type: NOTICE
last_upgrade_date { year: 2019 month: 8 day: 21 }
}
diff --git a/NOTICE b/NOTICE
deleted file mode 120000
index 7a694c9..0000000
--- a/NOTICE
+++ /dev/null
@@ -1 +0,0 @@
-LICENSE \ No newline at end of file
diff --git a/TEST_MAPPING b/TEST_MAPPING
index a4db69b..9328166 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,7 +1,18 @@
{
"presubmit": [
{
+ "name": "CtsGwpAsanTestCases"
+ },
+ {
+ "name": "debuggerd_test"
+ },
+ {
"name": "gwp_asan_unittest"
}
+ ],
+ "imports": [
+ {
+ "path": "bionic"
+ }
]
}
diff --git a/android/test_backtrace.cpp b/android/test_backtrace.cpp
index 193f490..4a6d20d 100644
--- a/android/test_backtrace.cpp
+++ b/android/test_backtrace.cpp
@@ -21,9 +21,7 @@
#include <unwindstack/LocalUnwinder.h>
#include <unwindstack/Unwinder.h>
-namespace gwp_asan {
-namespace options {
-
+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
@@ -42,70 +40,17 @@ size_t BacktraceUnwindstack(uintptr_t *TraceBuffer, size_t Size) {
if (!unwinder.Unwind(&frames, Size)) {
return 0;
}
- for (const auto& frame : frames) {
+ 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;
+// 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
@@ -114,21 +59,33 @@ static unwindstack::FrameData BuildFrame(unwindstack::Unwinder* unwinder, uintpt
// 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) {
+ gwp_asan::Printf_t Print) {
unwindstack::UnwinderFromPid unwinder(
- AllocationMetadata::kMaxTraceLengthToCollect, getpid());
- unwinder.Init(unwindstack::Regs::CurrentArch());
+ 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 = BuildFrame(&unwinder, TraceBuffer[i]);
+ unwindstack::FrameData frame_data =
+ unwinder.BuildFrameFromPcOnly(TraceBuffer[i]);
frame_data.num = i;
Print(" %s\n", unwinder.FormatFrame(frame_data).c_str());
}
}
-crash_handler::PrintBacktrace_t getPrintBacktraceFunction() {
+} // anonymous namespace
+
+namespace gwp_asan {
+namespace backtrace {
+options::Backtrace_t getBacktraceFunction() { return BacktraceUnwindstack; }
+
+PrintBacktrace_t getPrintBacktraceFunction() {
return PrintBacktraceUnwindstack;
}
-} // namespace options
+
+SegvBacktrace_t getSegvBacktraceFunction() { return SegvBacktrace; }
+} // namespace backtrace
} // namespace gwp_asan
diff --git a/android/test_printf.cpp b/android/test_printf.cpp
index 27bb0de..d5b0607 100644
--- a/android/test_printf.cpp
+++ b/android/test_printf.cpp
@@ -2,8 +2,7 @@
#include <stdarg.h>
#include <unistd.h>
-#include "gwp_asan/optional/segv_handler.h"
-#include "gwp_asan/options.h"
+#include "gwp_asan/optional/printf.h"
namespace {
void PrintfWrapper(const char *Format, ...) {
@@ -12,12 +11,12 @@ void PrintfWrapper(const char *Format, ...) {
async_safe_fatal_va_list("GWP-ASan", Format, List);
va_end(List);
}
-}; // anonymous namespace
+} // anonymous namespace
namespace gwp_asan {
namespace test {
// Android version of the Printf() function for use in gwp_asan_unittest. You
-// can find the declaration of this function in gwp_asan/tests/harness.h
-crash_handler::Printf_t getPrintfFunction() { return PrintfWrapper; }
-}; // namespace test
-}; // namespace gwp_asan
+// can find the declaration of this function in gwp_asan/optional/printf.h
+Printf_t getPrintfFunction() { return PrintfWrapper; }
+} // namespace test
+} // namespace gwp_asan
diff --git a/gwp_asan/common.cpp b/gwp_asan/common.cpp
index 3438c4b..b0f6c58 100644
--- a/gwp_asan/common.cpp
+++ b/gwp_asan/common.cpp
@@ -34,10 +34,13 @@ const char *ErrorToString(const Error &E) {
__builtin_trap();
}
+constexpr size_t AllocationMetadata::kStackFrameStorageBytes;
+constexpr size_t AllocationMetadata::kMaxTraceLengthToCollect;
+
void AllocationMetadata::RecordAllocation(uintptr_t AllocAddr,
size_t AllocSize) {
Addr = AllocAddr;
- Size = AllocSize;
+ RequestedSize = AllocSize;
IsDeallocated = false;
AllocationTrace.ThreadID = getThreadID();
diff --git a/gwp_asan/common.h b/gwp_asan/common.h
index d197711..7ce367e 100644
--- a/gwp_asan/common.h
+++ b/gwp_asan/common.h
@@ -49,7 +49,7 @@ struct AllocationMetadata {
static constexpr size_t kMaxTraceLengthToCollect = 128;
// Records the given allocation metadata into this struct.
- void RecordAllocation(uintptr_t Addr, size_t Size);
+ void RecordAllocation(uintptr_t Addr, size_t RequestedSize);
// Record that this allocation is now deallocated.
void RecordDeallocation();
@@ -70,7 +70,7 @@ struct AllocationMetadata {
// valid, as the allocation has never occurred.
uintptr_t Addr = 0;
// Represents the actual size of the allocation.
- size_t Size = 0;
+ size_t RequestedSize = 0;
CallSiteInfo AllocationTrace;
CallSiteInfo DeallocationTrace;
@@ -83,6 +83,8 @@ struct AllocationMetadata {
// crash handler. This, in conjunction with the Metadata array, forms the entire
// set of information required for understanding a GWP-ASan crash.
struct AllocatorState {
+ constexpr AllocatorState() {}
+
// Returns whether the provided pointer is a current sampled allocation that
// is owned by this pool.
GWP_ASAN_ALWAYS_INLINE bool pointerIsMine(const void *Ptr) const {
diff --git a/gwp_asan/crash_handler.cpp b/gwp_asan/crash_handler.cpp
index c3b9e14..6b4c39e 100644
--- a/gwp_asan/crash_handler.cpp
+++ b/gwp_asan/crash_handler.cpp
@@ -1,4 +1,4 @@
-//===-- crash_handler_interface.cpp -----------------------------*- C++ -*-===//
+//===-- crash_handler.cpp ---------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -10,6 +10,8 @@
#include "gwp_asan/stack_trace_compressor.h"
#include <assert.h>
+#include <stdint.h>
+#include <string.h>
using AllocationMetadata = gwp_asan::AllocationMetadata;
using Error = gwp_asan::Error;
@@ -101,7 +103,7 @@ uintptr_t __gwp_asan_get_allocation_address(
size_t __gwp_asan_get_allocation_size(
const gwp_asan::AllocationMetadata *AllocationMeta) {
- return AllocationMeta->Size;
+ return AllocationMeta->RequestedSize;
}
uint64_t __gwp_asan_get_allocation_thread_id(
@@ -112,9 +114,15 @@ uint64_t __gwp_asan_get_allocation_thread_id(
size_t __gwp_asan_get_allocation_trace(
const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,
size_t BufferLen) {
- return gwp_asan::compression::unpack(
+ uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];
+ size_t UnpackedLength = gwp_asan::compression::unpack(
AllocationMeta->AllocationTrace.CompressedTrace,
- AllocationMeta->AllocationTrace.TraceSize, Buffer, BufferLen);
+ AllocationMeta->AllocationTrace.TraceSize, UncompressedBuffer,
+ AllocationMetadata::kMaxTraceLengthToCollect);
+ if (UnpackedLength < BufferLen)
+ BufferLen = UnpackedLength;
+ memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));
+ return UnpackedLength;
}
bool __gwp_asan_is_deallocated(
@@ -130,9 +138,15 @@ uint64_t __gwp_asan_get_deallocation_thread_id(
size_t __gwp_asan_get_deallocation_trace(
const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,
size_t BufferLen) {
- return gwp_asan::compression::unpack(
+ uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];
+ size_t UnpackedLength = gwp_asan::compression::unpack(
AllocationMeta->DeallocationTrace.CompressedTrace,
- AllocationMeta->DeallocationTrace.TraceSize, Buffer, BufferLen);
+ AllocationMeta->DeallocationTrace.TraceSize, UncompressedBuffer,
+ AllocationMetadata::kMaxTraceLengthToCollect);
+ if (UnpackedLength < BufferLen)
+ BufferLen = UnpackedLength;
+ memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));
+ return UnpackedLength;
}
#ifdef __cplusplus
diff --git a/gwp_asan/crash_handler.h b/gwp_asan/crash_handler.h
index 631c319..4a95069 100644
--- a/gwp_asan/crash_handler.h
+++ b/gwp_asan/crash_handler.h
@@ -1,4 +1,4 @@
-//===-- crash_handler_interface.h -------------------------------*- C++ -*-===//
+//===-- crash_handler.h -----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/gwp_asan/definitions.h b/gwp_asan/definitions.h
index 563c408..bec0290 100644
--- a/gwp_asan/definitions.h
+++ b/gwp_asan/definitions.h
@@ -1,4 +1,4 @@
-//===-- gwp_asan_definitions.h ----------------------------------*- C++ -*-===//
+//===-- definitions.h -------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/gwp_asan/guarded_pool_allocator.cpp b/gwp_asan/guarded_pool_allocator.cpp
index b2602e4..d784927 100644
--- a/gwp_asan/guarded_pool_allocator.cpp
+++ b/gwp_asan/guarded_pool_allocator.cpp
@@ -8,24 +8,11 @@
#include "gwp_asan/guarded_pool_allocator.h"
-#include "gwp_asan/optional/segv_handler.h"
#include "gwp_asan/options.h"
-#include "gwp_asan/random.h"
#include "gwp_asan/utilities.h"
-// RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this
-// macro is defined before including <inttypes.h>.
-#ifndef __STDC_FORMAT_MACROS
-#define __STDC_FORMAT_MACROS 1
-#endif
-
#include <assert.h>
-#include <inttypes.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
+#include <stddef.h>
using AllocationMetadata = gwp_asan::AllocationMetadata;
using Error = gwp_asan::Error;
@@ -39,14 +26,15 @@ namespace {
// init-order-fiasco.
GuardedPoolAllocator *SingletonPtr = nullptr;
-class ScopedBoolean {
-public:
- ScopedBoolean(bool &B) : Bool(B) { Bool = true; }
- ~ScopedBoolean() { Bool = false; }
+size_t roundUpTo(size_t Size, size_t Boundary) {
+ return (Size + Boundary - 1) & ~(Boundary - 1);
+}
-private:
- bool &Bool;
-};
+uintptr_t getPageAddr(uintptr_t Ptr, uintptr_t PageSize) {
+ return Ptr & ~(PageSize - 1);
+}
+
+bool isPowerOfTwo(uintptr_t X) { return (X & (X - 1)) == 0; }
} // anonymous namespace
// Gets the singleton implementation of this class. Thread-compatible until
@@ -64,7 +52,7 @@ void GuardedPoolAllocator::init(const options::Options &Opts) {
return;
Check(Opts.SampleRate >= 0, "GWP-ASan Error: SampleRate is < 0.");
- Check(Opts.SampleRate <= INT32_MAX, "GWP-ASan Error: SampleRate is > 2^31.");
+ Check(Opts.SampleRate < (1 << 30), "GWP-ASan Error: SampleRate is >= 2^30.");
Check(Opts.MaxSimultaneousAllocations >= 0,
"GWP-ASan Error: MaxSimultaneousAllocations is < 0.");
@@ -73,25 +61,27 @@ void GuardedPoolAllocator::init(const options::Options &Opts) {
State.MaxSimultaneousAllocations = Opts.MaxSimultaneousAllocations;
- State.PageSize = getPlatformPageSize();
-
- PerfectlyRightAlign = Opts.PerfectlyRightAlign;
+ const size_t PageSize = getPlatformPageSize();
+ // getPageAddr() and roundUpTo() assume the page size to be a power of 2.
+ assert((PageSize & (PageSize - 1)) == 0);
+ State.PageSize = PageSize;
size_t PoolBytesRequired =
- State.PageSize * (1 + State.MaxSimultaneousAllocations) +
+ PageSize * (1 + State.MaxSimultaneousAllocations) +
State.MaxSimultaneousAllocations * State.maximumAllocationSize();
- void *GuardedPoolMemory = mapMemory(PoolBytesRequired, kGwpAsanGuardPageName);
+ assert(PoolBytesRequired % PageSize == 0);
+ void *GuardedPoolMemory = reserveGuardedPool(PoolBytesRequired);
- size_t BytesRequired = State.MaxSimultaneousAllocations * sizeof(*Metadata);
+ size_t BytesRequired =
+ roundUpTo(State.MaxSimultaneousAllocations * sizeof(*Metadata), PageSize);
Metadata = reinterpret_cast<AllocationMetadata *>(
- mapMemory(BytesRequired, kGwpAsanMetadataName));
- markReadWrite(Metadata, BytesRequired, kGwpAsanMetadataName);
+ map(BytesRequired, kGwpAsanMetadataName));
// Allocate memory and set up the free pages queue.
- BytesRequired = State.MaxSimultaneousAllocations * sizeof(*FreeSlots);
- FreeSlots = reinterpret_cast<size_t *>(
- mapMemory(BytesRequired, kGwpAsanFreeSlotsName));
- markReadWrite(FreeSlots, BytesRequired, kGwpAsanFreeSlotsName);
+ BytesRequired = roundUpTo(
+ State.MaxSimultaneousAllocations * sizeof(*FreeSlots), PageSize);
+ FreeSlots =
+ reinterpret_cast<size_t *>(map(BytesRequired, kGwpAsanFreeSlotsName));
// Multiply the sample rate by 2 to give a good, fast approximation for (1 /
// SampleRate) chance of sampling.
@@ -101,8 +91,9 @@ void GuardedPoolAllocator::init(const options::Options &Opts) {
AdjustedSampleRatePlusOne = 2;
initPRNG();
- ThreadLocals.NextSampleCounter =
- (getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1;
+ getThreadLocals()->NextSampleCounter =
+ ((getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1) &
+ ThreadLocalPackedVariables::NextSampleCounterMask;
State.GuardedPagePool = reinterpret_cast<uintptr_t>(GuardedPoolMemory);
State.GuardedPagePoolEnd =
@@ -112,9 +103,15 @@ void GuardedPoolAllocator::init(const options::Options &Opts) {
installAtFork();
}
-void GuardedPoolAllocator::disable() { PoolMutex.lock(); }
+void GuardedPoolAllocator::disable() {
+ PoolMutex.lock();
+ BacktraceMutex.lock();
+}
-void GuardedPoolAllocator::enable() { PoolMutex.unlock(); }
+void GuardedPoolAllocator::enable() {
+ PoolMutex.unlock();
+ BacktraceMutex.unlock();
+}
void GuardedPoolAllocator::iterate(void *Base, size_t Size, iterate_callback Cb,
void *Arg) {
@@ -123,48 +120,96 @@ void GuardedPoolAllocator::iterate(void *Base, size_t Size, iterate_callback Cb,
const AllocationMetadata &Meta = Metadata[i];
if (Meta.Addr && !Meta.IsDeallocated && Meta.Addr >= Start &&
Meta.Addr < Start + Size)
- Cb(Meta.Addr, Meta.Size, Arg);
+ Cb(Meta.Addr, Meta.RequestedSize, Arg);
}
}
void GuardedPoolAllocator::uninitTestOnly() {
if (State.GuardedPagePool) {
- unmapMemory(reinterpret_cast<void *>(State.GuardedPagePool),
- State.GuardedPagePoolEnd - State.GuardedPagePool,
- kGwpAsanGuardPageName);
+ unreserveGuardedPool();
State.GuardedPagePool = 0;
State.GuardedPagePoolEnd = 0;
}
if (Metadata) {
- unmapMemory(Metadata, State.MaxSimultaneousAllocations * sizeof(*Metadata),
- kGwpAsanMetadataName);
+ unmap(Metadata,
+ roundUpTo(State.MaxSimultaneousAllocations * sizeof(*Metadata),
+ State.PageSize));
Metadata = nullptr;
}
if (FreeSlots) {
- unmapMemory(FreeSlots,
- State.MaxSimultaneousAllocations * sizeof(*FreeSlots),
- kGwpAsanFreeSlotsName);
+ unmap(FreeSlots,
+ roundUpTo(State.MaxSimultaneousAllocations * sizeof(*FreeSlots),
+ State.PageSize));
FreeSlots = nullptr;
}
+ *getThreadLocals() = ThreadLocalPackedVariables();
}
-static uintptr_t getPageAddr(uintptr_t Ptr, uintptr_t PageSize) {
- return Ptr & ~(PageSize - 1);
+// Note, minimum backing allocation size in GWP-ASan is always one page, and
+// each slot could potentially be multiple pages (but always in
+// page-increments). Thus, for anything that requires less than page size
+// alignment, we don't need to allocate extra padding to ensure the alignment
+// can be met.
+size_t GuardedPoolAllocator::getRequiredBackingSize(size_t Size,
+ size_t Alignment,
+ size_t PageSize) {
+ assert(isPowerOfTwo(Alignment) && "Alignment must be a power of two!");
+ assert(Alignment != 0 && "Alignment should be non-zero");
+ assert(Size != 0 && "Size should be non-zero");
+
+ if (Alignment <= PageSize)
+ return Size;
+
+ return Size + Alignment - PageSize;
}
-void *GuardedPoolAllocator::allocate(size_t Size) {
+uintptr_t GuardedPoolAllocator::alignUp(uintptr_t Ptr, size_t Alignment) {
+ assert(isPowerOfTwo(Alignment) && "Alignment must be a power of two!");
+ assert(Alignment != 0 && "Alignment should be non-zero");
+ if ((Ptr & (Alignment - 1)) == 0)
+ return Ptr;
+
+ Ptr += Alignment - (Ptr & (Alignment - 1));
+ return Ptr;
+}
+
+uintptr_t GuardedPoolAllocator::alignDown(uintptr_t Ptr, size_t Alignment) {
+ assert(isPowerOfTwo(Alignment) && "Alignment must be a power of two!");
+ assert(Alignment != 0 && "Alignment should be non-zero");
+ if ((Ptr & (Alignment - 1)) == 0)
+ return Ptr;
+
+ Ptr -= Ptr & (Alignment - 1);
+ return Ptr;
+}
+
+void *GuardedPoolAllocator::allocate(size_t Size, size_t Alignment) {
// GuardedPagePoolEnd == 0 when GWP-ASan is disabled. If we are disabled, fall
// back to the supporting allocator.
- if (State.GuardedPagePoolEnd == 0)
+ if (State.GuardedPagePoolEnd == 0) {
+ getThreadLocals()->NextSampleCounter =
+ (AdjustedSampleRatePlusOne - 1) &
+ ThreadLocalPackedVariables::NextSampleCounterMask;
return nullptr;
+ }
- // Protect against recursivity.
- if (ThreadLocals.RecursiveGuard)
+ if (Size == 0)
+ Size = 1;
+ if (Alignment == 0)
+ Alignment = alignof(max_align_t);
+
+ if (!isPowerOfTwo(Alignment) || Alignment > State.maximumAllocationSize() ||
+ Size > State.maximumAllocationSize())
return nullptr;
- ScopedBoolean SB(ThreadLocals.RecursiveGuard);
- if (Size == 0 || Size > State.maximumAllocationSize())
+ size_t BackingSize = getRequiredBackingSize(Size, Alignment, State.PageSize);
+ if (BackingSize > State.maximumAllocationSize())
+ return nullptr;
+
+ // Protect against recursivity.
+ if (getThreadLocals()->RecursiveGuard)
return nullptr;
+ ScopedRecursiveGuard SRG;
size_t Index;
{
@@ -175,27 +220,35 @@ void *GuardedPoolAllocator::allocate(size_t Size) {
if (Index == kInvalidSlotID)
return nullptr;
- uintptr_t Ptr = State.slotToAddr(Index);
- // Should we right-align this allocation?
- if (getRandomUnsigned32() % 2 == 0) {
- AlignmentStrategy Align = AlignmentStrategy::DEFAULT;
- if (PerfectlyRightAlign)
- Align = AlignmentStrategy::PERFECT;
- Ptr +=
- State.maximumAllocationSize() - rightAlignedAllocationSize(Size, Align);
- }
- AllocationMetadata *Meta = addrToMetadata(Ptr);
+ uintptr_t SlotStart = State.slotToAddr(Index);
+ AllocationMetadata *Meta = addrToMetadata(SlotStart);
+ uintptr_t SlotEnd = State.slotToAddr(Index) + State.maximumAllocationSize();
+ uintptr_t UserPtr;
+ // Randomly choose whether to left-align or right-align the allocation, and
+ // then apply the necessary adjustments to get an aligned pointer.
+ if (getRandomUnsigned32() % 2 == 0)
+ UserPtr = alignUp(SlotStart, Alignment);
+ else
+ UserPtr = alignDown(SlotEnd - Size, Alignment);
+
+ assert(UserPtr >= SlotStart);
+ assert(UserPtr + Size <= SlotEnd);
// If a slot is multiple pages in size, and the allocation takes up a single
// page, we can improve overflow detection by leaving the unused pages as
// unmapped.
- markReadWrite(reinterpret_cast<void *>(getPageAddr(Ptr, State.PageSize)),
- Size, kGwpAsanAliveSlotName);
+ const size_t PageSize = State.PageSize;
+ allocateInGuardedPool(
+ reinterpret_cast<void *>(getPageAddr(UserPtr, PageSize)),
+ roundUpTo(Size, PageSize));
- Meta->RecordAllocation(Ptr, Size);
- Meta->AllocationTrace.RecordBacktrace(Backtrace);
+ Meta->RecordAllocation(UserPtr, Size);
+ {
+ ScopedLock UL(BacktraceMutex);
+ Meta->AllocationTrace.RecordBacktrace(Backtrace);
+ }
- return reinterpret_cast<void *>(Ptr);
+ return reinterpret_cast<void *>(UserPtr);
}
void GuardedPoolAllocator::trapOnAddress(uintptr_t Address, Error E) {
@@ -209,7 +262,7 @@ void GuardedPoolAllocator::trapOnAddress(uintptr_t Address, Error E) {
}
void GuardedPoolAllocator::stop() {
- ThreadLocals.RecursiveGuard = true;
+ getThreadLocals()->RecursiveGuard = true;
PoolMutex.tryLock();
}
@@ -240,14 +293,15 @@ void GuardedPoolAllocator::deallocate(void *Ptr) {
// Ensure that the unwinder is not called if the recursive flag is set,
// otherwise non-reentrant unwinders may deadlock.
- if (!ThreadLocals.RecursiveGuard) {
- ScopedBoolean B(ThreadLocals.RecursiveGuard);
+ if (!getThreadLocals()->RecursiveGuard) {
+ ScopedRecursiveGuard SRG;
+ ScopedLock UL(BacktraceMutex);
Meta->DeallocationTrace.RecordBacktrace(Backtrace);
}
}
- markInaccessible(reinterpret_cast<void *>(SlotStart),
- State.maximumAllocationSize(), kGwpAsanGuardPageName);
+ deallocateInGuardedPool(reinterpret_cast<void *>(SlotStart),
+ State.maximumAllocationSize());
// And finally, lock again to release the slot back into the pool.
ScopedLock L(PoolMutex);
@@ -259,7 +313,7 @@ size_t GuardedPoolAllocator::getSize(const void *Ptr) {
ScopedLock L(PoolMutex);
AllocationMetadata *Meta = addrToMetadata(reinterpret_cast<uintptr_t>(Ptr));
assert(Meta->Addr == reinterpret_cast<uintptr_t>(Ptr));
- return Meta->Size;
+ return Meta->RequestedSize;
}
AllocationMetadata *GuardedPoolAllocator::addrToMetadata(uintptr_t Ptr) const {
@@ -286,7 +340,12 @@ void GuardedPoolAllocator::freeSlot(size_t SlotIndex) {
FreeSlots[FreeSlotsLength++] = SlotIndex;
}
-GWP_ASAN_TLS_INITIAL_EXEC
-GuardedPoolAllocator::ThreadLocalPackedVariables
- GuardedPoolAllocator::ThreadLocals;
+uint32_t GuardedPoolAllocator::getRandomUnsigned32() {
+ uint32_t RandomState = getThreadLocals()->RandomState;
+ RandomState ^= RandomState << 13;
+ RandomState ^= RandomState >> 17;
+ RandomState ^= RandomState << 5;
+ getThreadLocals()->RandomState = RandomState;
+ return RandomState;
+}
} // namespace gwp_asan
diff --git a/gwp_asan/guarded_pool_allocator.h b/gwp_asan/guarded_pool_allocator.h
index ae00506..6d2ce25 100644
--- a/gwp_asan/guarded_pool_allocator.h
+++ b/gwp_asan/guarded_pool_allocator.h
@@ -13,11 +13,13 @@
#include "gwp_asan/definitions.h"
#include "gwp_asan/mutex.h"
#include "gwp_asan/options.h"
-#include "gwp_asan/random.h"
-#include "gwp_asan/stack_trace_compressor.h"
+#include "gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.h" // IWYU pragma: keep
+#include "gwp_asan/platform_specific/guarded_pool_allocator_posix.h" // IWYU pragma: keep
+#include "gwp_asan/platform_specific/guarded_pool_allocator_tls.h"
#include <stddef.h>
#include <stdint.h>
+// IWYU pragma: no_include <__stddef_max_align_t.h>
namespace gwp_asan {
// This class is the primary implementation of the allocator portion of GWP-
@@ -37,7 +39,7 @@ public:
// GWP-ASan. The constructor value-initialises the class such that if no
// further initialisation takes place, calls to shouldSample() and
// pointerIsMine() will return false.
- constexpr GuardedPoolAllocator(){};
+ constexpr GuardedPoolAllocator() {}
GuardedPoolAllocator(const GuardedPoolAllocator &) = delete;
GuardedPoolAllocator &operator=(const GuardedPoolAllocator &) = delete;
@@ -78,11 +80,12 @@ public:
// class must be valid when zero-initialised, and we wish to sample as
// infrequently as possible when this is the case, hence we underflow to
// UINT32_MAX.
- if (GWP_ASAN_UNLIKELY(ThreadLocals.NextSampleCounter == 0))
- ThreadLocals.NextSampleCounter =
- (getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1;
+ if (GWP_ASAN_UNLIKELY(getThreadLocals()->NextSampleCounter == 0))
+ getThreadLocals()->NextSampleCounter =
+ ((getRandomUnsigned32() % (AdjustedSampleRatePlusOne - 1)) + 1) &
+ ThreadLocalPackedVariables::NextSampleCounterMask;
- return GWP_ASAN_UNLIKELY(--ThreadLocals.NextSampleCounter == 0);
+ return GWP_ASAN_UNLIKELY(--getThreadLocals()->NextSampleCounter == 0);
}
// Returns whether the provided pointer is a current sampled allocation that
@@ -91,10 +94,13 @@ public:
return State.pointerIsMine(Ptr);
}
- // Allocate memory in a guarded slot, and return a pointer to the new
- // allocation. Returns nullptr if the pool is empty, the requested size is too
- // large for this pool to handle, or the requested size is zero.
- void *allocate(size_t Size);
+ // Allocate memory in a guarded slot, with the specified `Alignment`. Returns
+ // nullptr if the pool is empty, if the alignnment is not a power of two, or
+ // if the size/alignment makes the allocation too large for this pool to
+ // handle. By default, uses strong alignment (i.e. `max_align_t`), see
+ // http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2293.htm for discussion of
+ // alignment issues in the standard.
+ void *allocate(size_t Size, size_t Alignment = alignof(max_align_t));
// Deallocate memory in a guarded slot. The provided pointer must have been
// allocated using this pool. This will set the guarded slot as inaccessible.
@@ -109,6 +115,18 @@ public:
// Returns a pointer to the AllocatorState region.
const AllocatorState *getAllocatorState() const { return &State; }
+ // Exposed as protected for testing.
+protected:
+ // Returns the actual allocation size required to service an allocation with
+ // the provided Size and Alignment.
+ static size_t getRequiredBackingSize(size_t Size, size_t Alignment,
+ size_t PageSize);
+
+ // Returns the provided pointer that meets the specified alignment, depending
+ // on whether it's left or right aligned.
+ static uintptr_t alignUp(uintptr_t Ptr, size_t Alignment);
+ static uintptr_t alignDown(uintptr_t Ptr, size_t Alignment);
+
private:
// Name of actively-occupied slot mappings.
static constexpr const char *kGwpAsanAliveSlotName = "GWP-ASan Alive Slot";
@@ -124,15 +142,30 @@ private:
// memory into this process in a platform-specific way. Pointer and size
// arguments are expected to be page-aligned. These functions will never
// return on error, instead electing to kill the calling process on failure.
- // Note that memory is initially mapped inaccessible. In order for RW
- // mappings, call mapMemory() followed by markReadWrite() on the returned
- // pointer. Each mapping is named on platforms that support it, primarily
- // Android. This name must be a statically allocated string, as the Android
- // kernel uses the string pointer directly.
- void *mapMemory(size_t Size, const char *Name) const;
- void unmapMemory(void *Ptr, size_t Size, const char *Name) const;
- void markReadWrite(void *Ptr, size_t Size, const char *Name) const;
- void markInaccessible(void *Ptr, size_t Size, const char *Name) const;
+ // The pool memory is initially reserved and inaccessible, and RW mappings are
+ // subsequently created and destroyed via allocateInGuardedPool() and
+ // deallocateInGuardedPool(). Each mapping is named on platforms that support
+ // it, primarily Android. This name must be a statically allocated string, as
+ // the Android kernel uses the string pointer directly.
+ void *map(size_t Size, const char *Name) const;
+ void unmap(void *Ptr, size_t Size) const;
+
+ // The pool is managed separately, as some platforms (particularly Fuchsia)
+ // manage virtual memory regions as a chunk where individual pages can still
+ // have separate permissions. These platforms maintain metadata about the
+ // region in order to perform operations. The pool is unique as it's the only
+ // thing in GWP-ASan that treats pages in a single VM region on an individual
+ // basis for page protection.
+ // The pointer returned by reserveGuardedPool() is the reserved address range
+ // of (at least) Size bytes.
+ void *reserveGuardedPool(size_t Size);
+ // allocateInGuardedPool() Ptr and Size must be a subrange of the previously
+ // reserved pool range.
+ void allocateInGuardedPool(void *Ptr, size_t Size) const;
+ // deallocateInGuardedPool() Ptr and Size must be an exact pair previously
+ // passed to allocateInGuardedPool().
+ void deallocateInGuardedPool(void *Ptr, size_t Size) const;
+ void unreserveGuardedPool();
// Get the page size from the platform-specific implementation. Only needs to
// be called once, and the result should be cached in PageSize in this class.
@@ -163,6 +196,10 @@ private:
// A mutex to protect the guarded slot and metadata pool for this class.
Mutex PoolMutex;
+ // Some unwinders can grab the libdl lock. In order to provide atfork
+ // protection, we need to ensure that we allow an unwinding thread to release
+ // the libdl lock before forking.
+ Mutex BacktraceMutex;
// Record the number allocations that we've sampled. We store this amount so
// that we don't randomly choose to recycle a slot that previously had an
// allocation before all the slots have been utilised.
@@ -191,22 +228,21 @@ private:
// the sample rate.
uint32_t AdjustedSampleRatePlusOne = 0;
- // Pack the thread local variables into a struct to ensure that they're in
- // the same cache line for performance reasons. These are the most touched
- // variables in GWP-ASan.
- struct alignas(8) ThreadLocalPackedVariables {
- constexpr ThreadLocalPackedVariables() {}
- // Thread-local decrementing counter that indicates that a given allocation
- // should be sampled when it reaches zero.
- uint32_t NextSampleCounter = 0;
- // Guard against recursivity. Unwinders often contain complex behaviour that
- // may not be safe for the allocator (i.e. the unwinder calls dlopen(),
- // which calls malloc()). When recursive behaviour is detected, we will
- // automatically fall back to the supporting allocator to supply the
- // allocation.
- bool RecursiveGuard = false;
+ // Additional platform specific data structure for the guarded pool mapping.
+ PlatformSpecificMapData GuardedPagePoolPlatformData = {};
+
+ class ScopedRecursiveGuard {
+ public:
+ ScopedRecursiveGuard() { getThreadLocals()->RecursiveGuard = true; }
+ ~ScopedRecursiveGuard() { getThreadLocals()->RecursiveGuard = false; }
};
- static GWP_ASAN_TLS_INITIAL_EXEC ThreadLocalPackedVariables ThreadLocals;
+
+ // Initialise the PRNG, platform-specific.
+ void initPRNG();
+
+ // xorshift (32-bit output), extremely fast PRNG that uses arithmetic
+ // operations only. Seeded using platform-specific mechanisms by initPRNG().
+ uint32_t getRandomUnsigned32();
};
} // namespace gwp_asan
diff --git a/gwp_asan/mutex.h b/gwp_asan/mutex.h
index c29df4c..34b91a2 100644
--- a/gwp_asan/mutex.h
+++ b/gwp_asan/mutex.h
@@ -9,14 +9,11 @@
#ifndef GWP_ASAN_MUTEX_H_
#define GWP_ASAN_MUTEX_H_
-#ifdef __unix__
-#include <pthread.h>
-#else
-#error "GWP-ASan is not supported on this platform."
-#endif
+#include "gwp_asan/platform_specific/mutex_fuchsia.h" // IWYU pragma: keep
+#include "gwp_asan/platform_specific/mutex_posix.h" // IWYU pragma: keep
namespace gwp_asan {
-class Mutex {
+class Mutex final : PlatformMutex {
public:
constexpr Mutex() = default;
~Mutex() = default;
@@ -28,11 +25,6 @@ public:
bool tryLock();
// Unlock the mutex.
void unlock();
-
-private:
-#ifdef __unix__
- pthread_mutex_t Mu = PTHREAD_MUTEX_INITIALIZER;
-#endif // defined(__unix__)
};
class ScopedLock {
diff --git a/gwp_asan/optional/backtrace.h b/gwp_asan/optional/backtrace.h
index 3a72eb3..9bb12af 100644
--- a/gwp_asan/optional/backtrace.h
+++ b/gwp_asan/optional/backtrace.h
@@ -9,21 +9,45 @@
#ifndef GWP_ASAN_OPTIONAL_BACKTRACE_H_
#define GWP_ASAN_OPTIONAL_BACKTRACE_H_
-#include "gwp_asan/optional/segv_handler.h"
+#include "gwp_asan/optional/printf.h"
#include "gwp_asan/options.h"
namespace gwp_asan {
-namespace options {
-// Functions to get the platform-specific and implementation-specific backtrace
-// and backtrace printing functions when RTGwpAsanBacktraceLibc or
-// RTGwpAsanBacktraceSanitizerCommon are linked. Use these functions to get the
-// backtrace function for populating the Options::Backtrace and
-// Options::PrintBacktrace when initialising the GuardedPoolAllocator. Please
-// note any thread-safety descriptions for the implementation of these functions
-// that you use.
-Backtrace_t getBacktraceFunction();
-crash_handler::PrintBacktrace_t getPrintBacktraceFunction();
-} // namespace options
+namespace backtrace {
+// ================================ Description ================================
+// This function shall take the backtrace provided in `TraceBuffer`, and print
+// it in a human-readable format using `Print`. Generally, this function shall
+// resolve raw pointers to section offsets and print them with the following
+// sanitizer-common format:
+// " #{frame_number} {pointer} in {function name} ({binary name}+{offset}"
+// e.g. " #5 0x420459 in _start (/tmp/uaf+0x420459)"
+// This format allows the backtrace to be symbolized offline successfully using
+// llvm-symbolizer.
+// =================================== Notes ===================================
+// This function may directly or indirectly call malloc(), as the
+// GuardedPoolAllocator contains a reentrancy barrier to prevent infinite
+// recursion. Any allocation made inside this function will be served by the
+// supporting allocator, and will not have GWP-ASan protections.
+typedef void (*PrintBacktrace_t)(uintptr_t *TraceBuffer, size_t TraceLength,
+ Printf_t Print);
+
+// Returns a function pointer to a backtrace function that's suitable for
+// unwinding through a signal handler. This is important primarily for frame-
+// pointer based unwinders, DWARF or other unwinders can simply provide the
+// normal backtrace function as the implementation here. On POSIX, SignalContext
+// should be the `ucontext_t` from the signal handler.
+typedef size_t (*SegvBacktrace_t)(uintptr_t *TraceBuffer, size_t Size,
+ void *SignalContext);
+
+// Returns platform-specific provided implementations of Backtrace_t for use
+// inside the GWP-ASan core allocator.
+options::Backtrace_t getBacktraceFunction();
+
+// Returns platform-specific provided implementations of PrintBacktrace_t and
+// SegvBacktrace_t for use in the optional SEGV handler.
+PrintBacktrace_t getPrintBacktraceFunction();
+SegvBacktrace_t getSegvBacktraceFunction();
+} // namespace backtrace
} // namespace gwp_asan
#endif // GWP_ASAN_OPTIONAL_BACKTRACE_H_
diff --git a/gwp_asan/optional/backtrace_fuchsia.cpp b/gwp_asan/optional/backtrace_fuchsia.cpp
new file mode 100644
index 0000000..09b0325
--- /dev/null
+++ b/gwp_asan/optional/backtrace_fuchsia.cpp
@@ -0,0 +1,27 @@
+//===-- backtrace_fuchsia.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/optional/backtrace.h"
+
+#include <zircon/sanitizer.h>
+
+namespace gwp_asan {
+namespace backtrace {
+
+// Fuchsia's C library provides safe, fast, best-effort backtraces itself.
+options::Backtrace_t getBacktraceFunction() {
+ return __sanitizer_fast_backtrace;
+}
+
+// These are only used in fatal signal handling, which is not used on Fuchsia.
+
+PrintBacktrace_t getPrintBacktraceFunction() { return nullptr; }
+SegvBacktrace_t getSegvBacktraceFunction() { return nullptr; }
+
+} // namespace backtrace
+} // namespace gwp_asan
diff --git a/gwp_asan/optional/backtrace_linux_libc.cpp b/gwp_asan/optional/backtrace_linux_libc.cpp
index bb0aad2..ea8e72b 100644
--- a/gwp_asan/optional/backtrace_linux_libc.cpp
+++ b/gwp_asan/optional/backtrace_linux_libc.cpp
@@ -13,7 +13,9 @@
#include <stdlib.h>
#include <string.h>
+#include "gwp_asan/definitions.h"
#include "gwp_asan/optional/backtrace.h"
+#include "gwp_asan/optional/printf.h"
#include "gwp_asan/options.h"
namespace {
@@ -23,8 +25,16 @@ size_t Backtrace(uintptr_t *TraceBuffer, size_t Size) {
return backtrace(reinterpret_cast<void **>(TraceBuffer), Size);
}
+// We don't need any custom handling for the Segv backtrace - the libc unwinder
+// has no problems with unwinding through a signal handler. Force inlining here
+// to avoid the additional frame.
+GWP_ASAN_ALWAYS_INLINE size_t SegvBacktrace(uintptr_t *TraceBuffer, size_t Size,
+ void * /*Context*/) {
+ return Backtrace(TraceBuffer, Size);
+}
+
static void PrintBacktrace(uintptr_t *Trace, size_t TraceLength,
- gwp_asan::crash_handler::Printf_t Printf) {
+ gwp_asan::Printf_t Printf) {
if (TraceLength == 0) {
Printf(" <not found (does your allocator support backtracing?)>\n\n");
return;
@@ -47,10 +57,11 @@ static void PrintBacktrace(uintptr_t *Trace, size_t TraceLength,
} // anonymous namespace
namespace gwp_asan {
-namespace options {
-Backtrace_t getBacktraceFunction() { return Backtrace; }
-crash_handler::PrintBacktrace_t getPrintBacktraceFunction() {
- return PrintBacktrace;
-}
-} // namespace options
+namespace backtrace {
+
+options::Backtrace_t getBacktraceFunction() { return Backtrace; }
+PrintBacktrace_t getPrintBacktraceFunction() { return PrintBacktrace; }
+SegvBacktrace_t getSegvBacktraceFunction() { return SegvBacktrace; }
+
+} // namespace backtrace
} // namespace gwp_asan
diff --git a/gwp_asan/optional/backtrace_sanitizer_common.cpp b/gwp_asan/optional/backtrace_sanitizer_common.cpp
index 3ac4b52..e6cce86 100644
--- a/gwp_asan/optional/backtrace_sanitizer_common.cpp
+++ b/gwp_asan/optional/backtrace_sanitizer_common.cpp
@@ -22,30 +22,47 @@ void __sanitizer::BufferedStackTrace::UnwindImpl(uptr pc, uptr bp,
void *context,
bool request_fast,
u32 max_depth) {
- if (!StackTrace::WillUseFastUnwind(request_fast)) {
- return Unwind(max_depth, pc, bp, context, 0, 0, request_fast);
- }
- Unwind(max_depth, pc, 0, context, 0, 0, false);
+ if (!StackTrace::WillUseFastUnwind(request_fast))
+ return Unwind(max_depth, pc, 0, context, 0, 0, false);
+
+ uptr top = 0;
+ uptr bottom = 0;
+ GetThreadStackTopAndBottom(/*at_initialization*/ false, &top, &bottom);
+
+ return Unwind(max_depth, pc, bp, context, top, bottom, request_fast);
}
namespace {
-size_t Backtrace(uintptr_t *TraceBuffer, size_t Size) {
+size_t BacktraceCommon(uintptr_t *TraceBuffer, size_t Size, void *Context) {
+ // Use the slow sanitizer unwinder in the segv handler. Fast frame pointer
+ // unwinders can end up dropping frames because the kernel sigreturn() frame's
+ // return address is the return address at time of fault. This has the result
+ // of never actually capturing the PC where the signal was raised.
+ bool UseFastUnwind = (Context == nullptr);
+
__sanitizer::BufferedStackTrace Trace;
Trace.Reset();
if (Size > __sanitizer::kStackTraceMax)
Size = __sanitizer::kStackTraceMax;
Trace.Unwind((__sanitizer::uptr)__builtin_return_address(0),
- (__sanitizer::uptr)__builtin_frame_address(0),
- /* ucontext */ nullptr,
- /* fast unwind */ true, Size - 1);
+ (__sanitizer::uptr)__builtin_frame_address(0), Context,
+ UseFastUnwind, Size - 1);
memcpy(TraceBuffer, Trace.trace, Trace.size * sizeof(uintptr_t));
return Trace.size;
}
+size_t Backtrace(uintptr_t *TraceBuffer, size_t Size) {
+ return BacktraceCommon(TraceBuffer, Size, nullptr);
+}
+
+size_t SegvBacktrace(uintptr_t *TraceBuffer, size_t Size, void *Context) {
+ return BacktraceCommon(TraceBuffer, Size, Context);
+}
+
static void PrintBacktrace(uintptr_t *Trace, size_t TraceLength,
- gwp_asan::crash_handler::Printf_t Printf) {
+ gwp_asan::Printf_t Printf) {
__sanitizer::StackTrace StackTrace;
StackTrace.trace = reinterpret_cast<__sanitizer::uptr *>(Trace);
StackTrace.size = TraceLength;
@@ -60,21 +77,23 @@ static void PrintBacktrace(uintptr_t *Trace, size_t TraceLength,
} // anonymous namespace
namespace gwp_asan {
-namespace options {
+namespace backtrace {
+
// This function is thread-compatible. It must be synchronised in respect to any
// other calls to getBacktraceFunction(), calls to getPrintBacktraceFunction(),
// and calls to either of the functions that they return. Furthermore, this may
// require synchronisation with any calls to sanitizer_common that use flags.
// Generally, this function will be called during the initialisation of the
// allocator, which is done in a thread-compatible manner.
-Backtrace_t getBacktraceFunction() {
+options::Backtrace_t getBacktraceFunction() {
// The unwinder requires the default flags to be set.
__sanitizer::SetCommonFlagsDefaults();
__sanitizer::InitializeCommonFlags();
return Backtrace;
}
-crash_handler::PrintBacktrace_t getPrintBacktraceFunction() {
- return PrintBacktrace;
-}
-} // namespace options
+
+PrintBacktrace_t getPrintBacktraceFunction() { return PrintBacktrace; }
+SegvBacktrace_t getSegvBacktraceFunction() { return SegvBacktrace; }
+
+} // namespace backtrace
} // namespace gwp_asan
diff --git a/gwp_asan/optional/options_parser.cpp b/gwp_asan/optional/options_parser.cpp
index 2e63862..6023412 100644
--- a/gwp_asan/optional/options_parser.cpp
+++ b/gwp_asan/optional/options_parser.cpp
@@ -7,84 +7,251 @@
//===----------------------------------------------------------------------===//
#include "gwp_asan/optional/options_parser.h"
+#include "gwp_asan/optional/printf.h"
+#include "gwp_asan/utilities.h"
+#include <assert.h>
#include <stdarg.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
-#include "gwp_asan/options.h"
-#include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_flag_parser.h"
-#include "sanitizer_common/sanitizer_flags.h"
-
-namespace gwp_asan {
-namespace options {
namespace {
-void registerGwpAsanFlags(__sanitizer::FlagParser *parser, Options *o) {
-#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description) \
- RegisterFlag(parser, #Name, Description, &o->Name);
+enum class OptionType : uint8_t {
+ OT_bool,
+ OT_int,
+};
+
+#define InvokeIfNonNull(Printf, ...) \
+ do { \
+ if (Printf) \
+ Printf(__VA_ARGS__); \
+ } while (0);
+
+class OptionParser {
+public:
+ explicit OptionParser(gwp_asan::Printf_t PrintfForWarnings)
+ : Printf(PrintfForWarnings) {}
+ void registerOption(const char *Name, const char *Desc, OptionType Type,
+ void *Var);
+ void parseString(const char *S);
+ void printOptionDescriptions();
+
+private:
+ // Calculate at compile-time how many options are available.
+#define GWP_ASAN_OPTION(...) +1
+ static constexpr size_t MaxOptions = 0
#include "gwp_asan/options.inc"
+ ;
#undef GWP_ASAN_OPTION
+
+ struct Option {
+ const char *Name;
+ const char *Desc;
+ OptionType Type;
+ void *Var;
+ } Options[MaxOptions];
+
+ size_t NumberOfOptions = 0;
+ const char *Buffer = nullptr;
+ uintptr_t Pos = 0;
+ gwp_asan::Printf_t Printf = nullptr;
+
+ void skipWhitespace();
+ void parseOptions();
+ bool parseOption();
+ bool setOptionToValue(const char *Name, const char *Value);
+};
+
+void OptionParser::printOptionDescriptions() {
+ InvokeIfNonNull(Printf, "GWP-ASan: Available options:\n");
+ for (size_t I = 0; I < NumberOfOptions; ++I)
+ InvokeIfNonNull(Printf, "\t%s\n\t\t- %s\n", Options[I].Name,
+ Options[I].Desc);
+}
+
+bool isSeparator(char C) {
+ return C == ' ' || C == ',' || C == ':' || C == '\n' || C == '\t' ||
+ C == '\r';
+}
+
+bool isSeparatorOrNull(char C) { return !C || isSeparator(C); }
+
+void OptionParser::skipWhitespace() {
+ while (isSeparator(Buffer[Pos]))
+ ++Pos;
+}
+
+bool OptionParser::parseOption() {
+ const uintptr_t NameStart = Pos;
+ while (Buffer[Pos] != '=' && !isSeparatorOrNull(Buffer[Pos]))
+ ++Pos;
+
+ const char *Name = Buffer + NameStart;
+ if (Buffer[Pos] != '=') {
+ InvokeIfNonNull(Printf, "GWP-ASan: Expected '=' when parsing option '%s'.",
+ Name);
+ return false;
+ }
+ const uintptr_t ValueStart = ++Pos;
+ const char *Value;
+ if (Buffer[Pos] == '\'' || Buffer[Pos] == '"') {
+ const char Quote = Buffer[Pos++];
+ while (Buffer[Pos] != 0 && Buffer[Pos] != Quote)
+ ++Pos;
+ if (Buffer[Pos] == 0) {
+ InvokeIfNonNull(Printf, "GWP-ASan: Unterminated string in option '%s'.",
+ Name);
+ return false;
+ }
+ Value = Buffer + ValueStart + 1;
+ ++Pos; // consume the closing quote
+ } else {
+ while (!isSeparatorOrNull(Buffer[Pos]))
+ ++Pos;
+ Value = Buffer + ValueStart;
+ }
+
+ return setOptionToValue(Name, Value);
}
-const char *getCompileDefinitionGwpAsanDefaultOptions() {
-#ifdef GWP_ASAN_DEFAULT_OPTIONS
- return SANITIZER_STRINGIFY(GWP_ASAN_DEFAULT_OPTIONS);
-#else
- return "";
-#endif
+void OptionParser::parseOptions() {
+ while (true) {
+ skipWhitespace();
+ if (Buffer[Pos] == 0)
+ break;
+ if (!parseOption()) {
+ InvokeIfNonNull(Printf, "GWP-ASan: Options parsing failed.\n");
+ return;
+ }
+ }
+}
+
+void OptionParser::parseString(const char *S) {
+ if (!S)
+ return;
+ Buffer = S;
+ Pos = 0;
+ parseOptions();
+}
+
+bool parseBool(const char *Value, bool *b) {
+ if (strncmp(Value, "0", 1) == 0 || strncmp(Value, "no", 2) == 0 ||
+ strncmp(Value, "false", 5) == 0) {
+ *b = false;
+ return true;
+ }
+ if (strncmp(Value, "1", 1) == 0 || strncmp(Value, "yes", 3) == 0 ||
+ strncmp(Value, "true", 4) == 0) {
+ *b = true;
+ return true;
+ }
+ return false;
+}
+
+bool OptionParser::setOptionToValue(const char *Name, const char *Value) {
+ for (size_t I = 0; I < NumberOfOptions; ++I) {
+ const uintptr_t Len = strlen(Options[I].Name);
+ if (strncmp(Name, Options[I].Name, Len) != 0 || Name[Len] != '=')
+ continue;
+ bool Ok = false;
+ switch (Options[I].Type) {
+ case OptionType::OT_bool:
+ Ok = parseBool(Value, reinterpret_cast<bool *>(Options[I].Var));
+ if (!Ok)
+ InvokeIfNonNull(
+ Printf, "GWP-ASan: Invalid boolean value '%s' for option '%s'.\n",
+ Value, Options[I].Name);
+ break;
+ case OptionType::OT_int:
+ char *ValueEnd;
+ *reinterpret_cast<int *>(Options[I].Var) =
+ static_cast<int>(strtol(Value, &ValueEnd, 10));
+ Ok =
+ *ValueEnd == '"' || *ValueEnd == '\'' || isSeparatorOrNull(*ValueEnd);
+ if (!Ok)
+ InvokeIfNonNull(
+ Printf, "GWP-ASan: Invalid integer value '%s' for option '%s'.\n",
+ Value, Options[I].Name);
+ break;
+ }
+ return Ok;
+ }
+
+ InvokeIfNonNull(Printf, "GWP-ASan: Unknown option '%s'.", Name);
+ return true;
+}
+
+void OptionParser::registerOption(const char *Name, const char *Desc,
+ OptionType Type, void *Var) {
+ assert(NumberOfOptions < MaxOptions &&
+ "GWP-ASan Error: Ran out of space for options.\n");
+ Options[NumberOfOptions].Name = Name;
+ Options[NumberOfOptions].Desc = Desc;
+ Options[NumberOfOptions].Type = Type;
+ Options[NumberOfOptions].Var = Var;
+ ++NumberOfOptions;
+}
+
+void registerGwpAsanOptions(OptionParser *parser,
+ gwp_asan::options::Options *o) {
+#define GWP_ASAN_OPTION(Type, Name, DefaultValue, Description) \
+ parser->registerOption(#Name, Description, OptionType::OT_##Type, &o->Name);
+#include "gwp_asan/options.inc"
+#undef GWP_ASAN_OPTION
}
const char *getGwpAsanDefaultOptions() {
return (__gwp_asan_default_options) ? __gwp_asan_default_options() : "";
}
-Options *getOptionsInternal() {
- static Options GwpAsanFlags;
- return &GwpAsanFlags;
+gwp_asan::options::Options *getOptionsInternal() {
+ static gwp_asan::options::Options GwpAsanOptions;
+ return &GwpAsanOptions;
}
} // anonymous namespace
-void initOptions() {
- __sanitizer::SetCommonFlagsDefaults();
+namespace gwp_asan {
+namespace options {
+void initOptions(const char *OptionsStr, Printf_t PrintfForWarnings) {
Options *o = getOptionsInternal();
o->setDefaults();
- __sanitizer::FlagParser Parser;
- registerGwpAsanFlags(&Parser, o);
-
- // Override from compile definition.
- Parser.ParseString(getCompileDefinitionGwpAsanDefaultOptions());
+ OptionParser Parser(PrintfForWarnings);
+ registerGwpAsanOptions(&Parser, o);
- // Override from user-specified string.
- Parser.ParseString(getGwpAsanDefaultOptions());
+ // Override from the weak function definition in this executable.
+ Parser.parseString(getGwpAsanDefaultOptions());
- // Override from environment.
- Parser.ParseString(__sanitizer::GetEnv("GWP_ASAN_OPTIONS"));
+ // Override from the provided options string.
+ Parser.parseString(OptionsStr);
- __sanitizer::InitializeCommonFlags();
- if (__sanitizer::Verbosity())
- __sanitizer::ReportUnrecognizedFlags();
+ if (o->help)
+ Parser.printOptionDescriptions();
if (!o->Enabled)
return;
- // Sanity checks for the parameters.
if (o->MaxSimultaneousAllocations <= 0) {
- __sanitizer::Printf("GWP-ASan ERROR: MaxSimultaneousAllocations must be > "
- "0 when GWP-ASan is enabled.\n");
- exit(EXIT_FAILURE);
+ InvokeIfNonNull(
+ PrintfForWarnings,
+ "GWP-ASan ERROR: MaxSimultaneousAllocations must be > 0 when GWP-ASan "
+ "is enabled.\n");
+ o->Enabled = false;
}
-
- if (o->SampleRate < 1) {
- __sanitizer::Printf(
+ if (o->SampleRate <= 0) {
+ InvokeIfNonNull(
+ PrintfForWarnings,
"GWP-ASan ERROR: SampleRate must be > 0 when GWP-ASan is enabled.\n");
- exit(EXIT_FAILURE);
+ o->Enabled = false;
}
}
+void initOptions(Printf_t PrintfForWarnings) {
+ initOptions(getenv("GWP_ASAN_OPTIONS"), PrintfForWarnings);
+}
+
Options &getOptions() { return *getOptionsInternal(); }
} // namespace options
diff --git a/gwp_asan/optional/options_parser.h b/gwp_asan/optional/options_parser.h
index 7a6bfaf..a5a0628 100644
--- a/gwp_asan/optional/options_parser.h
+++ b/gwp_asan/optional/options_parser.h
@@ -9,14 +9,15 @@
#ifndef GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_
#define GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_
-#include "gwp_asan/optional/backtrace.h"
+#include "gwp_asan/optional/printf.h"
#include "gwp_asan/options.h"
-#include "sanitizer_common/sanitizer_common.h"
namespace gwp_asan {
namespace options {
-// Parse the options from the GWP_ASAN_FLAGS environment variable.
-void initOptions();
+// Parse the options from the GWP_ASAN_OPTIONS environment variable.
+void initOptions(Printf_t PrintfForWarnings = nullptr);
+// Parse the options from the provided string.
+void initOptions(const char *OptionsStr, Printf_t PrintfForWarnings = nullptr);
// Returns the initialised options. Call initOptions() prior to calling this
// function.
Options &getOptions();
@@ -24,8 +25,7 @@ Options &getOptions();
} // namespace gwp_asan
extern "C" {
-SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE const char *
-__gwp_asan_default_options();
+__attribute__((weak)) const char *__gwp_asan_default_options();
}
#endif // GWP_ASAN_OPTIONAL_OPTIONS_PARSER_H_
diff --git a/gwp_asan/optional/printf.h b/gwp_asan/optional/printf.h
new file mode 100644
index 0000000..1004a2c
--- /dev/null
+++ b/gwp_asan/optional/printf.h
@@ -0,0 +1,33 @@
+//===-- printf.h ------------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef GWP_ASAN_OPTIONAL_PRINTF_H_
+#define GWP_ASAN_OPTIONAL_PRINTF_H_
+
+namespace gwp_asan {
+
+// ================================ Requirements ===============================
+// This function is required to be provided by the supporting allocator iff the
+// allocator wants to use any of the optional components.
+// ================================ Description ================================
+// This function shall produce output according to a strict subset of the C
+// standard library's printf() family. This function must support printing the
+// following formats:
+// 1. integers: "%([0-9]*)?(z|ll)?{d,u,x,X}"
+// 2. pointers: "%p"
+// 3. strings: "%[-]([0-9]*)?(\\.\\*)?s"
+// 4. chars: "%c"
+// This function must be implemented in a signal-safe manner, and thus must not
+// malloc().
+// =================================== Notes ===================================
+// This function has a slightly different signature than the C standard
+// library's printf(). Notably, it returns 'void' rather than 'int'.
+typedef void (*Printf_t)(const char *Format, ...);
+
+} // namespace gwp_asan
+#endif // GWP_ASAN_OPTIONAL_PRINTF_H_
diff --git a/gwp_asan/optional/segv_handler.h b/gwp_asan/optional/segv_handler.h
index 10af150..87d9fe1 100644
--- a/gwp_asan/optional/segv_handler.h
+++ b/gwp_asan/optional/segv_handler.h
@@ -1,4 +1,4 @@
-//===-- crash_handler.h -----------------------------------------*- C++ -*-===//
+//===-- segv_handler.h ------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -6,59 +6,15 @@
//
//===----------------------------------------------------------------------===//
-#ifndef GWP_ASAN_OPTIONAL_CRASH_HANDLER_H_
-#define GWP_ASAN_OPTIONAL_CRASH_HANDLER_H_
+#ifndef GWP_ASAN_OPTIONAL_SEGV_HANDLER_H_
+#define GWP_ASAN_OPTIONAL_SEGV_HANDLER_H_
#include "gwp_asan/guarded_pool_allocator.h"
-#include "gwp_asan/options.h"
+#include "gwp_asan/optional/backtrace.h"
+#include "gwp_asan/optional/printf.h"
namespace gwp_asan {
-namespace crash_handler {
-// ================================ Requirements ===============================
-// This function must be provided by the supporting allocator only when this
-// provided crash handler is used to dump the generic report.
-// sanitizer::Printf() function can be simply used here.
-// ================================ Description ================================
-// This function shall produce output according to a strict subset of the C
-// standard library's printf() family. This function must support printing the
-// following formats:
-// 1. integers: "%([0-9]*)?(z|ll)?{d,u,x,X}"
-// 2. pointers: "%p"
-// 3. strings: "%[-]([0-9]*)?(\\.\\*)?s"
-// 4. chars: "%c"
-// This function must be implemented in a signal-safe manner, and thus must not
-// malloc().
-// =================================== Notes ===================================
-// This function has a slightly different signature than the C standard
-// library's printf(). Notably, it returns 'void' rather than 'int'.
-typedef void (*Printf_t)(const char *Format, ...);
-
-// ================================ Requirements ===============================
-// This function is required for the supporting allocator, but one of the three
-// provided implementations may be used (RTGwpAsanBacktraceLibc,
-// RTGwpAsanBacktraceSanitizerCommon, or BasicPrintBacktraceFunction).
-// ================================ Description ================================
-// This function shall take the backtrace provided in `TraceBuffer`, and print
-// it in a human-readable format using `Print`. Generally, this function shall
-// resolve raw pointers to section offsets and print them with the following
-// sanitizer-common format:
-// " #{frame_number} {pointer} in {function name} ({binary name}+{offset}"
-// e.g. " #5 0x420459 in _start (/tmp/uaf+0x420459)"
-// This format allows the backtrace to be symbolized offline successfully using
-// llvm-symbolizer.
-// =================================== Notes ===================================
-// This function may directly or indirectly call malloc(), as the
-// GuardedPoolAllocator contains a reentrancy barrier to prevent infinite
-// recursion. Any allocation made inside this function will be served by the
-// supporting allocator, and will not have GWP-ASan protections.
-typedef void (*PrintBacktrace_t)(uintptr_t *TraceBuffer, size_t TraceLength,
- Printf_t Print);
-
-// Returns a function pointer to a basic PrintBacktrace implementation. This
-// implementation simply prints the stack trace in a human readable fashion
-// without any symbolization.
-PrintBacktrace_t getBasicPrintBacktraceFunction();
-
+namespace segv_handler {
// Install the SIGSEGV crash handler for printing use-after-free and heap-
// buffer-{under|over}flow exceptions if the user asked for it. This is platform
// specific as even though POSIX and Windows both support registering handlers
@@ -66,16 +22,12 @@ PrintBacktrace_t getBasicPrintBacktraceFunction();
// the address that caused the SIGSEGV exception. GPA->init() must be called
// before this function.
void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf,
- PrintBacktrace_t PrintBacktrace,
- options::Backtrace_t Backtrace);
+ gwp_asan::backtrace::PrintBacktrace_t PrintBacktrace,
+ gwp_asan::backtrace::SegvBacktrace_t SegvBacktrace);
+// Uninistall the signal handlers, test-only.
void uninstallSignalHandlers();
-
-void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
- const gwp_asan::AllocationMetadata *Metadata,
- options::Backtrace_t Backtrace, Printf_t Printf,
- PrintBacktrace_t PrintBacktrace);
-} // namespace crash_handler
+} // namespace segv_handler
} // namespace gwp_asan
-#endif // GWP_ASAN_OPTIONAL_CRASH_HANDLER_H_
+#endif // GWP_ASAN_OPTIONAL_SEGV_HANDLER_H_
diff --git a/gwp_asan/optional/segv_handler_fuchsia.cpp b/gwp_asan/optional/segv_handler_fuchsia.cpp
new file mode 100644
index 0000000..966d7d0
--- /dev/null
+++ b/gwp_asan/optional/segv_handler_fuchsia.cpp
@@ -0,0 +1,22 @@
+//===-- segv_handler_fuchsia.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/optional/segv_handler.h"
+
+// GWP-ASan on Fuchsia doesn't currently support signal handlers.
+
+namespace gwp_asan {
+namespace segv_handler {
+void installSignalHandlers(gwp_asan::GuardedPoolAllocator * /* GPA */,
+ Printf_t /* Printf */,
+ backtrace::PrintBacktrace_t /* PrintBacktrace */,
+ backtrace::SegvBacktrace_t /* SegvBacktrace */) {}
+
+void uninstallSignalHandlers() {}
+} // namespace segv_handler
+} // namespace gwp_asan
diff --git a/gwp_asan/optional/segv_handler_posix.cpp b/gwp_asan/optional/segv_handler_posix.cpp
index 22589b8..5c9bb9f 100644
--- a/gwp_asan/optional/segv_handler_posix.cpp
+++ b/gwp_asan/optional/segv_handler_posix.cpp
@@ -1,4 +1,4 @@
-//===-- crash_handler_posix.cpp ---------------------------------*- C++ -*-===//
+//===-- segv_handler_posix.cpp ----------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -12,62 +12,30 @@
#include "gwp_asan/optional/segv_handler.h"
#include "gwp_asan/options.h"
+// RHEL creates the PRIu64 format macro (for printing uint64_t's) only when this
+// macro is defined before including <inttypes.h>.
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS 1
+#endif
+
#include <assert.h>
#include <inttypes.h>
#include <signal.h>
#include <stdio.h>
-namespace {
using gwp_asan::AllocationMetadata;
using gwp_asan::Error;
using gwp_asan::GuardedPoolAllocator;
-using gwp_asan::crash_handler::PrintBacktrace_t;
-using gwp_asan::crash_handler::Printf_t;
-using gwp_asan::options::Backtrace_t;
-
-struct sigaction PreviousHandler;
-bool SignalHandlerInstalled;
-gwp_asan::GuardedPoolAllocator *GPAForSignalHandler;
-Printf_t PrintfForSignalHandler;
-PrintBacktrace_t PrintBacktraceForSignalHandler;
-Backtrace_t BacktraceForSignalHandler;
+using gwp_asan::Printf_t;
+using gwp_asan::backtrace::PrintBacktrace_t;
+using gwp_asan::backtrace::SegvBacktrace_t;
-static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
- if (GPAForSignalHandler) {
- GPAForSignalHandler->stop();
-
- gwp_asan::crash_handler::dumpReport(
- reinterpret_cast<uintptr_t>(info->si_addr),
- GPAForSignalHandler->getAllocatorState(),
- GPAForSignalHandler->getMetadataRegion(), BacktraceForSignalHandler,
- PrintfForSignalHandler, PrintBacktraceForSignalHandler);
- }
-
- // Process any previous handlers.
- if (PreviousHandler.sa_flags & SA_SIGINFO) {
- PreviousHandler.sa_sigaction(sig, info, ucontext);
- } else if (PreviousHandler.sa_handler == SIG_DFL) {
- // If the previous handler was the default handler, cause a core dump.
- signal(SIGSEGV, SIG_DFL);
- raise(SIGSEGV);
- } else if (PreviousHandler.sa_handler == SIG_IGN) {
- // If the previous segv handler was SIGIGN, crash iff we were responsible
- // for the crash.
- if (__gwp_asan_error_is_mine(GPAForSignalHandler->getAllocatorState(),
- reinterpret_cast<uintptr_t>(info->si_addr))) {
- signal(SIGSEGV, SIG_DFL);
- raise(SIGSEGV);
- }
- } else {
- PreviousHandler.sa_handler(sig);
- }
-}
+namespace {
struct ScopedEndOfReportDecorator {
- ScopedEndOfReportDecorator(gwp_asan::crash_handler::Printf_t Printf)
- : Printf(Printf) {}
+ ScopedEndOfReportDecorator(gwp_asan::Printf_t Printf) : Printf(Printf) {}
~ScopedEndOfReportDecorator() { Printf("*** End GWP-ASan report ***\n"); }
- gwp_asan::crash_handler::Printf_t Printf;
+ gwp_asan::Printf_t Printf;
};
// Prints the provided error and metadata information.
@@ -117,51 +85,10 @@ void printHeader(Error E, uintptr_t AccessPtr,
AccessPtr, DescriptionBuffer, ThreadBuffer);
}
-void defaultPrintStackTrace(uintptr_t *Trace, size_t TraceLength,
- gwp_asan::crash_handler::Printf_t Printf) {
- if (TraceLength == 0)
- Printf(" <unknown (does your allocator support backtracing?)>\n");
-
- for (size_t i = 0; i < TraceLength; ++i) {
- Printf(" #%zu 0x%zx in <unknown>\n", i, Trace[i]);
- }
- Printf("\n");
-}
-
-} // anonymous namespace
-
-namespace gwp_asan {
-namespace crash_handler {
-PrintBacktrace_t getBasicPrintBacktraceFunction() {
- return defaultPrintStackTrace;
-}
-
-void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf,
- PrintBacktrace_t PrintBacktrace,
- options::Backtrace_t Backtrace) {
- GPAForSignalHandler = GPA;
- PrintfForSignalHandler = Printf;
- PrintBacktraceForSignalHandler = PrintBacktrace;
- BacktraceForSignalHandler = Backtrace;
-
- struct sigaction Action;
- Action.sa_sigaction = sigSegvHandler;
- Action.sa_flags = SA_SIGINFO;
- sigaction(SIGSEGV, &Action, &PreviousHandler);
- SignalHandlerInstalled = true;
-}
-
-void uninstallSignalHandlers() {
- if (SignalHandlerInstalled) {
- sigaction(SIGSEGV, &PreviousHandler, nullptr);
- SignalHandlerInstalled = false;
- }
-}
-
void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
const gwp_asan::AllocationMetadata *Metadata,
- options::Backtrace_t Backtrace, Printf_t Printf,
- PrintBacktrace_t PrintBacktrace) {
+ SegvBacktrace_t SegvBacktrace, Printf_t Printf,
+ PrintBacktrace_t PrintBacktrace, void *Context) {
assert(State && "dumpReport missing Allocator State.");
assert(Metadata && "dumpReport missing Metadata.");
assert(Printf && "dumpReport missing Printf.");
@@ -194,7 +121,8 @@ void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
// Print the fault backtrace.
static constexpr unsigned kMaximumStackFramesForCrashTrace = 512;
uintptr_t Trace[kMaximumStackFramesForCrashTrace];
- size_t TraceLength = Backtrace(Trace, kMaximumStackFramesForCrashTrace);
+ size_t TraceLength =
+ SegvBacktrace(Trace, kMaximumStackFramesForCrashTrace, Context);
PrintBacktrace(Trace, TraceLength, Printf);
@@ -204,7 +132,7 @@ void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
// Maybe print the deallocation trace.
if (__gwp_asan_is_deallocated(AllocMeta)) {
uint64_t ThreadID = __gwp_asan_get_deallocation_thread_id(AllocMeta);
- if (ThreadID == kInvalidThreadID)
+ if (ThreadID == gwp_asan::kInvalidThreadID)
Printf("0x%zx was deallocated by thread <unknown> here:\n", ErrorPtr);
else
Printf("0x%zx was deallocated by thread %zu here:\n", ErrorPtr, ThreadID);
@@ -215,7 +143,7 @@ void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
// Print the allocation trace.
uint64_t ThreadID = __gwp_asan_get_allocation_thread_id(AllocMeta);
- if (ThreadID == kInvalidThreadID)
+ if (ThreadID == gwp_asan::kInvalidThreadID)
Printf("0x%zx was allocated by thread <unknown> here:\n", ErrorPtr);
else
Printf("0x%zx was allocated by thread %zu here:\n", ErrorPtr, ThreadID);
@@ -223,5 +151,75 @@ void dumpReport(uintptr_t ErrorPtr, const gwp_asan::AllocatorState *State,
AllocMeta, Trace, kMaximumStackFramesForCrashTrace);
PrintBacktrace(Trace, TraceLength, Printf);
}
-} // namespace crash_handler
+
+struct sigaction PreviousHandler;
+bool SignalHandlerInstalled;
+gwp_asan::GuardedPoolAllocator *GPAForSignalHandler;
+Printf_t PrintfForSignalHandler;
+PrintBacktrace_t PrintBacktraceForSignalHandler;
+SegvBacktrace_t BacktraceForSignalHandler;
+
+static void sigSegvHandler(int sig, siginfo_t *info, void *ucontext) {
+ if (GPAForSignalHandler) {
+ GPAForSignalHandler->stop();
+
+ dumpReport(reinterpret_cast<uintptr_t>(info->si_addr),
+ GPAForSignalHandler->getAllocatorState(),
+ GPAForSignalHandler->getMetadataRegion(),
+ BacktraceForSignalHandler, PrintfForSignalHandler,
+ PrintBacktraceForSignalHandler, ucontext);
+ }
+
+ // Process any previous handlers.
+ if (PreviousHandler.sa_flags & SA_SIGINFO) {
+ PreviousHandler.sa_sigaction(sig, info, ucontext);
+ } else if (PreviousHandler.sa_handler == SIG_DFL) {
+ // If the previous handler was the default handler, cause a core dump.
+ signal(SIGSEGV, SIG_DFL);
+ raise(SIGSEGV);
+ } else if (PreviousHandler.sa_handler == SIG_IGN) {
+ // If the previous segv handler was SIGIGN, crash iff we were responsible
+ // for the crash.
+ if (__gwp_asan_error_is_mine(GPAForSignalHandler->getAllocatorState(),
+ reinterpret_cast<uintptr_t>(info->si_addr))) {
+ signal(SIGSEGV, SIG_DFL);
+ raise(SIGSEGV);
+ }
+ } else {
+ PreviousHandler.sa_handler(sig);
+ }
+}
+} // anonymous namespace
+
+namespace gwp_asan {
+namespace segv_handler {
+
+void installSignalHandlers(gwp_asan::GuardedPoolAllocator *GPA, Printf_t Printf,
+ PrintBacktrace_t PrintBacktrace,
+ SegvBacktrace_t SegvBacktrace) {
+ assert(GPA && "GPA wasn't provided to installSignalHandlers.");
+ assert(Printf && "Printf wasn't provided to installSignalHandlers.");
+ assert(PrintBacktrace &&
+ "PrintBacktrace wasn't provided to installSignalHandlers.");
+ assert(SegvBacktrace &&
+ "SegvBacktrace wasn't provided to installSignalHandlers.");
+ GPAForSignalHandler = GPA;
+ PrintfForSignalHandler = Printf;
+ PrintBacktraceForSignalHandler = PrintBacktrace;
+ BacktraceForSignalHandler = SegvBacktrace;
+
+ struct sigaction Action = {};
+ Action.sa_sigaction = sigSegvHandler;
+ Action.sa_flags = SA_SIGINFO;
+ sigaction(SIGSEGV, &Action, &PreviousHandler);
+ SignalHandlerInstalled = true;
+}
+
+void uninstallSignalHandlers() {
+ if (SignalHandlerInstalled) {
+ sigaction(SIGSEGV, &PreviousHandler, nullptr);
+ SignalHandlerInstalled = false;
+ }
+}
+} // namespace segv_handler
} // namespace gwp_asan
diff --git a/gwp_asan/options.inc b/gwp_asan/options.inc
index 6cdddfb..9900a2a 100644
--- a/gwp_asan/options.inc
+++ b/gwp_asan/options.inc
@@ -10,17 +10,18 @@
#error "Define GWP_ASAN_OPTION prior to including this file!"
#endif
-GWP_ASAN_OPTION(bool, Enabled, true, "Is GWP-ASan enabled? Defaults to true.")
+#ifndef GWP_ASAN_DEFAULT_ENABLED
+#define GWP_ASAN_DEFAULT_ENABLED true
+#endif
-GWP_ASAN_OPTION(
- bool, PerfectlyRightAlign, false,
- "When allocations are right-aligned, should we perfectly align them up to "
- "the page boundary? By default (false), we round up allocation size to the "
- "nearest power of two (1, 2, 4, 8, 16) up to a maximum of 16-byte "
- "alignment for performance reasons. For Bionic, we use 8-byte alignment by "
- "default. Setting this to true can find single byte buffer-overflows for "
- "multibyte allocations at the cost of performance, and may be incompatible "
- "with some architectures.")
+#ifndef GWP_ASAN_STRINGIFY
+#define GWP_ASAN_STRINGIFY(S) GWP_ASAN_STRINGIFY_(S)
+#define GWP_ASAN_STRINGIFY_(S) #S
+#endif
+
+GWP_ASAN_OPTION(bool, Enabled, GWP_ASAN_DEFAULT_ENABLED,
+ "Is GWP-ASan enabled? Defaults to " GWP_ASAN_STRINGIFY(
+ GWP_ASAN_DEFAULT_ENABLED) ".")
GWP_ASAN_OPTION(int, MaxSimultaneousAllocations, 16,
"Number of simultaneously-guarded allocations available in the "
@@ -29,7 +30,7 @@ GWP_ASAN_OPTION(int, MaxSimultaneousAllocations, 16,
GWP_ASAN_OPTION(int, SampleRate, 5000,
"The probability (1 / SampleRate) that an allocation is "
"selected for GWP-ASan sampling. Default is 5000. Sample rates "
- "up to (2^31 - 1) are supported.")
+ "up to (2^30 - 1) are supported.")
// Developer note - This option is not actually processed by GWP-ASan itself. It
// is included here so that a user can specify whether they want signal handlers
@@ -51,3 +52,18 @@ GWP_ASAN_OPTION(
GWP_ASAN_OPTION(bool, InstallForkHandlers, true,
"Install GWP-ASan atfork handlers to acquire internal locks "
"before fork and release them after.")
+
+GWP_ASAN_OPTION(bool, help, false, "Print a summary of the available options.")
+
+// =============================================================================
+// ==== WARNING
+// =============================================================================
+// If you are adding flags to GWP-ASan, please note that GWP-ASan flag strings
+// may be parsed by trusted system components (on Android, GWP-ASan flag strings
+// are parsed by libc during the dynamic loader). This means that GWP-ASan
+// should never feature flags like log paths on disk, because this can lead to
+// arbitrary file write and thus privilege escalation. For an example, see the
+// setuid ASan log_path exploits: https://www.exploit-db.com/exploits/46241.
+//
+// Please place all new flags above this warning, so that the warning always
+// stays at the bottom.
diff --git a/gwp_asan/platform_specific/common_fuchsia.cpp b/gwp_asan/platform_specific/common_fuchsia.cpp
new file mode 100644
index 0000000..b469ef8
--- /dev/null
+++ b/gwp_asan/platform_specific/common_fuchsia.cpp
@@ -0,0 +1,15 @@
+//===-- common_fuchsia.cpp --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/common.h"
+
+namespace gwp_asan {
+// This is only used for AllocationTrace.ThreadID and allocation traces are not
+// yet supported on Fuchsia.
+uint64_t getThreadID() { return kInvalidThreadID; }
+} // namespace gwp_asan
diff --git a/gwp_asan/platform_specific/common_posix.cpp b/gwp_asan/platform_specific/common_posix.cpp
index e44e629..0637fc2 100644
--- a/gwp_asan/platform_specific/common_posix.cpp
+++ b/gwp_asan/platform_specific/common_posix.cpp
@@ -1,4 +1,4 @@
-//===-- common_posix.cpp ---------------------------------*- C++ -*-===//
+//===-- common_posix.cpp ----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -8,7 +8,9 @@
#include "gwp_asan/common.h"
-#include <sys/syscall.h>
+#include <stdint.h>
+#include <sys/syscall.h> // IWYU pragma: keep
+// IWYU pragma: no_include <syscall.h>
#include <unistd.h>
namespace gwp_asan {
diff --git a/gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.cpp b/gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.cpp
new file mode 100644
index 0000000..ca5231a
--- /dev/null
+++ b/gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.cpp
@@ -0,0 +1,104 @@
+//===-- guarded_pool_allocator_fuchsia.cpp ----------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/guarded_pool_allocator.h"
+#include "gwp_asan/utilities.h"
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+#include <zircon/process.h>
+#include <zircon/syscalls.h>
+
+namespace gwp_asan {
+void GuardedPoolAllocator::initPRNG() {
+ _zx_cprng_draw(&getThreadLocals()->RandomState, sizeof(uint32_t));
+}
+
+void *GuardedPoolAllocator::map(size_t Size, const char *Name) const {
+ assert((Size % State.PageSize) == 0);
+ zx_handle_t Vmo;
+ zx_status_t Status = _zx_vmo_create(Size, 0, &Vmo);
+ Check(Status == ZX_OK, "Failed to create Vmo");
+ _zx_object_set_property(Vmo, ZX_PROP_NAME, Name, strlen(Name));
+ zx_vaddr_t Addr;
+ Status = _zx_vmar_map(_zx_vmar_root_self(),
+ ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_ALLOW_FAULTS,
+ 0, Vmo, 0, Size, &Addr);
+ Check(Status == ZX_OK, "Vmo mapping failed");
+ _zx_handle_close(Vmo);
+ return reinterpret_cast<void *>(Addr);
+}
+
+void GuardedPoolAllocator::unmap(void *Ptr, size_t Size) const {
+ assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
+ assert((Size % State.PageSize) == 0);
+ zx_status_t Status = _zx_vmar_unmap(_zx_vmar_root_self(),
+ reinterpret_cast<zx_vaddr_t>(Ptr), Size);
+ Check(Status == ZX_OK, "Vmo unmapping failed");
+}
+
+void *GuardedPoolAllocator::reserveGuardedPool(size_t Size) {
+ assert((Size % State.PageSize) == 0);
+ zx_vaddr_t Addr;
+ const zx_status_t Status = _zx_vmar_allocate(
+ _zx_vmar_root_self(),
+ ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC, 0,
+ Size, &GuardedPagePoolPlatformData.Vmar, &Addr);
+ Check(Status == ZX_OK, "Failed to reserve guarded pool allocator memory");
+ _zx_object_set_property(GuardedPagePoolPlatformData.Vmar, ZX_PROP_NAME,
+ kGwpAsanGuardPageName, strlen(kGwpAsanGuardPageName));
+ return reinterpret_cast<void *>(Addr);
+}
+
+void GuardedPoolAllocator::unreserveGuardedPool() {
+ const zx_handle_t Vmar = GuardedPagePoolPlatformData.Vmar;
+ assert(Vmar != ZX_HANDLE_INVALID && Vmar != _zx_vmar_root_self());
+ Check(_zx_vmar_destroy(Vmar) == ZX_OK, "Failed to destroy a vmar");
+ Check(_zx_handle_close(Vmar) == ZX_OK, "Failed to close a vmar");
+ GuardedPagePoolPlatformData.Vmar = ZX_HANDLE_INVALID;
+}
+
+void GuardedPoolAllocator::allocateInGuardedPool(void *Ptr, size_t Size) const {
+ assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
+ assert((Size % State.PageSize) == 0);
+ zx_handle_t Vmo;
+ zx_status_t Status = _zx_vmo_create(Size, 0, &Vmo);
+ Check(Status == ZX_OK, "Failed to create vmo");
+ _zx_object_set_property(Vmo, ZX_PROP_NAME, kGwpAsanAliveSlotName,
+ strlen(kGwpAsanAliveSlotName));
+ const zx_handle_t Vmar = GuardedPagePoolPlatformData.Vmar;
+ assert(Vmar != ZX_HANDLE_INVALID && Vmar != _zx_vmar_root_self());
+ const size_t Offset =
+ reinterpret_cast<uintptr_t>(Ptr) - State.GuardedPagePool;
+ zx_vaddr_t P;
+ Status = _zx_vmar_map(Vmar,
+ ZX_VM_PERM_READ | ZX_VM_PERM_WRITE |
+ ZX_VM_ALLOW_FAULTS | ZX_VM_SPECIFIC,
+ Offset, Vmo, 0, Size, &P);
+ Check(Status == ZX_OK, "Vmo mapping failed");
+ _zx_handle_close(Vmo);
+}
+
+void GuardedPoolAllocator::deallocateInGuardedPool(void *Ptr,
+ size_t Size) const {
+ assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
+ assert((Size % State.PageSize) == 0);
+ const zx_handle_t Vmar = GuardedPagePoolPlatformData.Vmar;
+ assert(Vmar != ZX_HANDLE_INVALID && Vmar != _zx_vmar_root_self());
+ const zx_status_t Status =
+ _zx_vmar_unmap(Vmar, reinterpret_cast<zx_vaddr_t>(Ptr), Size);
+ Check(Status == ZX_OK, "Vmar unmapping failed");
+}
+
+size_t GuardedPoolAllocator::getPlatformPageSize() {
+ return _zx_system_get_page_size();
+}
+
+void GuardedPoolAllocator::installAtFork() {}
+} // namespace gwp_asan
diff --git a/gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.h b/gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.h
new file mode 100644
index 0000000..fbd7d3a
--- /dev/null
+++ b/gwp_asan/platform_specific/guarded_pool_allocator_fuchsia.h
@@ -0,0 +1,22 @@
+//===-- guarded_pool_allocator_fuchsia.h ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__Fuchsia__)
+#ifndef GWP_ASAN_GUARDED_POOL_ALLOCATOR_FUCHSIA_H_
+#define GWP_ASAN_GUARDED_POOL_ALLOCATOR_FUCHSIA_H_
+
+#include <zircon/types.h>
+
+namespace gwp_asan {
+struct PlatformSpecificMapData {
+ zx_handle_t Vmar;
+};
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_GUARDED_POOL_ALLOCATOR_FUCHSIA_H_
+#endif // defined(__Fuchsia__)
diff --git a/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp b/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp
index a8767a4..adb7330 100644
--- a/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp
+++ b/gwp_asan/platform_specific/guarded_pool_allocator_posix.cpp
@@ -6,16 +6,17 @@
//
//===----------------------------------------------------------------------===//
+#include "gwp_asan/common.h"
#include "gwp_asan/guarded_pool_allocator.h"
+#include "gwp_asan/platform_specific/guarded_pool_allocator_tls.h"
#include "gwp_asan/utilities.h"
#include <assert.h>
-#include <errno.h>
-#include <signal.h>
+#include <pthread.h>
+#include <stdint.h>
#include <stdlib.h>
-#include <string.h>
#include <sys/mman.h>
-#include <sys/types.h>
+#include <time.h>
#include <unistd.h>
#ifdef ANDROID
@@ -24,6 +25,7 @@
#define PR_SET_VMA_ANON_NAME 0
#endif // ANDROID
+namespace {
void MaybeSetMappingName(void *Mapping, size_t Size, const char *Name) {
#ifdef ANDROID
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, Mapping, Size, Name);
@@ -31,39 +33,64 @@ void MaybeSetMappingName(void *Mapping, size_t Size, const char *Name) {
// Anonymous mapping names are only supported on Android.
return;
}
+} // anonymous namespace
namespace gwp_asan {
-void *GuardedPoolAllocator::mapMemory(size_t Size, const char *Name) const {
- void *Ptr =
- mmap(nullptr, Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+
+void GuardedPoolAllocator::initPRNG() {
+ getThreadLocals()->RandomState =
+ static_cast<uint32_t>(time(nullptr) + getThreadID());
+}
+
+void *GuardedPoolAllocator::map(size_t Size, const char *Name) const {
+ assert((Size % State.PageSize) == 0);
+ void *Ptr = mmap(nullptr, Size, PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
Check(Ptr != MAP_FAILED, "Failed to map guarded pool allocator memory");
MaybeSetMappingName(Ptr, Size, Name);
return Ptr;
}
-void GuardedPoolAllocator::unmapMemory(void *Ptr, size_t Size,
- const char *Name) const {
+void GuardedPoolAllocator::unmap(void *Ptr, size_t Size) const {
+ assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
+ assert((Size % State.PageSize) == 0);
Check(munmap(Ptr, Size) == 0,
"Failed to unmap guarded pool allocator memory.");
- MaybeSetMappingName(Ptr, Size, Name);
}
-void GuardedPoolAllocator::markReadWrite(void *Ptr, size_t Size,
- const char *Name) const {
+void *GuardedPoolAllocator::reserveGuardedPool(size_t Size) {
+ assert((Size % State.PageSize) == 0);
+ void *Ptr =
+ mmap(nullptr, Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
+ Check(Ptr != MAP_FAILED, "Failed to reserve guarded pool allocator memory");
+ MaybeSetMappingName(Ptr, Size, kGwpAsanGuardPageName);
+ return Ptr;
+}
+
+void GuardedPoolAllocator::unreserveGuardedPool() {
+ unmap(reinterpret_cast<void *>(State.GuardedPagePool),
+ State.GuardedPagePoolEnd - State.GuardedPagePool);
+}
+
+void GuardedPoolAllocator::allocateInGuardedPool(void *Ptr, size_t Size) const {
+ assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
+ assert((Size % State.PageSize) == 0);
Check(mprotect(Ptr, Size, PROT_READ | PROT_WRITE) == 0,
- "Failed to set guarded pool allocator memory at as RW.");
- MaybeSetMappingName(Ptr, Size, Name);
+ "Failed to allocate in guarded pool allocator memory");
+ MaybeSetMappingName(Ptr, Size, kGwpAsanAliveSlotName);
}
-void GuardedPoolAllocator::markInaccessible(void *Ptr, size_t Size,
- const char *Name) const {
+void GuardedPoolAllocator::deallocateInGuardedPool(void *Ptr,
+ size_t Size) const {
+ assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0);
+ assert((Size % State.PageSize) == 0);
// mmap() a PROT_NONE page over the address to release it to the system, if
// we used mprotect() here the system would count pages in the quarantine
// against the RSS.
Check(mmap(Ptr, Size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1,
0) != MAP_FAILED,
- "Failed to set guarded pool allocator memory as inaccessible.");
- MaybeSetMappingName(Ptr, Size, Name);
+ "Failed to deallocate in guarded pool allocator memory");
+ MaybeSetMappingName(Ptr, Size, kGwpAsanGuardPageName);
}
size_t GuardedPoolAllocator::getPlatformPageSize() {
@@ -81,5 +108,4 @@ void GuardedPoolAllocator::installAtFork() {
};
pthread_atfork(Disable, Enable, Enable);
}
-
} // namespace gwp_asan
diff --git a/gwp_asan/platform_specific/guarded_pool_allocator_posix.h b/gwp_asan/platform_specific/guarded_pool_allocator_posix.h
new file mode 100644
index 0000000..7f4ba0d
--- /dev/null
+++ b/gwp_asan/platform_specific/guarded_pool_allocator_posix.h
@@ -0,0 +1,18 @@
+//===-- guarded_pool_allocator_posix.h --------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__unix__)
+#ifndef GWP_ASAN_GUARDED_POOL_ALLOCATOR_POSIX_H_
+#define GWP_ASAN_GUARDED_POOL_ALLOCATOR_POSIX_H_
+
+namespace gwp_asan {
+struct PlatformSpecificMapData {};
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_GUARDED_POOL_ALLOCATOR_POSIX_H_
+#endif // defined(__unix__)
diff --git a/gwp_asan/platform_specific/guarded_pool_allocator_tls.h b/gwp_asan/platform_specific/guarded_pool_allocator_tls.h
new file mode 100644
index 0000000..3e2055d
--- /dev/null
+++ b/gwp_asan/platform_specific/guarded_pool_allocator_tls.h
@@ -0,0 +1,55 @@
+//===-- guarded_pool_allocator_tls.h ----------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef GWP_ASAN_GUARDED_POOL_ALLOCATOR_TLS_H_
+#define GWP_ASAN_GUARDED_POOL_ALLOCATOR_TLS_H_
+
+#include "gwp_asan/definitions.h"
+
+#include <stdint.h>
+
+namespace gwp_asan {
+// Pack the thread local variables into a struct to ensure that they're in
+// the same cache line for performance reasons. These are the most touched
+// variables in GWP-ASan.
+struct ThreadLocalPackedVariables {
+ constexpr ThreadLocalPackedVariables()
+ : RandomState(0xacd979ce), NextSampleCounter(0), RecursiveGuard(false) {}
+ // Initialised to a magic constant so that an uninitialised GWP-ASan won't
+ // regenerate its sample counter for as long as possible. The xorshift32()
+ // algorithm used below results in getRandomUnsigned32(0xacd979ce) ==
+ // 0xfffffffe.
+ uint32_t RandomState;
+ // Thread-local decrementing counter that indicates that a given allocation
+ // should be sampled when it reaches zero.
+ uint32_t NextSampleCounter : 31;
+ // The mask is needed to silence conversion errors.
+ static const uint32_t NextSampleCounterMask = (1U << 31) - 1;
+ // Guard against recursivity. Unwinders often contain complex behaviour that
+ // may not be safe for the allocator (i.e. the unwinder calls dlopen(),
+ // which calls malloc()). When recursive behaviour is detected, we will
+ // automatically fall back to the supporting allocator to supply the
+ // allocation.
+ bool RecursiveGuard : 1;
+};
+static_assert(sizeof(ThreadLocalPackedVariables) == sizeof(uint64_t),
+ "thread local data does not fit in a uint64_t");
+} // namespace gwp_asan
+
+#ifdef GWP_ASAN_PLATFORM_TLS_HEADER
+#include GWP_ASAN_PLATFORM_TLS_HEADER
+#else
+namespace gwp_asan {
+inline ThreadLocalPackedVariables *getThreadLocals() {
+ alignas(8) static GWP_ASAN_TLS_INITIAL_EXEC ThreadLocalPackedVariables Locals;
+ return &Locals;
+}
+} // namespace gwp_asan
+#endif // GWP_ASAN_PLATFORM_TLS_HEADER
+
+#endif // GWP_ASAN_GUARDED_POOL_ALLOCATOR_TLS_H_
diff --git a/gwp_asan/platform_specific/mutex_fuchsia.cpp b/gwp_asan/platform_specific/mutex_fuchsia.cpp
new file mode 100644
index 0000000..0431a82
--- /dev/null
+++ b/gwp_asan/platform_specific/mutex_fuchsia.cpp
@@ -0,0 +1,21 @@
+//===-- mutex_fuchsia.cpp ---------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/mutex.h"
+
+#include <lib/sync/mutex.h>
+
+namespace gwp_asan {
+void Mutex::lock() __TA_NO_THREAD_SAFETY_ANALYSIS { sync_mutex_lock(&Mu); }
+
+bool Mutex::tryLock() __TA_NO_THREAD_SAFETY_ANALYSIS {
+ return sync_mutex_trylock(&Mu) == ZX_OK;
+}
+
+void Mutex::unlock() __TA_NO_THREAD_SAFETY_ANALYSIS { sync_mutex_unlock(&Mu); }
+} // namespace gwp_asan
diff --git a/gwp_asan/platform_specific/mutex_fuchsia.h b/gwp_asan/platform_specific/mutex_fuchsia.h
new file mode 100644
index 0000000..edfc1a6
--- /dev/null
+++ b/gwp_asan/platform_specific/mutex_fuchsia.h
@@ -0,0 +1,23 @@
+//===-- mutex_fuchsia.h -----------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__Fuchsia__)
+#ifndef GWP_ASAN_MUTEX_FUCHSIA_H_
+#define GWP_ASAN_MUTEX_FUCHSIA_H_
+
+#include <lib/sync/mutex.h>
+
+namespace gwp_asan {
+class PlatformMutex {
+protected:
+ sync_mutex_t Mu = {};
+};
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_MUTEX_FUCHSIA_H_
+#endif // defined(__Fuchsia__)
diff --git a/gwp_asan/platform_specific/mutex_posix.h b/gwp_asan/platform_specific/mutex_posix.h
new file mode 100644
index 0000000..7f02391
--- /dev/null
+++ b/gwp_asan/platform_specific/mutex_posix.h
@@ -0,0 +1,23 @@
+//===-- mutex_posix.h -------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#if defined(__unix__)
+#ifndef GWP_ASAN_MUTEX_POSIX_H_
+#define GWP_ASAN_MUTEX_POSIX_H_
+
+#include <pthread.h>
+
+namespace gwp_asan {
+class PlatformMutex {
+protected:
+ pthread_mutex_t Mu = PTHREAD_MUTEX_INITIALIZER;
+};
+} // namespace gwp_asan
+
+#endif // GWP_ASAN_MUTEX_POSIX_H_
+#endif // defined(__unix__)
diff --git a/gwp_asan/platform_specific/utilities_fuchsia.cpp b/gwp_asan/platform_specific/utilities_fuchsia.cpp
new file mode 100644
index 0000000..bc9d3a4
--- /dev/null
+++ b/gwp_asan/platform_specific/utilities_fuchsia.cpp
@@ -0,0 +1,19 @@
+//===-- utilities_fuchsia.cpp -----------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/utilities.h"
+
+#include <string.h>
+#include <zircon/sanitizer.h>
+
+namespace gwp_asan {
+void die(const char *Message) {
+ __sanitizer_log_write(Message, strlen(Message));
+ __builtin_trap();
+}
+} // namespace gwp_asan
diff --git a/gwp_asan/platform_specific/utilities_posix.cpp b/gwp_asan/platform_specific/utilities_posix.cpp
index 45b1939..7357963 100644
--- a/gwp_asan/platform_specific/utilities_posix.cpp
+++ b/gwp_asan/platform_specific/utilities_posix.cpp
@@ -6,12 +6,10 @@
//
//===----------------------------------------------------------------------===//
-#include "gwp_asan/definitions.h"
-#include "gwp_asan/utilities.h"
-
-#include <assert.h>
+#include <features.h> // IWYU pragma: keep (for __BIONIC__ macro)
#ifdef __BIONIC__
+#include "gwp_asan/definitions.h"
#include <stdlib.h>
extern "C" GWP_ASAN_WEAK void android_set_abort_message(const char *);
#else // __BIONIC__
@@ -19,71 +17,14 @@ extern "C" GWP_ASAN_WEAK void android_set_abort_message(const char *);
#endif
namespace gwp_asan {
-
+void die(const char *Message) {
#ifdef __BIONIC__
-void Check(bool Condition, const char *Message) {
- if (Condition)
- return;
if (&android_set_abort_message != nullptr)
android_set_abort_message(Message);
abort();
-}
#else // __BIONIC__
-void Check(bool Condition, const char *Message) {
- if (Condition)
- return;
fprintf(stderr, "%s", Message);
__builtin_trap();
-}
#endif // __BIONIC__
-
-// See `bionic/tests/malloc_test.cpp` in the Android source for documentation
-// regarding their alignment guarantees. We always round up to the closest
-// 8-byte window. As GWP-ASan's malloc(X) can always get exactly an X-sized
-// allocation, an allocation that rounds up to 16-bytes will always be given a
-// 16-byte aligned allocation.
-static size_t alignBionic(size_t RealAllocationSize) {
- if (RealAllocationSize % 8 == 0)
- return RealAllocationSize;
- return RealAllocationSize + 8 - (RealAllocationSize % 8);
-}
-
-static size_t alignPowerOfTwo(size_t RealAllocationSize) {
- if (RealAllocationSize <= 2)
- return RealAllocationSize;
- if (RealAllocationSize <= 4)
- return 4;
- if (RealAllocationSize <= 8)
- return 8;
- if (RealAllocationSize % 16 == 0)
- return RealAllocationSize;
- return RealAllocationSize + 16 - (RealAllocationSize % 16);
}
-
-#ifdef __BIONIC__
-static constexpr AlignmentStrategy PlatformDefaultAlignment =
- AlignmentStrategy::BIONIC;
-#else // __BIONIC__
-static constexpr AlignmentStrategy PlatformDefaultAlignment =
- AlignmentStrategy::POWER_OF_TWO;
-#endif // __BIONIC__
-
-size_t rightAlignedAllocationSize(size_t RealAllocationSize,
- AlignmentStrategy Align) {
- assert(RealAllocationSize > 0);
- if (Align == AlignmentStrategy::DEFAULT)
- Align = PlatformDefaultAlignment;
-
- switch (Align) {
- case AlignmentStrategy::BIONIC:
- return alignBionic(RealAllocationSize);
- case AlignmentStrategy::POWER_OF_TWO:
- return alignPowerOfTwo(RealAllocationSize);
- case AlignmentStrategy::PERFECT:
- return RealAllocationSize;
- case AlignmentStrategy::DEFAULT:
- __builtin_unreachable();
- }
-}
-
} // namespace gwp_asan
diff --git a/gwp_asan/random.cpp b/gwp_asan/random.cpp
deleted file mode 100644
index 2180f92..0000000
--- a/gwp_asan/random.cpp
+++ /dev/null
@@ -1,31 +0,0 @@
-//===-- random.cpp ----------------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "gwp_asan/random.h"
-#include "gwp_asan/common.h"
-
-#include <time.h>
-
-// Initialised to a magic constant so that an uninitialised GWP-ASan won't
-// regenerate its sample counter for as long as possible. The xorshift32()
-// algorithm used below results in getRandomUnsigned32(0xff82eb50) ==
-// 0xfffffea4.
-GWP_ASAN_TLS_INITIAL_EXEC uint32_t RandomState = 0xff82eb50;
-
-namespace gwp_asan {
-void initPRNG() {
- RandomState = time(nullptr) + getThreadID();
-}
-
-uint32_t getRandomUnsigned32() {
- RandomState ^= RandomState << 13;
- RandomState ^= RandomState >> 17;
- RandomState ^= RandomState << 5;
- return RandomState;
-}
-} // namespace gwp_asan
diff --git a/gwp_asan/random.h b/gwp_asan/random.h
deleted file mode 100644
index 953b989..0000000
--- a/gwp_asan/random.h
+++ /dev/null
@@ -1,23 +0,0 @@
-//===-- random.h ------------------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef GWP_ASAN_RANDOM_H_
-#define GWP_ASAN_RANDOM_H_
-
-#include <stdint.h>
-
-namespace gwp_asan {
-// Initialise the PRNG, using time and thread ID as the seed.
-void initPRNG();
-
-// xorshift (32-bit output), extremely fast PRNG that uses arithmetic operations
-// only. Seeded using walltime.
-uint32_t getRandomUnsigned32();
-} // namespace gwp_asan
-
-#endif // GWP_ASAN_RANDOM_H_
diff --git a/gwp_asan/tests/alignment.cpp b/gwp_asan/tests/alignment.cpp
index bf98f1f..5f24a9a 100644
--- a/gwp_asan/tests/alignment.cpp
+++ b/gwp_asan/tests/alignment.cpp
@@ -6,39 +6,109 @@
//
//===----------------------------------------------------------------------===//
+#include "gwp_asan/guarded_pool_allocator.h"
#include "gwp_asan/tests/harness.h"
-#include "gwp_asan/utilities.h"
-
-TEST(AlignmentTest, PowerOfTwo) {
- std::vector<std::pair<size_t, size_t>> AskedSizeToAlignedSize = {
- {1, 1}, {2, 2}, {3, 4}, {4, 4}, {5, 8}, {7, 8},
- {8, 8}, {9, 16}, {15, 16}, {16, 16}, {17, 32}, {31, 32},
- {32, 32}, {33, 48}, {4095, 4096}, {4096, 4096},
- };
-
- for (const auto &KV : AskedSizeToAlignedSize) {
- EXPECT_EQ(KV.second,
- gwp_asan::rightAlignedAllocationSize(
- KV.first, gwp_asan::AlignmentStrategy::POWER_OF_TWO));
+
+class AlignmentTestGPA : public gwp_asan::GuardedPoolAllocator {
+public:
+ static size_t getRequiredBackingSize(size_t Size, size_t Alignment,
+ size_t PageSize) {
+ return GuardedPoolAllocator::getRequiredBackingSize(Size, Alignment,
+ PageSize);
+ }
+ static uintptr_t alignUp(uintptr_t Ptr, size_t Alignment) {
+ return GuardedPoolAllocator::alignUp(Ptr, Alignment);
}
+ static uintptr_t alignDown(uintptr_t Ptr, size_t Alignment) {
+ return GuardedPoolAllocator::alignDown(Ptr, Alignment);
+ }
+};
+
+// Global assumptions for these tests:
+// 1. Page size is 0x1000.
+// 2. All tests assume a slot is multipage, between 0x4000 - 0x8000. While we
+// don't use multipage slots right now, this tests more boundary conditions
+// and allows us to add this feature at a later date without rewriting the
+// alignment functionality.
+// These aren't actual requirements of the allocator - but just simplifies the
+// numerics of the testing.
+TEST(AlignmentTest, LeftAlignedAllocs) {
+ // Alignment < Page Size.
+ EXPECT_EQ(0x4000, AlignmentTestGPA::alignUp(
+ /* Ptr */ 0x4000, /* Alignment */ 0x1));
+ // Alignment == Page Size.
+ EXPECT_EQ(0x4000, AlignmentTestGPA::alignUp(
+ /* Ptr */ 0x4000, /* Alignment */ 0x1000));
+ // Alignment > Page Size.
+ EXPECT_EQ(0x4000, AlignmentTestGPA::alignUp(
+ /* Ptr */ 0x4000, /* Alignment */ 0x4000));
}
-TEST(AlignmentTest, AlignBionic) {
- std::vector<std::pair<size_t, size_t>> AskedSizeToAlignedSize = {
- {1, 8}, {2, 8}, {3, 8}, {4, 8}, {5, 8}, {7, 8},
- {8, 8}, {9, 16}, {15, 16}, {16, 16}, {17, 24}, {31, 32},
- {32, 32}, {33, 40}, {4095, 4096}, {4096, 4096},
- };
+TEST(AlignmentTest, SingleByteAllocs) {
+ // Alignment < Page Size.
+ EXPECT_EQ(0x1,
+ AlignmentTestGPA::getRequiredBackingSize(
+ /* Size */ 0x1, /* Alignment */ 0x1, /* PageSize */ 0x1000));
+ EXPECT_EQ(0x7fff, AlignmentTestGPA::alignDown(
+ /* Ptr */ 0x8000 - 0x1, /* Alignment */ 0x1));
- for (const auto &KV : AskedSizeToAlignedSize) {
- EXPECT_EQ(KV.second, gwp_asan::rightAlignedAllocationSize(
- KV.first, gwp_asan::AlignmentStrategy::BIONIC));
- }
+ // Alignment == Page Size.
+ EXPECT_EQ(0x1,
+ AlignmentTestGPA::getRequiredBackingSize(
+ /* Size */ 0x1, /* Alignment */ 0x1000, /* PageSize */ 0x1000));
+ EXPECT_EQ(0x7000, AlignmentTestGPA::alignDown(
+ /* Ptr */ 0x8000 - 0x1, /* Alignment */ 0x1000));
+
+ // Alignment > Page Size.
+ EXPECT_EQ(0x3001,
+ AlignmentTestGPA::getRequiredBackingSize(
+ /* Size */ 0x1, /* Alignment */ 0x4000, /* PageSize */ 0x1000));
+ EXPECT_EQ(0x4000, AlignmentTestGPA::alignDown(
+ /* Ptr */ 0x8000 - 0x1, /* Alignment */ 0x4000));
}
-TEST(AlignmentTest, PerfectAlignment) {
- for (size_t i = 1; i <= 4096; ++i) {
- EXPECT_EQ(i, gwp_asan::rightAlignedAllocationSize(
- i, gwp_asan::AlignmentStrategy::PERFECT));
- }
+TEST(AlignmentTest, PageSizedAllocs) {
+ // Alignment < Page Size.
+ EXPECT_EQ(0x1000,
+ AlignmentTestGPA::getRequiredBackingSize(
+ /* Size */ 0x1000, /* Alignment */ 0x1, /* PageSize */ 0x1000));
+ EXPECT_EQ(0x7000, AlignmentTestGPA::alignDown(
+ /* Ptr */ 0x8000 - 0x1000, /* Alignment */ 0x1));
+
+ // Alignment == Page Size.
+ EXPECT_EQ(0x1000, AlignmentTestGPA::getRequiredBackingSize(
+ /* Size */ 0x1000, /* Alignment */ 0x1000,
+ /* PageSize */ 0x1000));
+ EXPECT_EQ(0x7000, AlignmentTestGPA::alignDown(
+ /* Ptr */ 0x8000 - 0x1000, /* Alignment */ 0x1000));
+
+ // Alignment > Page Size.
+ EXPECT_EQ(0x4000, AlignmentTestGPA::getRequiredBackingSize(
+ /* Size */ 0x1000, /* Alignment */ 0x4000,
+ /* PageSize */ 0x1000));
+ EXPECT_EQ(0x4000, AlignmentTestGPA::alignDown(
+ /* Ptr */ 0x8000 - 0x1000, /* Alignment */ 0x4000));
+}
+
+TEST(AlignmentTest, MoreThanPageAllocs) {
+ // Alignment < Page Size.
+ EXPECT_EQ(0x2fff,
+ AlignmentTestGPA::getRequiredBackingSize(
+ /* Size */ 0x2fff, /* Alignment */ 0x1, /* PageSize */ 0x1000));
+ EXPECT_EQ(0x5001, AlignmentTestGPA::alignDown(
+ /* Ptr */ 0x8000 - 0x2fff, /* Alignment */ 0x1));
+
+ // Alignment == Page Size.
+ EXPECT_EQ(0x2fff, AlignmentTestGPA::getRequiredBackingSize(
+ /* Size */ 0x2fff, /* Alignment */ 0x1000,
+ /* PageSize */ 0x1000));
+ EXPECT_EQ(0x5000, AlignmentTestGPA::alignDown(
+ /* Ptr */ 0x8000 - 0x2fff, /* Alignment */ 0x1000));
+
+ // Alignment > Page Size.
+ EXPECT_EQ(0x5fff, AlignmentTestGPA::getRequiredBackingSize(
+ /* Size */ 0x2fff, /* Alignment */ 0x4000,
+ /* PageSize */ 0x1000));
+ EXPECT_EQ(0x4000, AlignmentTestGPA::alignDown(
+ /* Ptr */ 0x8000 - 0x2fff, /* Alignment */ 0x4000));
}
diff --git a/gwp_asan/tests/backtrace.cpp b/gwp_asan/tests/backtrace.cpp
index b3d4427..9515065 100644
--- a/gwp_asan/tests/backtrace.cpp
+++ b/gwp_asan/tests/backtrace.cpp
@@ -8,6 +8,7 @@
#include <string>
+#include "gwp_asan/common.h"
#include "gwp_asan/crash_handler.h"
#include "gwp_asan/tests/harness.h"
@@ -76,9 +77,46 @@ TEST(Backtrace, Short) {
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 *TraceBuffer, size_t Size) -> size_t {
+ // Need to inintialise the elements that will be packed.
+ memset(TraceBuffer, 0u, Size * sizeof(*TraceBuffer));
+
+ // Indicate that there were more frames, and we just didn't have enough
+ // room to store them.
+ return Size * 2;
+ });
+ // Retrieve a frame from the collected backtrace, make sure it works E2E.
+ uintptr_t TraceOutput;
+ EXPECT_EQ(gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect,
+ __gwp_asan_get_allocation_trace(&Meta, &TraceOutput, 1));
+}
+
+TEST(Backtrace, ExceedsRetrievableAllocLength) {
+ gwp_asan::AllocationMetadata Meta;
+ constexpr size_t kNumFramesToStore = 3u;
+ Meta.AllocationTrace.RecordBacktrace(
+ [](uintptr_t *TraceBuffer, size_t /* Size */) -> size_t {
+ memset(TraceBuffer, kNumFramesToStore,
+ kNumFramesToStore * sizeof(*TraceBuffer));
+ return kNumFramesToStore;
+ });
+ uintptr_t TraceOutput;
+ // Ask for one element, get told that there's `kNumFramesToStore` available.
+ EXPECT_EQ(kNumFramesToStore,
+ __gwp_asan_get_allocation_trace(&Meta, &TraceOutput, 1));
+}
+
+TEST(Backtrace, ExceedsRetrievableDeallocLength) {
+ gwp_asan::AllocationMetadata Meta;
+ constexpr size_t kNumFramesToStore = 3u;
+ Meta.DeallocationTrace.RecordBacktrace(
+ [](uintptr_t *TraceBuffer, size_t /* Size */) -> size_t {
+ memset(TraceBuffer, kNumFramesToStore,
+ kNumFramesToStore * sizeof(*TraceBuffer));
+ return kNumFramesToStore;
});
uintptr_t TraceOutput;
- EXPECT_EQ(1u, __gwp_asan_get_allocation_trace(&Meta, &TraceOutput, 1));
+ // Ask for one element, get told that there's `kNumFramesToStore` available.
+ EXPECT_EQ(kNumFramesToStore,
+ __gwp_asan_get_deallocation_trace(&Meta, &TraceOutput, 1));
}
diff --git a/gwp_asan/tests/basic.cpp b/gwp_asan/tests/basic.cpp
index 29f420d..88e7ed1 100644
--- a/gwp_asan/tests/basic.cpp
+++ b/gwp_asan/tests/basic.cpp
@@ -39,6 +39,37 @@ TEST_F(CustomGuardedPoolAllocator, SizedAllocations) {
TEST_F(DefaultGuardedPoolAllocator, TooLargeAllocation) {
EXPECT_EQ(nullptr,
GPA.allocate(GPA.getAllocatorState()->maximumAllocationSize() + 1));
+ EXPECT_EQ(nullptr, GPA.allocate(SIZE_MAX, 0));
+ EXPECT_EQ(nullptr, GPA.allocate(SIZE_MAX, 1));
+ EXPECT_EQ(nullptr, GPA.allocate(0, SIZE_MAX / 2));
+ EXPECT_EQ(nullptr, GPA.allocate(1, SIZE_MAX / 2));
+ EXPECT_EQ(nullptr, GPA.allocate(SIZE_MAX, SIZE_MAX / 2));
+}
+
+TEST_F(DefaultGuardedPoolAllocator, ZeroSizeAndAlignmentAllocations) {
+ void *P;
+ EXPECT_NE(nullptr, (P = GPA.allocate(0, 0)));
+ GPA.deallocate(P);
+ EXPECT_NE(nullptr, (P = GPA.allocate(1, 0)));
+ GPA.deallocate(P);
+ EXPECT_NE(nullptr, (P = GPA.allocate(0, 1)));
+ GPA.deallocate(P);
+}
+
+TEST_F(DefaultGuardedPoolAllocator, NonPowerOfTwoAlignment) {
+ EXPECT_EQ(nullptr, GPA.allocate(0, 3));
+ EXPECT_EQ(nullptr, GPA.allocate(1, 3));
+ EXPECT_EQ(nullptr, GPA.allocate(0, SIZE_MAX));
+ EXPECT_EQ(nullptr, GPA.allocate(1, SIZE_MAX));
+}
+
+// Added multi-page slots? You'll need to expand this test.
+TEST_F(DefaultGuardedPoolAllocator, TooBigForSinglePageSlots) {
+ EXPECT_EQ(nullptr, GPA.allocate(0x1001, 0));
+ EXPECT_EQ(nullptr, GPA.allocate(0x1001, 1));
+ EXPECT_EQ(nullptr, GPA.allocate(0x1001, 0x1000));
+ EXPECT_EQ(nullptr, GPA.allocate(1, 0x2000));
+ EXPECT_EQ(nullptr, GPA.allocate(0, 0x2000));
}
TEST_F(CustomGuardedPoolAllocator, AllocAllSlots) {
diff --git a/gwp_asan/tests/compression.cpp b/gwp_asan/tests/compression.cpp
index 7a5894d..2423c86 100644
--- a/gwp_asan/tests/compression.cpp
+++ b/gwp_asan/tests/compression.cpp
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
#include "gwp_asan/stack_trace_compressor.h"
-#include "gtest/gtest.h"
+#include "gwp_asan/tests/harness.h"
namespace gwp_asan {
namespace compression {
diff --git a/gwp_asan/tests/crash_handler_api.cpp b/gwp_asan/tests/crash_handler_api.cpp
index 10a014e..4cdb569 100644
--- a/gwp_asan/tests/crash_handler_api.cpp
+++ b/gwp_asan/tests/crash_handler_api.cpp
@@ -16,7 +16,7 @@ using GuardedPoolAllocator = gwp_asan::GuardedPoolAllocator;
using AllocationMetadata = gwp_asan::AllocationMetadata;
using AllocatorState = gwp_asan::AllocatorState;
-class CrashHandlerAPITest : public ::testing::Test {
+class CrashHandlerAPITest : public Test {
public:
void SetUp() override { setupState(); }
@@ -29,7 +29,7 @@ protected:
size_t Slot = State.getNearestSlot(Addr);
Metadata[Slot].Addr = Addr;
- Metadata[Slot].Size = Size;
+ Metadata[Slot].RequestedSize = Size;
Metadata[Slot].IsDeallocated = IsDeallocated;
Metadata[Slot].AllocationTrace.ThreadID = 123;
Metadata[Slot].DeallocationTrace.ThreadID = 321;
@@ -80,7 +80,8 @@ protected:
__gwp_asan_get_metadata(&State, Metadata, ErrorPtr);
EXPECT_NE(nullptr, Meta);
EXPECT_EQ(Metadata[Index].Addr, __gwp_asan_get_allocation_address(Meta));
- EXPECT_EQ(Metadata[Index].Size, __gwp_asan_get_allocation_size(Meta));
+ EXPECT_EQ(Metadata[Index].RequestedSize,
+ __gwp_asan_get_allocation_size(Meta));
EXPECT_EQ(Metadata[Index].AllocationTrace.ThreadID,
__gwp_asan_get_allocation_thread_id(Meta));
diff --git a/gwp_asan/tests/driver.cpp b/gwp_asan/tests/driver.cpp
index b402cec..02ab360 100644
--- a/gwp_asan/tests/driver.cpp
+++ b/gwp_asan/tests/driver.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
-#include "gtest/gtest.h"
+#include "gwp_asan/tests/harness.h"
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv);
diff --git a/gwp_asan/tests/harness.cpp b/gwp_asan/tests/harness.cpp
index 77c25ee..e668c73 100644
--- a/gwp_asan/tests/harness.cpp
+++ b/gwp_asan/tests/harness.cpp
@@ -1,4 +1,12 @@
-#include "harness.h"
+//===-- harness.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/tests/harness.h"
namespace gwp_asan {
namespace test {
diff --git a/gwp_asan/tests/harness.h b/gwp_asan/tests/harness.h
index e47254e..a61b856 100644
--- a/gwp_asan/tests/harness.h
+++ b/gwp_asan/tests/harness.h
@@ -11,10 +11,17 @@
#include <stdarg.h>
+#if defined(__Fuchsia__)
+#include <zxtest/zxtest.h>
+using Test = ::zxtest::Test;
+#else
#include "gtest/gtest.h"
+using Test = ::testing::Test;
+#endif
#include "gwp_asan/guarded_pool_allocator.h"
#include "gwp_asan/optional/backtrace.h"
+#include "gwp_asan/optional/printf.h"
#include "gwp_asan/optional/segv_handler.h"
#include "gwp_asan/options.h"
@@ -24,7 +31,7 @@ namespace test {
// their own signal-safe Printf function. In LLVM, we use
// `optional/printf_sanitizer_common.cpp` which supplies the __sanitizer::Printf
// for this purpose.
-crash_handler::Printf_t getPrintfFunction();
+Printf_t getPrintfFunction();
// First call returns true, all the following calls return false.
bool OnlyOnce();
@@ -32,7 +39,7 @@ bool OnlyOnce();
}; // namespace test
}; // namespace gwp_asan
-class DefaultGuardedPoolAllocator : public ::testing::Test {
+class DefaultGuardedPoolAllocator : public Test {
public:
void SetUp() override {
gwp_asan::options::Options Opts;
@@ -51,7 +58,7 @@ protected:
MaxSimultaneousAllocations;
};
-class CustomGuardedPoolAllocator : public ::testing::Test {
+class CustomGuardedPoolAllocator : public Test {
public:
void
InitNumSlots(decltype(gwp_asan::options::Options::MaxSimultaneousAllocations)
@@ -74,24 +81,25 @@ protected:
MaxSimultaneousAllocations;
};
-class BacktraceGuardedPoolAllocator : public ::testing::Test {
+class BacktraceGuardedPoolAllocator : public Test {
public:
void SetUp() override {
gwp_asan::options::Options Opts;
Opts.setDefaults();
- Opts.Backtrace = gwp_asan::options::getBacktraceFunction();
+ Opts.Backtrace = gwp_asan::backtrace::getBacktraceFunction();
Opts.InstallForkHandlers = gwp_asan::test::OnlyOnce();
GPA.init(Opts);
- gwp_asan::crash_handler::installSignalHandlers(
+ gwp_asan::segv_handler::installSignalHandlers(
&GPA, gwp_asan::test::getPrintfFunction(),
- gwp_asan::options::getPrintBacktraceFunction(), Opts.Backtrace);
+ gwp_asan::backtrace::getPrintBacktraceFunction(),
+ gwp_asan::backtrace::getSegvBacktraceFunction());
}
void TearDown() override {
GPA.uninitTestOnly();
- gwp_asan::crash_handler::uninstallSignalHandlers();
+ gwp_asan::segv_handler::uninstallSignalHandlers();
}
protected:
diff --git a/gwp_asan/tests/iterate.cpp b/gwp_asan/tests/iterate.cpp
index c40df15..2b8635d 100644
--- a/gwp_asan/tests/iterate.cpp
+++ b/gwp_asan/tests/iterate.cpp
@@ -8,6 +8,9 @@
#include "gwp_asan/tests/harness.h"
+#include <set>
+#include <vector>
+
TEST_F(CustomGuardedPoolAllocator, Iterate) {
InitNumSlots(7);
std::vector<std::pair<void *, size_t>> Allocated;
diff --git a/gwp_asan/tests/late_init.cpp b/gwp_asan/tests/late_init.cpp
index c7d62c8..8a94727 100644
--- a/gwp_asan/tests/late_init.cpp
+++ b/gwp_asan/tests/late_init.cpp
@@ -8,7 +8,7 @@
#include "gwp_asan/guarded_pool_allocator.h"
#include "gwp_asan/options.h"
-#include "gtest/gtest.h"
+#include "gwp_asan/tests/harness.h"
TEST(LateInit, CheckLateInitIsOK) {
gwp_asan::GuardedPoolAllocator GPA;
diff --git a/gwp_asan/tests/mutex_test.cpp b/gwp_asan/tests/mutex_test.cpp
index 5bc53b9..f68619c 100644
--- a/gwp_asan/tests/mutex_test.cpp
+++ b/gwp_asan/tests/mutex_test.cpp
@@ -7,7 +7,7 @@
//===----------------------------------------------------------------------===//
#include "gwp_asan/mutex.h"
-#include "gtest/gtest.h"
+#include "gwp_asan/tests/harness.h"
#include <atomic>
#include <mutex>
diff --git a/gwp_asan/tests/options.cpp b/gwp_asan/tests/options.cpp
new file mode 100644
index 0000000..ffa4bca
--- /dev/null
+++ b/gwp_asan/tests/options.cpp
@@ -0,0 +1,63 @@
+//===-- options.cpp ---------------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "gwp_asan/tests/harness.h"
+
+#include "gwp_asan/optional/options_parser.h"
+#include "gwp_asan/options.h"
+
+#include <stdarg.h>
+
+static char Message[1024];
+void MessageRecorder(const char *Format, ...) {
+ va_list Args;
+ va_start(Args, Format);
+ vsprintf(Message + strlen(Message), Format, Args);
+ va_end(Args);
+}
+
+TEST(GwpAsanOptionsTest, Basic) {
+ Message[0] = '\0';
+ gwp_asan::options::initOptions("Enabled=0:SampleRate=4:"
+ "InstallSignalHandlers=false",
+ MessageRecorder);
+ gwp_asan::options::Options Opts = gwp_asan::options::getOptions();
+ EXPECT_EQ('\0', Message[0]);
+ EXPECT_FALSE(Opts.Enabled);
+ EXPECT_FALSE(Opts.InstallSignalHandlers);
+ EXPECT_EQ(4, Opts.SampleRate);
+}
+
+void RunErrorTest(const char *OptionsStr, const char *ErrorNeedle) {
+ Message[0] = '\0';
+ gwp_asan::options::initOptions(OptionsStr, MessageRecorder);
+ EXPECT_NE('\0', Message[0])
+ << "Options string \"" << OptionsStr << "\" didn't generate an error.";
+ EXPECT_NE(nullptr, strstr(Message, ErrorNeedle))
+ << "Couldn't find error needle \"" << ErrorNeedle
+ << "\" in haystack created by options string \"" << OptionsStr
+ << "\". Error was: \"" << Message << "\".";
+}
+
+TEST(GwpAsanOptionsTest, FailureModes) {
+ RunErrorTest("Enabled=2", "Invalid boolean value '2' for option 'Enabled'");
+ RunErrorTest("Enabled=1:MaxSimultaneousAllocations=0",
+ "MaxSimultaneousAllocations must be > 0");
+ RunErrorTest("Enabled=1:MaxSimultaneousAllocations=-1",
+ "MaxSimultaneousAllocations must be > 0");
+ RunErrorTest("Enabled=1:SampleRate=0", "SampleRate must be > 0");
+ RunErrorTest("Enabled=1:SampleRate=-1", "SampleRate must be > 0");
+ RunErrorTest("Enabled=", "Invalid boolean value '' for option 'Enabled'");
+ RunErrorTest("==", "Unknown option '=='");
+ RunErrorTest("Enabled==0", "Invalid boolean value '=0' for option 'Enabled'");
+ RunErrorTest("Enabled:", "Expected '=' when parsing option 'Enabled:'");
+ RunErrorTest("Enabled:=", "Expected '=' when parsing option 'Enabled:='");
+ RunErrorTest("SampleRate=NOT_A_NUMBER",
+ "Invalid integer value 'NOT_A_NUMBER' for option 'SampleRate'");
+ RunErrorTest("NOT_A_VALUE=0", "Unknown option 'NOT_A_VALUE");
+}
diff --git a/gwp_asan/tests/optional/printf_sanitizer_common.cpp b/gwp_asan/tests/platform_specific/printf_sanitizer_common.cpp
index ea7141b..102b1db 100644
--- a/gwp_asan/tests/optional/printf_sanitizer_common.cpp
+++ b/gwp_asan/tests/platform_specific/printf_sanitizer_common.cpp
@@ -6,15 +6,14 @@
//
//===----------------------------------------------------------------------===//
-#include "gwp_asan/optional/segv_handler.h"
+#include "gwp_asan/optional/printf.h"
+
#include "sanitizer_common/sanitizer_common.h"
namespace gwp_asan {
namespace test {
-// This printf-function getter allows other platforms (e.g. Android) to define
-// their own signal-safe Printf function. In LLVM, we use
-// `optional/printf_sanitizer_common.cpp` which supplies the __sanitizer::Printf
-// for this purpose.
-crash_handler::Printf_t getPrintfFunction() { return __sanitizer::Printf; }
-}; // namespace test
-}; // namespace gwp_asan
+
+Printf_t getPrintfFunction() { return __sanitizer::Printf; }
+
+} // namespace test
+} // namespace gwp_asan
diff --git a/gwp_asan/tests/slot_reuse.cpp b/gwp_asan/tests/slot_reuse.cpp
index ee4b671..f2a77b0 100644
--- a/gwp_asan/tests/slot_reuse.cpp
+++ b/gwp_asan/tests/slot_reuse.cpp
@@ -8,6 +8,8 @@
#include "gwp_asan/tests/harness.h"
+#include <set>
+
void singleByteGoodAllocDealloc(gwp_asan::GuardedPoolAllocator *GPA) {
void *Ptr = GPA->allocate(1);
EXPECT_NE(nullptr, Ptr);
diff --git a/gwp_asan/utilities.h b/gwp_asan/utilities.h
index 71d525f..d8bc0e4 100644
--- a/gwp_asan/utilities.h
+++ b/gwp_asan/utilities.h
@@ -6,26 +6,23 @@
//
//===----------------------------------------------------------------------===//
+#ifndef GWP_ASAN_UTILITIES_H_
+#define GWP_ASAN_UTILITIES_H_
+
#include "gwp_asan/definitions.h"
#include <stddef.h>
-#include <stdint.h>
namespace gwp_asan {
-// Checks that `Condition` is true, otherwise fails in a platform-specific way
-// with `Message`.
-void Check(bool Condition, const char *Message);
-
-enum class AlignmentStrategy {
- // Default => POWER_OF_TWO on most platforms, BIONIC for Android Bionic.
- DEFAULT,
- POWER_OF_TWO,
- BIONIC,
- PERFECT,
-};
+// Terminates in a platform-specific way with `Message`.
+void die(const char *Message);
-// Returns the real size of a right-aligned allocation.
-size_t rightAlignedAllocationSize(
- size_t RealAllocationSize,
- AlignmentStrategy Align = AlignmentStrategy::DEFAULT);
+// Checks that `Condition` is true, otherwise dies with `Message`.
+GWP_ASAN_ALWAYS_INLINE void Check(bool Condition, const char *Message) {
+ if (Condition)
+ return;
+ die(Message);
+}
} // namespace gwp_asan
+
+#endif // GWP_ASAN_UTILITIES_H_
diff --git a/tools/options_parser_fuzzer.cpp b/tools/options_parser_fuzzer.cpp
new file mode 100644
index 0000000..2d87f12
--- /dev/null
+++ b/tools/options_parser_fuzzer.cpp
@@ -0,0 +1,12 @@
+#include <cstddef>
+#include <cstdint>
+
+#include <fuzzer/FuzzedDataProvider.h>
+
+#include "gwp_asan/optional/options_parser.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+ FuzzedDataProvider Fdp(Data, Size);
+ gwp_asan::options::initOptions(Fdp.ConsumeRemainingBytesAsString().c_str());
+ return 0;
+}