1//===-- crash_handler.cpp ---------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "gwp_asan/common.h"
10#include "gwp_asan/stack_trace_compressor.h"
11
12#include <assert.h>
13#include <stdint.h>
14#include <string.h>
15
16using AllocationMetadata = gwp_asan::AllocationMetadata;
17using Error = gwp_asan::Error;
18
19#ifdef __cplusplus
20extern "C" {
21#endif
22
23bool __gwp_asan_error_is_mine(const gwp_asan::AllocatorState *State,
24                              uintptr_t ErrorPtr) {
25  assert(State && "State should not be nullptr.");
26  if (State->FailureType != Error::UNKNOWN && State->FailureAddress != 0)
27    return true;
28
29  return ErrorPtr < State->GuardedPagePoolEnd &&
30         State->GuardedPagePool <= ErrorPtr;
31}
32
33uintptr_t
34__gwp_asan_get_internal_crash_address(const gwp_asan::AllocatorState *State,
35                                      uintptr_t ErrorPtr) {
36  // There can be a race between internally- and externally-raised faults. The
37  // fault address from the signal handler is used to discriminate whether it's
38  // internally- or externally-raised, and the pool maintains a special page at
39  // the end of the GuardedPagePool specifically for the internally-raised
40  // faults.
41  if (ErrorPtr != State->internallyDetectedErrorFaultAddress())
42    return 0u;
43  return State->FailureAddress;
44}
45
46static const AllocationMetadata *
47addrToMetadata(const gwp_asan::AllocatorState *State,
48               const AllocationMetadata *Metadata, uintptr_t Ptr) {
49  // Note - Similar implementation in guarded_pool_allocator.cpp.
50  return &Metadata[State->getNearestSlot(Ptr)];
51}
52
53gwp_asan::Error
54__gwp_asan_diagnose_error(const gwp_asan::AllocatorState *State,
55                          const gwp_asan::AllocationMetadata *Metadata,
56                          uintptr_t ErrorPtr) {
57  if (!__gwp_asan_error_is_mine(State, ErrorPtr))
58    return Error::UNKNOWN;
59
60  if (State->FailureType != Error::UNKNOWN)
61    return State->FailureType;
62
63  // Check for use-after-free.
64  if (addrToMetadata(State, Metadata, ErrorPtr)->IsDeallocated)
65    return Error::USE_AFTER_FREE;
66
67  // Check for buffer-overflow. Because of allocation alignment or left/right
68  // page placement, we can have buffer-overflows that don't touch a guarded
69  // page, but these are not possible to detect unless it's also a
70  // use-after-free, which is handled above.
71  if (State->isGuardPage(ErrorPtr)) {
72    size_t Slot = State->getNearestSlot(ErrorPtr);
73    const AllocationMetadata *SlotMeta =
74        addrToMetadata(State, Metadata, State->slotToAddr(Slot));
75
76    // Ensure that this slot was allocated once upon a time.
77    if (!SlotMeta->Addr)
78      return Error::UNKNOWN;
79
80    if (SlotMeta->Addr < ErrorPtr)
81      return Error::BUFFER_OVERFLOW;
82    return Error::BUFFER_UNDERFLOW;
83  }
84
85  // If we have reached here, the error is still unknown.
86  return Error::UNKNOWN;
87}
88
89const gwp_asan::AllocationMetadata *
90__gwp_asan_get_metadata(const gwp_asan::AllocatorState *State,
91                        const gwp_asan::AllocationMetadata *Metadata,
92                        uintptr_t ErrorPtr) {
93  if (!__gwp_asan_error_is_mine(State, ErrorPtr))
94    return nullptr;
95
96  if (ErrorPtr >= State->GuardedPagePoolEnd ||
97      State->GuardedPagePool > ErrorPtr)
98    return nullptr;
99
100  const AllocationMetadata *Meta = addrToMetadata(State, Metadata, ErrorPtr);
101  if (Meta->Addr == 0)
102    return nullptr;
103
104  return Meta;
105}
106
107uintptr_t __gwp_asan_get_allocation_address(
108    const gwp_asan::AllocationMetadata *AllocationMeta) {
109  return AllocationMeta->Addr;
110}
111
112size_t __gwp_asan_get_allocation_size(
113    const gwp_asan::AllocationMetadata *AllocationMeta) {
114  return AllocationMeta->RequestedSize;
115}
116
117uint64_t __gwp_asan_get_allocation_thread_id(
118    const gwp_asan::AllocationMetadata *AllocationMeta) {
119  return AllocationMeta->AllocationTrace.ThreadID;
120}
121
122size_t __gwp_asan_get_allocation_trace(
123    const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,
124    size_t BufferLen) {
125  uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];
126  size_t UnpackedLength = gwp_asan::compression::unpack(
127      AllocationMeta->AllocationTrace.CompressedTrace,
128      AllocationMeta->AllocationTrace.TraceSize, UncompressedBuffer,
129      AllocationMetadata::kMaxTraceLengthToCollect);
130  if (UnpackedLength < BufferLen)
131    BufferLen = UnpackedLength;
132  memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));
133  return UnpackedLength;
134}
135
136bool __gwp_asan_is_deallocated(
137    const gwp_asan::AllocationMetadata *AllocationMeta) {
138  return AllocationMeta->IsDeallocated;
139}
140
141uint64_t __gwp_asan_get_deallocation_thread_id(
142    const gwp_asan::AllocationMetadata *AllocationMeta) {
143  return AllocationMeta->DeallocationTrace.ThreadID;
144}
145
146size_t __gwp_asan_get_deallocation_trace(
147    const gwp_asan::AllocationMetadata *AllocationMeta, uintptr_t *Buffer,
148    size_t BufferLen) {
149  uintptr_t UncompressedBuffer[AllocationMetadata::kMaxTraceLengthToCollect];
150  size_t UnpackedLength = gwp_asan::compression::unpack(
151      AllocationMeta->DeallocationTrace.CompressedTrace,
152      AllocationMeta->DeallocationTrace.TraceSize, UncompressedBuffer,
153      AllocationMetadata::kMaxTraceLengthToCollect);
154  if (UnpackedLength < BufferLen)
155    BufferLen = UnpackedLength;
156  memcpy(Buffer, UncompressedBuffer, BufferLen * sizeof(*Buffer));
157  return UnpackedLength;
158}
159
160#ifdef __cplusplus
161} // extern "C"
162#endif
163