forked from OSchip/llvm-project
209 lines
7.7 KiB
C++
209 lines
7.7 KiB
C++
//===-- 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].Size = 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 = 0xb000;
|
|
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].Size, __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 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));
|
|
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));
|
|
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));
|
|
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));
|
|
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));
|
|
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));
|
|
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));
|
|
checkMetadata(Index, FailureAddress);
|
|
}
|