//===-- crash_handler_api.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/crash_handler.h"
#include "gwp_asan/guarded_pool_allocator.h"
#include "gwp_asan/stack_trace_compressor.h"
#include "gwp_asan/tests/harness.h"
using Error = gwp_asan::Error;
using GuardedPoolAllocator = gwp_asan::GuardedPoolAllocator;
using AllocationMetadata = gwp_asan::AllocationMetadata;
using AllocatorState = gwp_asan::AllocatorState;
class CrashHandlerAPITest : public ::testing::Test {
public:
void SetUp() override { setupState(); }
protected:
size_t metadata(uintptr_t Addr, uintptr_t Size, bool IsDeallocated) {
// Should only be allocating the 0x3000, 0x5000, 0x7000, 0x9000 pages.
EXPECT_GE(Addr, 0x3000u);
EXPECT_LT(Addr, 0xa000u);
size_t Slot = State.getNearestSlot(Addr);
Metadata[Slot].Addr = Addr;
Metadata[Slot].RequestedSize = Size;
Metadata[Slot].IsDeallocated = IsDeallocated;
Metadata[Slot].AllocationTrace.ThreadID = 123;
Metadata[Slot].DeallocationTrace.ThreadID = 321;
setupBacktraces(&Metadata[Slot]);
return Slot;
}
void setupState() {
State.GuardedPagePool = 0x2000;
State.GuardedPagePoolEnd = 0xc000;
InternalFaultAddr = State.GuardedPagePoolEnd - 0x10;
State.MaxSimultaneousAllocations = 4; // 0x3000, 0x5000, 0x7000, 0x9000.
State.PageSize = 0x1000;
}
void setupBacktraces(AllocationMetadata *Meta) {
Meta->AllocationTrace.TraceSize = gwp_asan::compression::pack(
BacktraceConstants, kNumBacktraceConstants,
Meta->AllocationTrace.CompressedTrace,
AllocationMetadata::kStackFrameStorageBytes);
if (Meta->IsDeallocated)
Meta->DeallocationTrace.TraceSize = gwp_asan::compression::pack(
BacktraceConstants, kNumBacktraceConstants,
Meta->DeallocationTrace.CompressedTrace,
AllocationMetadata::kStackFrameStorageBytes);
}
void checkBacktrace(const AllocationMetadata *Meta, bool IsDeallocated) {
uintptr_t Buffer[kNumBacktraceConstants];
size_t NumBacktraceConstants = kNumBacktraceConstants;
EXPECT_EQ(NumBacktraceConstants, __gwp_asan_get_allocation_trace(
Meta, Buffer, kNumBacktraceConstants));
for (size_t i = 0; i < kNumBacktraceConstants; ++i)
EXPECT_EQ(Buffer[i], BacktraceConstants[i]);
if (IsDeallocated) {
EXPECT_EQ(NumBacktraceConstants,
__gwp_asan_get_deallocation_trace(Meta, Buffer,
kNumBacktraceConstants));
for (size_t i = 0; i < kNumBacktraceConstants; ++i)
EXPECT_EQ(Buffer[i], BacktraceConstants[i]);
}
}
void checkMetadata(size_t Index, uintptr_t ErrorPtr) {
const AllocationMetadata *Meta =
__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].RequestedSize,
__gwp_asan_get_allocation_size(Meta));
EXPECT_EQ(Metadata[Index].AllocationTrace.ThreadID,
__gwp_asan_get_allocation_thread_id(Meta));
bool IsDeallocated = __gwp_asan_is_deallocated(Meta);
EXPECT_EQ(Metadata[Index].IsDeallocated, IsDeallocated);
checkBacktrace(Meta, IsDeallocated);
if (!IsDeallocated)
return;
EXPECT_EQ(Metadata[Index].DeallocationTrace.ThreadID,
__gwp_asan_get_deallocation_thread_id(Meta));
}
static constexpr size_t kNumBacktraceConstants = 4;
static uintptr_t BacktraceConstants[kNumBacktraceConstants];
AllocatorState State = {};
AllocationMetadata Metadata[4] = {};
uintptr_t InternalFaultAddr;
};
uintptr_t CrashHandlerAPITest::BacktraceConstants[kNumBacktraceConstants] = {
0xdeadbeef, 0xdeadc0de, 0xbadc0ffe, 0xcafef00d};
TEST_F(CrashHandlerAPITest, PointerNotMine) {
uintptr_t UnknownPtr = reinterpret_cast<uintptr_t>(&State);
EXPECT_FALSE(__gwp_asan_error_is_mine(&State, 0));
EXPECT_FALSE(__gwp_asan_error_is_mine(&State, UnknownPtr));
EXPECT_EQ(Error::UNKNOWN, __gwp_asan_diagnose_error(&State, Metadata, 0));
EXPECT_EQ(Error::UNKNOWN,
__gwp_asan_diagnose_error(&State, Metadata, UnknownPtr));
EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, 0));
EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, UnknownPtr));
}
TEST_F(CrashHandlerAPITest, PointerNotAllocated) {
uintptr_t FailureAddress = 0x9000;
EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress));
EXPECT_EQ(Error::UNKNOWN,
__gwp_asan_diagnose_error(&State, Metadata, FailureAddress));
EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress));
EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, FailureAddress));
}
TEST_F(CrashHandlerAPITest, DoubleFree) {
size_t Index =
metadata(/* Addr */ 0x7000, /* Size */ 0x20, /* IsDeallocated */ true);
uintptr_t FailureAddress = 0x7000;
State.FailureType = Error::DOUBLE_FREE;
State.FailureAddress = FailureAddress;
EXPECT_TRUE(__gwp_asan_error_is_mine(&State));
EXPECT_EQ(Error::DOUBLE_FREE,
__gwp_asan_diagnose_error(&State, Metadata, 0x0));
EXPECT_EQ(FailureAddress,
__gwp_asan_get_internal_crash_address(&State, InternalFaultAddr));
checkMetadata(Index, FailureAddress);
}
TEST_F(CrashHandlerAPITest, InvalidFree) {
size_t Index =
metadata(/* Addr */ 0x7000, /* Size */ 0x20, /* IsDeallocated */ false);
uintptr_t FailureAddress = 0x7001;
State.FailureType = Error::INVALID_FREE;
State.FailureAddress = FailureAddress;
EXPECT_TRUE(__gwp_asan_error_is_mine(&State));
EXPECT_EQ(Error::INVALID_FREE,
__gwp_asan_diagnose_error(&State, Metadata, 0x0));
EXPECT_EQ(FailureAddress,
__gwp_asan_get_internal_crash_address(&State, InternalFaultAddr));
checkMetadata(Index, FailureAddress);
}
TEST_F(CrashHandlerAPITest, InvalidFreeNoMetadata) {
uintptr_t FailureAddress = 0x7001;
State.FailureType = Error::INVALID_FREE;
State.FailureAddress = FailureAddress;
EXPECT_TRUE(__gwp_asan_error_is_mine(&State));
EXPECT_EQ(Error::INVALID_FREE,
__gwp_asan_diagnose_error(&State, Metadata, 0x0));
EXPECT_EQ(FailureAddress,
__gwp_asan_get_internal_crash_address(&State, InternalFaultAddr));
EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, FailureAddress));
}
TEST_F(CrashHandlerAPITest, UseAfterFree) {
size_t Index =
metadata(/* Addr */ 0x7000, /* Size */ 0x20, /* IsDeallocated */ true);
uintptr_t FailureAddress = 0x7001;
EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress));
EXPECT_EQ(Error::USE_AFTER_FREE,
__gwp_asan_diagnose_error(&State, Metadata, FailureAddress));
EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress));
checkMetadata(Index, FailureAddress);
}
TEST_F(CrashHandlerAPITest, BufferOverflow) {
size_t Index =
metadata(/* Addr */ 0x5f00, /* Size */ 0x100, /* IsDeallocated */ false);
uintptr_t FailureAddress = 0x6000;
EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress));
EXPECT_EQ(Error::BUFFER_OVERFLOW,
__gwp_asan_diagnose_error(&State, Metadata, FailureAddress));
EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress));
checkMetadata(Index, FailureAddress);
}
TEST_F(CrashHandlerAPITest, BufferUnderflow) {
size_t Index =
metadata(/* Addr */ 0x3000, /* Size */ 0x10, /* IsDeallocated*/ false);
uintptr_t FailureAddress = 0x2fff;
EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress));
EXPECT_EQ(Error::BUFFER_UNDERFLOW,
__gwp_asan_diagnose_error(&State, Metadata, FailureAddress));
EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress));
checkMetadata(Index, FailureAddress);
}