1d89ec533Spatrick //===-- crash_handler.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/common.h"
101f9cb04fSpatrick #include "gwp_asan/stack_trace_compressor.h"
111f9cb04fSpatrick
121f9cb04fSpatrick #include <assert.h>
13d89ec533Spatrick #include <stdint.h>
14d89ec533Spatrick #include <string.h>
151f9cb04fSpatrick
161f9cb04fSpatrick using AllocationMetadata = gwp_asan::AllocationMetadata;
171f9cb04fSpatrick using Error = gwp_asan::Error;
181f9cb04fSpatrick
191f9cb04fSpatrick #ifdef __cplusplus
201f9cb04fSpatrick extern "C" {
211f9cb04fSpatrick #endif
221f9cb04fSpatrick
__gwp_asan_error_is_mine(const gwp_asan::AllocatorState * State,uintptr_t ErrorPtr)231f9cb04fSpatrick bool __gwp_asan_error_is_mine(const gwp_asan::AllocatorState *State,
241f9cb04fSpatrick uintptr_t ErrorPtr) {
251f9cb04fSpatrick assert(State && "State should not be nullptr.");
261f9cb04fSpatrick if (State->FailureType != Error::UNKNOWN && State->FailureAddress != 0)
271f9cb04fSpatrick return true;
281f9cb04fSpatrick
291f9cb04fSpatrick return ErrorPtr < State->GuardedPagePoolEnd &&
301f9cb04fSpatrick State->GuardedPagePool <= ErrorPtr;
311f9cb04fSpatrick }
321f9cb04fSpatrick
331f9cb04fSpatrick uintptr_t
__gwp_asan_get_internal_crash_address(const gwp_asan::AllocatorState * State,uintptr_t ErrorPtr)34*810390e3Srobert __gwp_asan_get_internal_crash_address(const gwp_asan::AllocatorState *State,
35*810390e3Srobert uintptr_t ErrorPtr) {
36*810390e3Srobert // There can be a race between internally- and externally-raised faults. The
37*810390e3Srobert // fault address from the signal handler is used to discriminate whether it's
38*810390e3Srobert // internally- or externally-raised, and the pool maintains a special page at
39*810390e3Srobert // the end of the GuardedPagePool specifically for the internally-raised
40*810390e3Srobert // faults.
41*810390e3Srobert if (ErrorPtr != State->internallyDetectedErrorFaultAddress())
42*810390e3Srobert return 0u;
431f9cb04fSpatrick return State->FailureAddress;
441f9cb04fSpatrick }
451f9cb04fSpatrick
461f9cb04fSpatrick static const AllocationMetadata *
addrToMetadata(const gwp_asan::AllocatorState * State,const AllocationMetadata * Metadata,uintptr_t Ptr)471f9cb04fSpatrick addrToMetadata(const gwp_asan::AllocatorState *State,
481f9cb04fSpatrick const AllocationMetadata *Metadata, uintptr_t Ptr) {
491f9cb04fSpatrick // Note - Similar implementation in guarded_pool_allocator.cpp.
501f9cb04fSpatrick return &Metadata[State->getNearestSlot(Ptr)];
511f9cb04fSpatrick }
521f9cb04fSpatrick
531f9cb04fSpatrick gwp_asan::Error
__gwp_asan_diagnose_error(const gwp_asan::AllocatorState * State,const gwp_asan::AllocationMetadata * Metadata,uintptr_t ErrorPtr)541f9cb04fSpatrick __gwp_asan_diagnose_error(const gwp_asan::AllocatorState *State,
551f9cb04fSpatrick const gwp_asan::AllocationMetadata *Metadata,
561f9cb04fSpatrick uintptr_t ErrorPtr) {
571f9cb04fSpatrick if (!__gwp_asan_error_is_mine(State, ErrorPtr))
581f9cb04fSpatrick return Error::UNKNOWN;
591f9cb04fSpatrick
601f9cb04fSpatrick if (State->FailureType != Error::UNKNOWN)
611f9cb04fSpatrick return State->FailureType;
621f9cb04fSpatrick
63*810390e3Srobert // Check for use-after-free.
64*810390e3Srobert if (addrToMetadata(State, Metadata, ErrorPtr)->IsDeallocated)
65*810390e3Srobert return Error::USE_AFTER_FREE;
66*810390e3Srobert
67*810390e3Srobert // Check for buffer-overflow. Because of allocation alignment or left/right
68*810390e3Srobert // page placement, we can have buffer-overflows that don't touch a guarded
69*810390e3Srobert // page, but these are not possible to detect unless it's also a
70*810390e3Srobert // use-after-free, which is handled above.
711f9cb04fSpatrick if (State->isGuardPage(ErrorPtr)) {
721f9cb04fSpatrick size_t Slot = State->getNearestSlot(ErrorPtr);
731f9cb04fSpatrick const AllocationMetadata *SlotMeta =
741f9cb04fSpatrick addrToMetadata(State, Metadata, State->slotToAddr(Slot));
751f9cb04fSpatrick
761f9cb04fSpatrick // Ensure that this slot was allocated once upon a time.
771f9cb04fSpatrick if (!SlotMeta->Addr)
781f9cb04fSpatrick return Error::UNKNOWN;
791f9cb04fSpatrick
801f9cb04fSpatrick if (SlotMeta->Addr < ErrorPtr)
811f9cb04fSpatrick return Error::BUFFER_OVERFLOW;
821f9cb04fSpatrick return Error::BUFFER_UNDERFLOW;
831f9cb04fSpatrick }
841f9cb04fSpatrick
851f9cb04fSpatrick // If we have reached here, the error is still unknown.
861f9cb04fSpatrick return Error::UNKNOWN;
871f9cb04fSpatrick }
881f9cb04fSpatrick
891f9cb04fSpatrick const gwp_asan::AllocationMetadata *
__gwp_asan_get_metadata(const gwp_asan::AllocatorState * State,const gwp_asan::AllocationMetadata * Metadata,uintptr_t ErrorPtr)901f9cb04fSpatrick __gwp_asan_get_metadata(const gwp_asan::AllocatorState *State,
911f9cb04fSpatrick const gwp_asan::AllocationMetadata *Metadata,
921f9cb04fSpatrick uintptr_t ErrorPtr) {
931f9cb04fSpatrick if (!__gwp_asan_error_is_mine(State, ErrorPtr))
941f9cb04fSpatrick return nullptr;
951f9cb04fSpatrick
961f9cb04fSpatrick if (ErrorPtr >= State->GuardedPagePoolEnd ||
971f9cb04fSpatrick State->GuardedPagePool > ErrorPtr)
981f9cb04fSpatrick return nullptr;
991f9cb04fSpatrick
1001f9cb04fSpatrick const AllocationMetadata *Meta = addrToMetadata(State, Metadata, ErrorPtr);
1011f9cb04fSpatrick if (Meta->Addr == 0)
1021f9cb04fSpatrick return nullptr;
1031f9cb04fSpatrick
1041f9cb04fSpatrick return Meta;
1051f9cb04fSpatrick }
1061f9cb04fSpatrick
__gwp_asan_get_allocation_address(const gwp_asan::AllocationMetadata * AllocationMeta)1071f9cb04fSpatrick uintptr_t __gwp_asan_get_allocation_address(
1081f9cb04fSpatrick const gwp_asan::AllocationMetadata *AllocationMeta) {
1091f9cb04fSpatrick return AllocationMeta->Addr;
1101f9cb04fSpatrick }
1111f9cb04fSpatrick
__gwp_asan_get_allocation_size(const gwp_asan::AllocationMetadata * AllocationMeta)1121f9cb04fSpatrick size_t __gwp_asan_get_allocation_size(
1131f9cb04fSpatrick const gwp_asan::AllocationMetadata *AllocationMeta) {
114d89ec533Spatrick return AllocationMeta->RequestedSize;
1151f9cb04fSpatrick }
1161f9cb04fSpatrick
__gwp_asan_get_allocation_thread_id(const gwp_asan::AllocationMetadata * AllocationMeta)1171f9cb04fSpatrick uint64_t __gwp_asan_get_allocation_thread_id(
1181f9cb04fSpatrick const gwp_asan::AllocationMetadata *AllocationMeta) {
1191f9cb04fSpatrick return AllocationMeta->AllocationTrace.ThreadID;
1201f9cb04fSpatrick }
1211f9cb04fSpatrick
__gwp_asan_get_allocation_trace(const gwp_asan::AllocationMetadata * AllocationMeta,uintptr_t * Buffer,size_t BufferLen)1221f9cb04fSpatrick size_t __gwp_asan_get_allocation_trace(
1231f9cb04fSpatrick const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,
1241f9cb04fSpatrick size_t BufferLen) {
125d89ec533Spatrick uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];
126d89ec533Spatrick size_t UnpackedLength = gwp_asan::compression::unpack(
1271f9cb04fSpatrick AllocationMeta->AllocationTrace.CompressedTrace,
128d89ec533Spatrick AllocationMeta->AllocationTrace.TraceSize, UncompressedBuffer,
129d89ec533Spatrick AllocationMetadata::kMaxTraceLengthToCollect);
130d89ec533Spatrick if (UnpackedLength < BufferLen)
131d89ec533Spatrick BufferLen = UnpackedLength;
132d89ec533Spatrick memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));
133d89ec533Spatrick return UnpackedLength;
1341f9cb04fSpatrick }
1351f9cb04fSpatrick
__gwp_asan_is_deallocated(const gwp_asan::AllocationMetadata * AllocationMeta)1361f9cb04fSpatrick bool __gwp_asan_is_deallocated(
1371f9cb04fSpatrick const gwp_asan::AllocationMetadata *AllocationMeta) {
1381f9cb04fSpatrick return AllocationMeta->IsDeallocated;
1391f9cb04fSpatrick }
1401f9cb04fSpatrick
__gwp_asan_get_deallocation_thread_id(const gwp_asan::AllocationMetadata * AllocationMeta)1411f9cb04fSpatrick uint64_t __gwp_asan_get_deallocation_thread_id(
1421f9cb04fSpatrick const gwp_asan::AllocationMetadata *AllocationMeta) {
1431f9cb04fSpatrick return AllocationMeta->DeallocationTrace.ThreadID;
1441f9cb04fSpatrick }
1451f9cb04fSpatrick
__gwp_asan_get_deallocation_trace(const gwp_asan::AllocationMetadata * AllocationMeta,uintptr_t * Buffer,size_t BufferLen)1461f9cb04fSpatrick size_t __gwp_asan_get_deallocation_trace(
1471f9cb04fSpatrick const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,
1481f9cb04fSpatrick size_t BufferLen) {
149d89ec533Spatrick uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];
150d89ec533Spatrick size_t UnpackedLength = gwp_asan::compression::unpack(
1511f9cb04fSpatrick AllocationMeta->DeallocationTrace.CompressedTrace,
152d89ec533Spatrick AllocationMeta->DeallocationTrace.TraceSize, UncompressedBuffer,
153d89ec533Spatrick AllocationMetadata::kMaxTraceLengthToCollect);
154d89ec533Spatrick if (UnpackedLength < BufferLen)
155d89ec533Spatrick BufferLen = UnpackedLength;
156d89ec533Spatrick memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));
157d89ec533Spatrick return UnpackedLength;
1581f9cb04fSpatrick }
1591f9cb04fSpatrick
1601f9cb04fSpatrick #ifdef __cplusplus
1611f9cb04fSpatrick } // extern "C"
1621f9cb04fSpatrick #endif
163