11f9cb04fSpatrick //===-- crash_handler_api.cpp -----------------------------------*- C++ -*-===//
21f9cb04fSpatrick //
31f9cb04fSpatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
41f9cb04fSpatrick // See https://llvm.org/LICENSE.txt for license information.
51f9cb04fSpatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61f9cb04fSpatrick //
71f9cb04fSpatrick //===----------------------------------------------------------------------===//
81f9cb04fSpatrick
91f9cb04fSpatrick #include "gwp_asan/crash_handler.h"
101f9cb04fSpatrick #include "gwp_asan/guarded_pool_allocator.h"
111f9cb04fSpatrick #include "gwp_asan/stack_trace_compressor.h"
121f9cb04fSpatrick #include "gwp_asan/tests/harness.h"
131f9cb04fSpatrick
141f9cb04fSpatrick using Error = gwp_asan::Error;
151f9cb04fSpatrick using GuardedPoolAllocator = gwp_asan::GuardedPoolAllocator;
161f9cb04fSpatrick using AllocationMetadata = gwp_asan::AllocationMetadata;
171f9cb04fSpatrick using AllocatorState = gwp_asan::AllocatorState;
181f9cb04fSpatrick
19d89ec533Spatrick class CrashHandlerAPITest : public Test {
201f9cb04fSpatrick public:
SetUp()211f9cb04fSpatrick void SetUp() override { setupState(); }
221f9cb04fSpatrick
231f9cb04fSpatrick protected:
metadata(uintptr_t Addr,uintptr_t Size,bool IsDeallocated)241f9cb04fSpatrick size_t metadata(uintptr_t Addr, uintptr_t Size, bool IsDeallocated) {
251f9cb04fSpatrick // Should only be allocating the 0x3000, 0x5000, 0x7000, 0x9000 pages.
261f9cb04fSpatrick EXPECT_GE(Addr, 0x3000u);
271f9cb04fSpatrick EXPECT_LT(Addr, 0xa000u);
281f9cb04fSpatrick
291f9cb04fSpatrick size_t Slot = State.getNearestSlot(Addr);
301f9cb04fSpatrick
311f9cb04fSpatrick Metadata[Slot].Addr = Addr;
32d89ec533Spatrick Metadata[Slot].RequestedSize = Size;
331f9cb04fSpatrick Metadata[Slot].IsDeallocated = IsDeallocated;
341f9cb04fSpatrick Metadata[Slot].AllocationTrace.ThreadID = 123;
351f9cb04fSpatrick Metadata[Slot].DeallocationTrace.ThreadID = 321;
361f9cb04fSpatrick setupBacktraces(&Metadata[Slot]);
371f9cb04fSpatrick
381f9cb04fSpatrick return Slot;
391f9cb04fSpatrick }
401f9cb04fSpatrick
setupState()411f9cb04fSpatrick void setupState() {
421f9cb04fSpatrick State.GuardedPagePool = 0x2000;
43*810390e3Srobert State.GuardedPagePoolEnd = 0xc000;
44*810390e3Srobert InternalFaultAddr = State.GuardedPagePoolEnd - 0x10;
451f9cb04fSpatrick State.MaxSimultaneousAllocations = 4; // 0x3000, 0x5000, 0x7000, 0x9000.
461f9cb04fSpatrick State.PageSize = 0x1000;
471f9cb04fSpatrick }
481f9cb04fSpatrick
setupBacktraces(AllocationMetadata * Meta)491f9cb04fSpatrick void setupBacktraces(AllocationMetadata *Meta) {
501f9cb04fSpatrick Meta->AllocationTrace.TraceSize = gwp_asan::compression::pack(
511f9cb04fSpatrick BacktraceConstants, kNumBacktraceConstants,
521f9cb04fSpatrick Meta->AllocationTrace.CompressedTrace,
531f9cb04fSpatrick AllocationMetadata::kStackFrameStorageBytes);
541f9cb04fSpatrick
551f9cb04fSpatrick if (Meta->IsDeallocated)
561f9cb04fSpatrick Meta->DeallocationTrace.TraceSize = gwp_asan::compression::pack(
571f9cb04fSpatrick BacktraceConstants, kNumBacktraceConstants,
581f9cb04fSpatrick Meta->DeallocationTrace.CompressedTrace,
591f9cb04fSpatrick AllocationMetadata::kStackFrameStorageBytes);
601f9cb04fSpatrick }
611f9cb04fSpatrick
checkBacktrace(const AllocationMetadata * Meta,bool IsDeallocated)621f9cb04fSpatrick void checkBacktrace(const AllocationMetadata *Meta, bool IsDeallocated) {
631f9cb04fSpatrick uintptr_t Buffer[kNumBacktraceConstants];
641f9cb04fSpatrick size_t NumBacktraceConstants = kNumBacktraceConstants;
651f9cb04fSpatrick EXPECT_EQ(NumBacktraceConstants, __gwp_asan_get_allocation_trace(
661f9cb04fSpatrick Meta, Buffer, kNumBacktraceConstants));
671f9cb04fSpatrick for (size_t i = 0; i < kNumBacktraceConstants; ++i)
681f9cb04fSpatrick EXPECT_EQ(Buffer[i], BacktraceConstants[i]);
691f9cb04fSpatrick
701f9cb04fSpatrick if (IsDeallocated) {
711f9cb04fSpatrick EXPECT_EQ(NumBacktraceConstants,
721f9cb04fSpatrick __gwp_asan_get_deallocation_trace(Meta, Buffer,
731f9cb04fSpatrick kNumBacktraceConstants));
741f9cb04fSpatrick for (size_t i = 0; i < kNumBacktraceConstants; ++i)
751f9cb04fSpatrick EXPECT_EQ(Buffer[i], BacktraceConstants[i]);
761f9cb04fSpatrick }
771f9cb04fSpatrick }
781f9cb04fSpatrick
checkMetadata(size_t Index,uintptr_t ErrorPtr)791f9cb04fSpatrick void checkMetadata(size_t Index, uintptr_t ErrorPtr) {
801f9cb04fSpatrick const AllocationMetadata *Meta =
811f9cb04fSpatrick __gwp_asan_get_metadata(&State, Metadata, ErrorPtr);
821f9cb04fSpatrick EXPECT_NE(nullptr, Meta);
831f9cb04fSpatrick EXPECT_EQ(Metadata[Index].Addr, __gwp_asan_get_allocation_address(Meta));
84d89ec533Spatrick EXPECT_EQ(Metadata[Index].RequestedSize,
85d89ec533Spatrick __gwp_asan_get_allocation_size(Meta));
861f9cb04fSpatrick EXPECT_EQ(Metadata[Index].AllocationTrace.ThreadID,
871f9cb04fSpatrick __gwp_asan_get_allocation_thread_id(Meta));
881f9cb04fSpatrick
891f9cb04fSpatrick bool IsDeallocated = __gwp_asan_is_deallocated(Meta);
901f9cb04fSpatrick EXPECT_EQ(Metadata[Index].IsDeallocated, IsDeallocated);
911f9cb04fSpatrick checkBacktrace(Meta, IsDeallocated);
921f9cb04fSpatrick
931f9cb04fSpatrick if (!IsDeallocated)
941f9cb04fSpatrick return;
951f9cb04fSpatrick
961f9cb04fSpatrick EXPECT_EQ(Metadata[Index].DeallocationTrace.ThreadID,
971f9cb04fSpatrick __gwp_asan_get_deallocation_thread_id(Meta));
981f9cb04fSpatrick }
991f9cb04fSpatrick
1001f9cb04fSpatrick static constexpr size_t kNumBacktraceConstants = 4;
1011f9cb04fSpatrick static uintptr_t BacktraceConstants[kNumBacktraceConstants];
1021f9cb04fSpatrick AllocatorState State = {};
1031f9cb04fSpatrick AllocationMetadata Metadata[4] = {};
104*810390e3Srobert uintptr_t InternalFaultAddr;
1051f9cb04fSpatrick };
1061f9cb04fSpatrick
1071f9cb04fSpatrick uintptr_t CrashHandlerAPITest::BacktraceConstants[kNumBacktraceConstants] = {
1081f9cb04fSpatrick 0xdeadbeef, 0xdeadc0de, 0xbadc0ffe, 0xcafef00d};
1091f9cb04fSpatrick
TEST_F(CrashHandlerAPITest,PointerNotMine)1101f9cb04fSpatrick TEST_F(CrashHandlerAPITest, PointerNotMine) {
1111f9cb04fSpatrick uintptr_t UnknownPtr = reinterpret_cast<uintptr_t>(&State);
1121f9cb04fSpatrick
1131f9cb04fSpatrick EXPECT_FALSE(__gwp_asan_error_is_mine(&State, 0));
1141f9cb04fSpatrick EXPECT_FALSE(__gwp_asan_error_is_mine(&State, UnknownPtr));
1151f9cb04fSpatrick
1161f9cb04fSpatrick EXPECT_EQ(Error::UNKNOWN, __gwp_asan_diagnose_error(&State, Metadata, 0));
1171f9cb04fSpatrick EXPECT_EQ(Error::UNKNOWN,
1181f9cb04fSpatrick __gwp_asan_diagnose_error(&State, Metadata, UnknownPtr));
1191f9cb04fSpatrick
1201f9cb04fSpatrick EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, 0));
1211f9cb04fSpatrick EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, UnknownPtr));
1221f9cb04fSpatrick }
1231f9cb04fSpatrick
TEST_F(CrashHandlerAPITest,PointerNotAllocated)1241f9cb04fSpatrick TEST_F(CrashHandlerAPITest, PointerNotAllocated) {
1251f9cb04fSpatrick uintptr_t FailureAddress = 0x9000;
1261f9cb04fSpatrick
1271f9cb04fSpatrick EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress));
1281f9cb04fSpatrick EXPECT_EQ(Error::UNKNOWN,
1291f9cb04fSpatrick __gwp_asan_diagnose_error(&State, Metadata, FailureAddress));
130*810390e3Srobert EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress));
1311f9cb04fSpatrick EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, FailureAddress));
1321f9cb04fSpatrick }
1331f9cb04fSpatrick
TEST_F(CrashHandlerAPITest,DoubleFree)1341f9cb04fSpatrick TEST_F(CrashHandlerAPITest, DoubleFree) {
1351f9cb04fSpatrick size_t Index =
1361f9cb04fSpatrick metadata(/* Addr */ 0x7000, /* Size */ 0x20, /* IsDeallocated */ true);
1371f9cb04fSpatrick uintptr_t FailureAddress = 0x7000;
1381f9cb04fSpatrick
1391f9cb04fSpatrick State.FailureType = Error::DOUBLE_FREE;
1401f9cb04fSpatrick State.FailureAddress = FailureAddress;
1411f9cb04fSpatrick
1421f9cb04fSpatrick EXPECT_TRUE(__gwp_asan_error_is_mine(&State));
1431f9cb04fSpatrick EXPECT_EQ(Error::DOUBLE_FREE,
1441f9cb04fSpatrick __gwp_asan_diagnose_error(&State, Metadata, 0x0));
145*810390e3Srobert EXPECT_EQ(FailureAddress,
146*810390e3Srobert __gwp_asan_get_internal_crash_address(&State, InternalFaultAddr));
1471f9cb04fSpatrick checkMetadata(Index, FailureAddress);
1481f9cb04fSpatrick }
1491f9cb04fSpatrick
TEST_F(CrashHandlerAPITest,InvalidFree)1501f9cb04fSpatrick TEST_F(CrashHandlerAPITest, InvalidFree) {
1511f9cb04fSpatrick size_t Index =
1521f9cb04fSpatrick metadata(/* Addr */ 0x7000, /* Size */ 0x20, /* IsDeallocated */ false);
1531f9cb04fSpatrick uintptr_t FailureAddress = 0x7001;
1541f9cb04fSpatrick
1551f9cb04fSpatrick State.FailureType = Error::INVALID_FREE;
1561f9cb04fSpatrick State.FailureAddress = FailureAddress;
1571f9cb04fSpatrick
1581f9cb04fSpatrick EXPECT_TRUE(__gwp_asan_error_is_mine(&State));
1591f9cb04fSpatrick EXPECT_EQ(Error::INVALID_FREE,
1601f9cb04fSpatrick __gwp_asan_diagnose_error(&State, Metadata, 0x0));
161*810390e3Srobert EXPECT_EQ(FailureAddress,
162*810390e3Srobert __gwp_asan_get_internal_crash_address(&State, InternalFaultAddr));
1631f9cb04fSpatrick checkMetadata(Index, FailureAddress);
1641f9cb04fSpatrick }
1651f9cb04fSpatrick
TEST_F(CrashHandlerAPITest,InvalidFreeNoMetadata)1661f9cb04fSpatrick TEST_F(CrashHandlerAPITest, InvalidFreeNoMetadata) {
1671f9cb04fSpatrick uintptr_t FailureAddress = 0x7001;
1681f9cb04fSpatrick
1691f9cb04fSpatrick State.FailureType = Error::INVALID_FREE;
1701f9cb04fSpatrick State.FailureAddress = FailureAddress;
1711f9cb04fSpatrick
1721f9cb04fSpatrick EXPECT_TRUE(__gwp_asan_error_is_mine(&State));
1731f9cb04fSpatrick EXPECT_EQ(Error::INVALID_FREE,
1741f9cb04fSpatrick __gwp_asan_diagnose_error(&State, Metadata, 0x0));
175*810390e3Srobert EXPECT_EQ(FailureAddress,
176*810390e3Srobert __gwp_asan_get_internal_crash_address(&State, InternalFaultAddr));
1771f9cb04fSpatrick EXPECT_EQ(nullptr, __gwp_asan_get_metadata(&State, Metadata, FailureAddress));
1781f9cb04fSpatrick }
1791f9cb04fSpatrick
TEST_F(CrashHandlerAPITest,UseAfterFree)1801f9cb04fSpatrick TEST_F(CrashHandlerAPITest, UseAfterFree) {
1811f9cb04fSpatrick size_t Index =
1821f9cb04fSpatrick metadata(/* Addr */ 0x7000, /* Size */ 0x20, /* IsDeallocated */ true);
1831f9cb04fSpatrick uintptr_t FailureAddress = 0x7001;
1841f9cb04fSpatrick
1851f9cb04fSpatrick EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress));
1861f9cb04fSpatrick EXPECT_EQ(Error::USE_AFTER_FREE,
1871f9cb04fSpatrick __gwp_asan_diagnose_error(&State, Metadata, FailureAddress));
188*810390e3Srobert EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress));
1891f9cb04fSpatrick checkMetadata(Index, FailureAddress);
1901f9cb04fSpatrick }
1911f9cb04fSpatrick
TEST_F(CrashHandlerAPITest,BufferOverflow)1921f9cb04fSpatrick TEST_F(CrashHandlerAPITest, BufferOverflow) {
1931f9cb04fSpatrick size_t Index =
1941f9cb04fSpatrick metadata(/* Addr */ 0x5f00, /* Size */ 0x100, /* IsDeallocated */ false);
1951f9cb04fSpatrick uintptr_t FailureAddress = 0x6000;
1961f9cb04fSpatrick
1971f9cb04fSpatrick EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress));
1981f9cb04fSpatrick EXPECT_EQ(Error::BUFFER_OVERFLOW,
1991f9cb04fSpatrick __gwp_asan_diagnose_error(&State, Metadata, FailureAddress));
200*810390e3Srobert EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress));
2011f9cb04fSpatrick checkMetadata(Index, FailureAddress);
2021f9cb04fSpatrick }
2031f9cb04fSpatrick
TEST_F(CrashHandlerAPITest,BufferUnderflow)2041f9cb04fSpatrick TEST_F(CrashHandlerAPITest, BufferUnderflow) {
2051f9cb04fSpatrick size_t Index =
2061f9cb04fSpatrick metadata(/* Addr */ 0x3000, /* Size */ 0x10, /* IsDeallocated*/ false);
2071f9cb04fSpatrick uintptr_t FailureAddress = 0x2fff;
2081f9cb04fSpatrick
2091f9cb04fSpatrick EXPECT_TRUE(__gwp_asan_error_is_mine(&State, FailureAddress));
2101f9cb04fSpatrick EXPECT_EQ(Error::BUFFER_UNDERFLOW,
2111f9cb04fSpatrick __gwp_asan_diagnose_error(&State, Metadata, FailureAddress));
212*810390e3Srobert EXPECT_EQ(0u, __gwp_asan_get_internal_crash_address(&State, FailureAddress));
2131f9cb04fSpatrick checkMetadata(Index, FailureAddress);
2141f9cb04fSpatrick }
215