diff options
Diffstat (limited to 'contrib/llvm-project/compiler-rt/lib/gwp_asan/crash_handler.cpp')
-rw-r--r-- | contrib/llvm-project/compiler-rt/lib/gwp_asan/crash_handler.cpp | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/contrib/llvm-project/compiler-rt/lib/gwp_asan/crash_handler.cpp b/contrib/llvm-project/compiler-rt/lib/gwp_asan/crash_handler.cpp new file mode 100644 index 000000000000..555365c6e6f4 --- /dev/null +++ b/contrib/llvm-project/compiler-rt/lib/gwp_asan/crash_handler.cpp @@ -0,0 +1,162 @@ +//===-- crash_handler.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gwp_asan/common.h" +#include "gwp_asan/stack_trace_compressor.h" + +#include <assert.h> +#include <stdint.h> +#include <string.h> + +using AllocationMetadata = gwp_asan::AllocationMetadata; +using Error = gwp_asan::Error; + +#ifdef __cplusplus +extern "C" { +#endif + +bool __gwp_asan_error_is_mine(const gwp_asan::AllocatorState *State, + uintptr_t ErrorPtr) { + assert(State && "State should not be nullptr."); + if (State->FailureType != Error::UNKNOWN && State->FailureAddress != 0) + return true; + + return ErrorPtr < State->GuardedPagePoolEnd && + State->GuardedPagePool <= ErrorPtr; +} + +uintptr_t +__gwp_asan_get_internal_crash_address(const gwp_asan::AllocatorState *State, + uintptr_t ErrorPtr) { + // There can be a race between internally- and externally-raised faults. The + // fault address from the signal handler is used to discriminate whether it's + // internally- or externally-raised, and the pool maintains a special page at + // the end of the GuardedPagePool specifically for the internally-raised + // faults. + if (ErrorPtr != State->internallyDetectedErrorFaultAddress()) + return 0u; + return State->FailureAddress; +} + +static const AllocationMetadata * +addrToMetadata(const gwp_asan::AllocatorState *State, + const AllocationMetadata *Metadata, uintptr_t Ptr) { + // Note - Similar implementation in guarded_pool_allocator.cpp. + return &Metadata[State->getNearestSlot(Ptr)]; +} + +gwp_asan::Error +__gwp_asan_diagnose_error(const gwp_asan::AllocatorState *State, + const gwp_asan::AllocationMetadata *Metadata, + uintptr_t ErrorPtr) { + if (!__gwp_asan_error_is_mine(State, ErrorPtr)) + return Error::UNKNOWN; + + if (State->FailureType != Error::UNKNOWN) + return State->FailureType; + + // Check for use-after-free. + if (addrToMetadata(State, Metadata, ErrorPtr)->IsDeallocated) + return Error::USE_AFTER_FREE; + + // Check for buffer-overflow. Because of allocation alignment or left/right + // page placement, we can have buffer-overflows that don't touch a guarded + // page, but these are not possible to detect unless it's also a + // use-after-free, which is handled above. + if (State->isGuardPage(ErrorPtr)) { + size_t Slot = State->getNearestSlot(ErrorPtr); + const AllocationMetadata *SlotMeta = + addrToMetadata(State, Metadata, State->slotToAddr(Slot)); + + // Ensure that this slot was allocated once upon a time. + if (!SlotMeta->Addr) + return Error::UNKNOWN; + + if (SlotMeta->Addr < ErrorPtr) + return Error::BUFFER_OVERFLOW; + return Error::BUFFER_UNDERFLOW; + } + + // If we have reached here, the error is still unknown. + return Error::UNKNOWN; +} + +const gwp_asan::AllocationMetadata * +__gwp_asan_get_metadata(const gwp_asan::AllocatorState *State, + const gwp_asan::AllocationMetadata *Metadata, + uintptr_t ErrorPtr) { + if (!__gwp_asan_error_is_mine(State, ErrorPtr)) + return nullptr; + + if (ErrorPtr >= State->GuardedPagePoolEnd || + State->GuardedPagePool > ErrorPtr) + return nullptr; + + const AllocationMetadata *Meta = addrToMetadata(State, Metadata, ErrorPtr); + if (Meta->Addr == 0) + return nullptr; + + return Meta; +} + +uintptr_t __gwp_asan_get_allocation_address( + const gwp_asan::AllocationMetadata *AllocationMeta) { + return AllocationMeta->Addr; +} + +size_t __gwp_asan_get_allocation_size( + const gwp_asan::AllocationMetadata *AllocationMeta) { + return AllocationMeta->RequestedSize; +} + +uint64_t __gwp_asan_get_allocation_thread_id( + const gwp_asan::AllocationMetadata *AllocationMeta) { + return AllocationMeta->AllocationTrace.ThreadID; +} + +size_t __gwp_asan_get_allocation_trace( + const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer, + size_t BufferLen) { + uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect]; + size_t UnpackedLength = gwp_asan::compression::unpack( + AllocationMeta->AllocationTrace.CompressedTrace, + AllocationMeta->AllocationTrace.TraceSize, UncompressedBuffer, + AllocationMetadata::kMaxTraceLengthToCollect); + if (UnpackedLength < BufferLen) + BufferLen = UnpackedLength; + memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer)); + return UnpackedLength; +} + +bool __gwp_asan_is_deallocated( + const gwp_asan::AllocationMetadata *AllocationMeta) { + return AllocationMeta->IsDeallocated; +} + +uint64_t __gwp_asan_get_deallocation_thread_id( + const gwp_asan::AllocationMetadata *AllocationMeta) { + return AllocationMeta->DeallocationTrace.ThreadID; +} + +size_t __gwp_asan_get_deallocation_trace( + const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer, + size_t BufferLen) { + uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect]; + size_t UnpackedLength = gwp_asan::compression::unpack( + AllocationMeta->DeallocationTrace.CompressedTrace, + AllocationMeta->DeallocationTrace.TraceSize, UncompressedBuffer, + AllocationMetadata::kMaxTraceLengthToCollect); + if (UnpackedLength < BufferLen) + BufferLen = UnpackedLength; + memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer)); + return UnpackedLength; +} + +#ifdef __cplusplus +} // extern "C" +#endif |