//===-- 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 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 = 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].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 CrashHandlerAPITest::BacktraceConstants[kNumBacktraceConstants] = { 0xdeadbeef, 0xdeadc0de, 0xbadc0ffe, 0xcafef00d}; TEST_F(CrashHandlerAPITest, PointerNotMine) { uintptr_t UnknownPtr = reinterpret_cast(&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); }