155 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			155 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- 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) {
 | 
						|
  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;
 | 
						|
 | 
						|
  // Let's try and figure out what the source of this error is.
 | 
						|
  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;
 | 
						|
  }
 | 
						|
 | 
						|
  // Access wasn't a guard page, check for use-after-free.
 | 
						|
  const AllocationMetadata *SlotMeta =
 | 
						|
      addrToMetadata(State, Metadata, ErrorPtr);
 | 
						|
  if (SlotMeta->IsDeallocated) {
 | 
						|
    return Error::USE_AFTER_FREE;
 | 
						|
  }
 | 
						|
 | 
						|
  // 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
 |