You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
154 lines
4.9 KiB
154 lines
4.9 KiB
//===-- 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 <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->Size;
|
|
}
|
|
|
|
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
|