xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/gwp_asan/crash_handler.cpp (revision bdd1243df58e60e85101c09001d9812a789b6bc4)
1e8d8bef9SDimitry Andric //===-- crash_handler.cpp ---------------------------------------*- C++ -*-===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric 
95ffd83dbSDimitry Andric #include "gwp_asan/common.h"
105ffd83dbSDimitry Andric #include "gwp_asan/stack_trace_compressor.h"
115ffd83dbSDimitry Andric 
125ffd83dbSDimitry Andric #include <assert.h>
13e8d8bef9SDimitry Andric #include <stdint.h>
14e8d8bef9SDimitry Andric #include <string.h>
155ffd83dbSDimitry Andric 
165ffd83dbSDimitry Andric using AllocationMetadata = gwp_asan::AllocationMetadata;
175ffd83dbSDimitry Andric using Error = gwp_asan::Error;
185ffd83dbSDimitry Andric 
195ffd83dbSDimitry Andric #ifdef __cplusplus
205ffd83dbSDimitry Andric extern "C" {
215ffd83dbSDimitry Andric #endif
225ffd83dbSDimitry Andric 
__gwp_asan_error_is_mine(const gwp_asan::AllocatorState * State,uintptr_t ErrorPtr)235ffd83dbSDimitry Andric bool __gwp_asan_error_is_mine(const gwp_asan::AllocatorState *State,
245ffd83dbSDimitry Andric                               uintptr_t ErrorPtr) {
255ffd83dbSDimitry Andric   assert(State && "State should not be nullptr.");
265ffd83dbSDimitry Andric   if (State->FailureType != Error::UNKNOWN && State->FailureAddress != 0)
275ffd83dbSDimitry Andric     return true;
285ffd83dbSDimitry Andric 
295ffd83dbSDimitry Andric   return ErrorPtr < State->GuardedPagePoolEnd &&
305ffd83dbSDimitry Andric          State->GuardedPagePool <= ErrorPtr;
315ffd83dbSDimitry Andric }
325ffd83dbSDimitry Andric 
335ffd83dbSDimitry Andric uintptr_t
__gwp_asan_get_internal_crash_address(const gwp_asan::AllocatorState * State,uintptr_t ErrorPtr)34*bdd1243dSDimitry Andric __gwp_asan_get_internal_crash_address(const gwp_asan::AllocatorState *State,
35*bdd1243dSDimitry Andric                                       uintptr_t ErrorPtr) {
36*bdd1243dSDimitry Andric   // There can be a race between internally- and externally-raised faults. The
37*bdd1243dSDimitry Andric   // fault address from the signal handler is used to discriminate whether it's
38*bdd1243dSDimitry Andric   // internally- or externally-raised, and the pool maintains a special page at
39*bdd1243dSDimitry Andric   // the end of the GuardedPagePool specifically for the internally-raised
40*bdd1243dSDimitry Andric   // faults.
41*bdd1243dSDimitry Andric   if (ErrorPtr != State->internallyDetectedErrorFaultAddress())
42*bdd1243dSDimitry Andric     return 0u;
435ffd83dbSDimitry Andric   return State->FailureAddress;
445ffd83dbSDimitry Andric }
455ffd83dbSDimitry Andric 
465ffd83dbSDimitry Andric static const AllocationMetadata *
addrToMetadata(const gwp_asan::AllocatorState * State,const AllocationMetadata * Metadata,uintptr_t Ptr)475ffd83dbSDimitry Andric addrToMetadata(const gwp_asan::AllocatorState *State,
485ffd83dbSDimitry Andric                const AllocationMetadata *Metadata, uintptr_t Ptr) {
495ffd83dbSDimitry Andric   // Note - Similar implementation in guarded_pool_allocator.cpp.
505ffd83dbSDimitry Andric   return &Metadata[State->getNearestSlot(Ptr)];
515ffd83dbSDimitry Andric }
525ffd83dbSDimitry Andric 
535ffd83dbSDimitry Andric gwp_asan::Error
__gwp_asan_diagnose_error(const gwp_asan::AllocatorState * State,const gwp_asan::AllocationMetadata * Metadata,uintptr_t ErrorPtr)545ffd83dbSDimitry Andric __gwp_asan_diagnose_error(const gwp_asan::AllocatorState *State,
555ffd83dbSDimitry Andric                           const gwp_asan::AllocationMetadata *Metadata,
565ffd83dbSDimitry Andric                           uintptr_t ErrorPtr) {
575ffd83dbSDimitry Andric   if (!__gwp_asan_error_is_mine(State, ErrorPtr))
585ffd83dbSDimitry Andric     return Error::UNKNOWN;
595ffd83dbSDimitry Andric 
605ffd83dbSDimitry Andric   if (State->FailureType != Error::UNKNOWN)
615ffd83dbSDimitry Andric     return State->FailureType;
625ffd83dbSDimitry Andric 
63*bdd1243dSDimitry Andric   // Check for use-after-free.
64*bdd1243dSDimitry Andric   if (addrToMetadata(State, Metadata, ErrorPtr)->IsDeallocated)
65*bdd1243dSDimitry Andric     return Error::USE_AFTER_FREE;
66*bdd1243dSDimitry Andric 
67*bdd1243dSDimitry Andric   // Check for buffer-overflow. Because of allocation alignment or left/right
68*bdd1243dSDimitry Andric   // page placement, we can have buffer-overflows that don't touch a guarded
69*bdd1243dSDimitry Andric   // page, but these are not possible to detect unless it's also a
70*bdd1243dSDimitry Andric   // use-after-free, which is handled above.
715ffd83dbSDimitry Andric   if (State->isGuardPage(ErrorPtr)) {
725ffd83dbSDimitry Andric     size_t Slot = State->getNearestSlot(ErrorPtr);
735ffd83dbSDimitry Andric     const AllocationMetadata *SlotMeta =
745ffd83dbSDimitry Andric         addrToMetadata(State, Metadata, State->slotToAddr(Slot));
755ffd83dbSDimitry Andric 
765ffd83dbSDimitry Andric     // Ensure that this slot was allocated once upon a time.
775ffd83dbSDimitry Andric     if (!SlotMeta->Addr)
785ffd83dbSDimitry Andric       return Error::UNKNOWN;
795ffd83dbSDimitry Andric 
805ffd83dbSDimitry Andric     if (SlotMeta->Addr < ErrorPtr)
815ffd83dbSDimitry Andric       return Error::BUFFER_OVERFLOW;
825ffd83dbSDimitry Andric     return Error::BUFFER_UNDERFLOW;
835ffd83dbSDimitry Andric   }
845ffd83dbSDimitry Andric 
855ffd83dbSDimitry Andric   // If we have reached here, the error is still unknown.
865ffd83dbSDimitry Andric   return Error::UNKNOWN;
875ffd83dbSDimitry Andric }
885ffd83dbSDimitry Andric 
895ffd83dbSDimitry Andric const gwp_asan::AllocationMetadata *
__gwp_asan_get_metadata(const gwp_asan::AllocatorState * State,const gwp_asan::AllocationMetadata * Metadata,uintptr_t ErrorPtr)905ffd83dbSDimitry Andric __gwp_asan_get_metadata(const gwp_asan::AllocatorState *State,
915ffd83dbSDimitry Andric                         const gwp_asan::AllocationMetadata *Metadata,
925ffd83dbSDimitry Andric                         uintptr_t ErrorPtr) {
935ffd83dbSDimitry Andric   if (!__gwp_asan_error_is_mine(State, ErrorPtr))
945ffd83dbSDimitry Andric     return nullptr;
955ffd83dbSDimitry Andric 
965ffd83dbSDimitry Andric   if (ErrorPtr >= State->GuardedPagePoolEnd ||
975ffd83dbSDimitry Andric       State->GuardedPagePool > ErrorPtr)
985ffd83dbSDimitry Andric     return nullptr;
995ffd83dbSDimitry Andric 
1005ffd83dbSDimitry Andric   const AllocationMetadata *Meta = addrToMetadata(State, Metadata, ErrorPtr);
1015ffd83dbSDimitry Andric   if (Meta->Addr == 0)
1025ffd83dbSDimitry Andric     return nullptr;
1035ffd83dbSDimitry Andric 
1045ffd83dbSDimitry Andric   return Meta;
1055ffd83dbSDimitry Andric }
1065ffd83dbSDimitry Andric 
__gwp_asan_get_allocation_address(const gwp_asan::AllocationMetadata * AllocationMeta)1075ffd83dbSDimitry Andric uintptr_t __gwp_asan_get_allocation_address(
1085ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta) {
1095ffd83dbSDimitry Andric   return AllocationMeta->Addr;
1105ffd83dbSDimitry Andric }
1115ffd83dbSDimitry Andric 
__gwp_asan_get_allocation_size(const gwp_asan::AllocationMetadata * AllocationMeta)1125ffd83dbSDimitry Andric size_t __gwp_asan_get_allocation_size(
1135ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta) {
114fe6060f1SDimitry Andric   return AllocationMeta->RequestedSize;
1155ffd83dbSDimitry Andric }
1165ffd83dbSDimitry Andric 
__gwp_asan_get_allocation_thread_id(const gwp_asan::AllocationMetadata * AllocationMeta)1175ffd83dbSDimitry Andric uint64_t __gwp_asan_get_allocation_thread_id(
1185ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta) {
1195ffd83dbSDimitry Andric   return AllocationMeta->AllocationTrace.ThreadID;
1205ffd83dbSDimitry Andric }
1215ffd83dbSDimitry Andric 
__gwp_asan_get_allocation_trace(const gwp_asan::AllocationMetadata * AllocationMeta,uintptr_t * Buffer,size_t BufferLen)1225ffd83dbSDimitry Andric size_t __gwp_asan_get_allocation_trace(
1235ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,
1245ffd83dbSDimitry Andric     size_t BufferLen) {
125e8d8bef9SDimitry Andric   uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];
126e8d8bef9SDimitry Andric   size_t UnpackedLength = gwp_asan::compression::unpack(
1275ffd83dbSDimitry Andric       AllocationMeta->AllocationTrace.CompressedTrace,
128e8d8bef9SDimitry Andric       AllocationMeta->AllocationTrace.TraceSize, UncompressedBuffer,
129e8d8bef9SDimitry Andric       AllocationMetadata::kMaxTraceLengthToCollect);
130e8d8bef9SDimitry Andric   if (UnpackedLength < BufferLen)
131e8d8bef9SDimitry Andric     BufferLen = UnpackedLength;
132e8d8bef9SDimitry Andric   memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));
133e8d8bef9SDimitry Andric   return UnpackedLength;
1345ffd83dbSDimitry Andric }
1355ffd83dbSDimitry Andric 
__gwp_asan_is_deallocated(const gwp_asan::AllocationMetadata * AllocationMeta)1365ffd83dbSDimitry Andric bool __gwp_asan_is_deallocated(
1375ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta) {
1385ffd83dbSDimitry Andric   return AllocationMeta->IsDeallocated;
1395ffd83dbSDimitry Andric }
1405ffd83dbSDimitry Andric 
__gwp_asan_get_deallocation_thread_id(const gwp_asan::AllocationMetadata * AllocationMeta)1415ffd83dbSDimitry Andric uint64_t __gwp_asan_get_deallocation_thread_id(
1425ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta) {
1435ffd83dbSDimitry Andric   return AllocationMeta->DeallocationTrace.ThreadID;
1445ffd83dbSDimitry Andric }
1455ffd83dbSDimitry Andric 
__gwp_asan_get_deallocation_trace(const gwp_asan::AllocationMetadata * AllocationMeta,uintptr_t * Buffer,size_t BufferLen)1465ffd83dbSDimitry Andric size_t __gwp_asan_get_deallocation_trace(
1475ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,
1485ffd83dbSDimitry Andric     size_t BufferLen) {
149e8d8bef9SDimitry Andric   uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];
150e8d8bef9SDimitry Andric   size_t UnpackedLength = gwp_asan::compression::unpack(
1515ffd83dbSDimitry Andric       AllocationMeta->DeallocationTrace.CompressedTrace,
152e8d8bef9SDimitry Andric       AllocationMeta->DeallocationTrace.TraceSize, UncompressedBuffer,
153e8d8bef9SDimitry Andric       AllocationMetadata::kMaxTraceLengthToCollect);
154e8d8bef9SDimitry Andric   if (UnpackedLength < BufferLen)
155e8d8bef9SDimitry Andric     BufferLen = UnpackedLength;
156e8d8bef9SDimitry Andric   memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));
157e8d8bef9SDimitry Andric   return UnpackedLength;
1585ffd83dbSDimitry Andric }
1595ffd83dbSDimitry Andric 
1605ffd83dbSDimitry Andric #ifdef __cplusplus
1615ffd83dbSDimitry Andric } // extern "C"
1625ffd83dbSDimitry Andric #endif
163