xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/gwp_asan/crash_handler.cpp (revision e8d8bef961a50d4dc22501cde4fb9fb0be1b2532)
1*e8d8bef9SDimitry 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>
13*e8d8bef9SDimitry Andric #include <stdint.h>
14*e8d8bef9SDimitry 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 
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
345ffd83dbSDimitry Andric __gwp_asan_get_internal_crash_address(const gwp_asan::AllocatorState *State) {
355ffd83dbSDimitry Andric   return State->FailureAddress;
365ffd83dbSDimitry Andric }
375ffd83dbSDimitry Andric 
385ffd83dbSDimitry Andric static const AllocationMetadata *
395ffd83dbSDimitry Andric addrToMetadata(const gwp_asan::AllocatorState *State,
405ffd83dbSDimitry Andric                const AllocationMetadata *Metadata, uintptr_t Ptr) {
415ffd83dbSDimitry Andric   // Note - Similar implementation in guarded_pool_allocator.cpp.
425ffd83dbSDimitry Andric   return &Metadata[State->getNearestSlot(Ptr)];
435ffd83dbSDimitry Andric }
445ffd83dbSDimitry Andric 
455ffd83dbSDimitry Andric gwp_asan::Error
465ffd83dbSDimitry Andric __gwp_asan_diagnose_error(const gwp_asan::AllocatorState *State,
475ffd83dbSDimitry Andric                           const gwp_asan::AllocationMetadata *Metadata,
485ffd83dbSDimitry Andric                           uintptr_t ErrorPtr) {
495ffd83dbSDimitry Andric   if (!__gwp_asan_error_is_mine(State, ErrorPtr))
505ffd83dbSDimitry Andric     return Error::UNKNOWN;
515ffd83dbSDimitry Andric 
525ffd83dbSDimitry Andric   if (State->FailureType != Error::UNKNOWN)
535ffd83dbSDimitry Andric     return State->FailureType;
545ffd83dbSDimitry Andric 
555ffd83dbSDimitry Andric   // Let's try and figure out what the source of this error is.
565ffd83dbSDimitry Andric   if (State->isGuardPage(ErrorPtr)) {
575ffd83dbSDimitry Andric     size_t Slot = State->getNearestSlot(ErrorPtr);
585ffd83dbSDimitry Andric     const AllocationMetadata *SlotMeta =
595ffd83dbSDimitry Andric         addrToMetadata(State, Metadata, State->slotToAddr(Slot));
605ffd83dbSDimitry Andric 
615ffd83dbSDimitry Andric     // Ensure that this slot was allocated once upon a time.
625ffd83dbSDimitry Andric     if (!SlotMeta->Addr)
635ffd83dbSDimitry Andric       return Error::UNKNOWN;
645ffd83dbSDimitry Andric 
655ffd83dbSDimitry Andric     if (SlotMeta->Addr < ErrorPtr)
665ffd83dbSDimitry Andric       return Error::BUFFER_OVERFLOW;
675ffd83dbSDimitry Andric     return Error::BUFFER_UNDERFLOW;
685ffd83dbSDimitry Andric   }
695ffd83dbSDimitry Andric 
705ffd83dbSDimitry Andric   // Access wasn't a guard page, check for use-after-free.
715ffd83dbSDimitry Andric   const AllocationMetadata *SlotMeta =
725ffd83dbSDimitry Andric       addrToMetadata(State, Metadata, ErrorPtr);
735ffd83dbSDimitry Andric   if (SlotMeta->IsDeallocated) {
745ffd83dbSDimitry Andric     return Error::USE_AFTER_FREE;
755ffd83dbSDimitry Andric   }
765ffd83dbSDimitry Andric 
775ffd83dbSDimitry Andric   // If we have reached here, the error is still unknown.
785ffd83dbSDimitry Andric   return Error::UNKNOWN;
795ffd83dbSDimitry Andric }
805ffd83dbSDimitry Andric 
815ffd83dbSDimitry Andric const gwp_asan::AllocationMetadata *
825ffd83dbSDimitry Andric __gwp_asan_get_metadata(const gwp_asan::AllocatorState *State,
835ffd83dbSDimitry Andric                         const gwp_asan::AllocationMetadata *Metadata,
845ffd83dbSDimitry Andric                         uintptr_t ErrorPtr) {
855ffd83dbSDimitry Andric   if (!__gwp_asan_error_is_mine(State, ErrorPtr))
865ffd83dbSDimitry Andric     return nullptr;
875ffd83dbSDimitry Andric 
885ffd83dbSDimitry Andric   if (ErrorPtr >= State->GuardedPagePoolEnd ||
895ffd83dbSDimitry Andric       State->GuardedPagePool > ErrorPtr)
905ffd83dbSDimitry Andric     return nullptr;
915ffd83dbSDimitry Andric 
925ffd83dbSDimitry Andric   const AllocationMetadata *Meta = addrToMetadata(State, Metadata, ErrorPtr);
935ffd83dbSDimitry Andric   if (Meta->Addr == 0)
945ffd83dbSDimitry Andric     return nullptr;
955ffd83dbSDimitry Andric 
965ffd83dbSDimitry Andric   return Meta;
975ffd83dbSDimitry Andric }
985ffd83dbSDimitry Andric 
995ffd83dbSDimitry Andric uintptr_t __gwp_asan_get_allocation_address(
1005ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta) {
1015ffd83dbSDimitry Andric   return AllocationMeta->Addr;
1025ffd83dbSDimitry Andric }
1035ffd83dbSDimitry Andric 
1045ffd83dbSDimitry Andric size_t __gwp_asan_get_allocation_size(
1055ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta) {
1065ffd83dbSDimitry Andric   return AllocationMeta->Size;
1075ffd83dbSDimitry Andric }
1085ffd83dbSDimitry Andric 
1095ffd83dbSDimitry Andric uint64_t __gwp_asan_get_allocation_thread_id(
1105ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta) {
1115ffd83dbSDimitry Andric   return AllocationMeta->AllocationTrace.ThreadID;
1125ffd83dbSDimitry Andric }
1135ffd83dbSDimitry Andric 
1145ffd83dbSDimitry Andric size_t __gwp_asan_get_allocation_trace(
1155ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,
1165ffd83dbSDimitry Andric     size_t BufferLen) {
117*e8d8bef9SDimitry Andric   uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];
118*e8d8bef9SDimitry Andric   size_t UnpackedLength = gwp_asan::compression::unpack(
1195ffd83dbSDimitry Andric       AllocationMeta->AllocationTrace.CompressedTrace,
120*e8d8bef9SDimitry Andric       AllocationMeta->AllocationTrace.TraceSize, UncompressedBuffer,
121*e8d8bef9SDimitry Andric       AllocationMetadata::kMaxTraceLengthToCollect);
122*e8d8bef9SDimitry Andric   if (UnpackedLength < BufferLen)
123*e8d8bef9SDimitry Andric     BufferLen = UnpackedLength;
124*e8d8bef9SDimitry Andric   memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));
125*e8d8bef9SDimitry Andric   return UnpackedLength;
1265ffd83dbSDimitry Andric }
1275ffd83dbSDimitry Andric 
1285ffd83dbSDimitry Andric bool __gwp_asan_is_deallocated(
1295ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta) {
1305ffd83dbSDimitry Andric   return AllocationMeta->IsDeallocated;
1315ffd83dbSDimitry Andric }
1325ffd83dbSDimitry Andric 
1335ffd83dbSDimitry Andric uint64_t __gwp_asan_get_deallocation_thread_id(
1345ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta) {
1355ffd83dbSDimitry Andric   return AllocationMeta->DeallocationTrace.ThreadID;
1365ffd83dbSDimitry Andric }
1375ffd83dbSDimitry Andric 
1385ffd83dbSDimitry Andric size_t __gwp_asan_get_deallocation_trace(
1395ffd83dbSDimitry Andric     const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,
1405ffd83dbSDimitry Andric     size_t BufferLen) {
141*e8d8bef9SDimitry Andric   uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];
142*e8d8bef9SDimitry Andric   size_t UnpackedLength = gwp_asan::compression::unpack(
1435ffd83dbSDimitry Andric       AllocationMeta->DeallocationTrace.CompressedTrace,
144*e8d8bef9SDimitry Andric       AllocationMeta->DeallocationTrace.TraceSize, UncompressedBuffer,
145*e8d8bef9SDimitry Andric       AllocationMetadata::kMaxTraceLengthToCollect);
146*e8d8bef9SDimitry Andric   if (UnpackedLength < BufferLen)
147*e8d8bef9SDimitry Andric     BufferLen = UnpackedLength;
148*e8d8bef9SDimitry Andric   memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));
149*e8d8bef9SDimitry Andric   return UnpackedLength;
1505ffd83dbSDimitry Andric }
1515ffd83dbSDimitry Andric 
1525ffd83dbSDimitry Andric #ifdef __cplusplus
1535ffd83dbSDimitry Andric } // extern "C"
1545ffd83dbSDimitry Andric #endif
155