xref: /openbsd-src/gnu/llvm/compiler-rt/lib/gwp_asan/crash_handler.cpp (revision 810390e339a5425391477d5d41c78d7cab2424ac)
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