1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
|
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/gwp_asan/client/lightweight_detector/malloc_shims.h"
#include <limits>
#include <optional>
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/notreached.h"
#include "base/numerics/checked_math.h"
#include "components/gwp_asan/client/lightweight_detector/random_eviction_quarantine.h"
#include "components/gwp_asan/client/sampling_state.h"
#include "partition_alloc/shim/allocator_shim.h"
namespace gwp_asan::internal::lud {
namespace {
using allocator_shim::AllocatorDispatch;
extern AllocatorDispatch g_allocator_dispatch;
// By being implemented as a global with inline method definitions, method calls
// and member accesses are inlined and as efficient as possible in the
// performance-sensitive allocation hot-path.
SamplingState<LIGHTWEIGHTDETECTOR> sampling_state;
bool MaybeQuarantine(void* address,
std::optional<size_t> maybe_size,
void* context,
FreeFunctionKind kind) {
if (!sampling_state.Sample()) [[likely]] {
return false;
}
AllocationInfo info;
info.address = address;
#if BUILDFLAG(IS_APPLE)
info.context = context;
#else
DCHECK_EQ(context, nullptr);
#endif
base::CheckedNumeric<size_t> size = maybe_size.value_or(
g_allocator_dispatch.next->get_size_estimate_function(address, context));
info.free_fn_kind = kind;
if (!size.AssignIfValid(&info.size)) [[unlikely]] {
return false;
}
return RandomEvictionQuarantine::Get()->Add(info);
}
void FreeFn(void* address, void* context) {
if (MaybeQuarantine(address, std::nullopt, context,
FreeFunctionKind::kFree)) {
return;
}
MUSTTAIL return g_allocator_dispatch.next->free_function(address, context);
}
void FreeWithSizeFn(void* address, size_t size, void* context) {
if (MaybeQuarantine(address, size, context,
FreeFunctionKind::kFreeWithSize)) {
return;
}
MUSTTAIL return g_allocator_dispatch.next->free_with_size_function(
address, size, context);
}
void FreeWithAlignmentFn(void* address, size_t alignment, void* context) {
if (MaybeQuarantine(address, std::nullopt, context,
FreeFunctionKind::kFreeWithAlignment)) {
return;
}
MUSTTAIL return g_allocator_dispatch.next->free_with_alignment_function(
address, alignment, context);
}
void FreeWithSizeAndAlignmentFn(void* address,
size_t size,
size_t alignment,
void* context) {
if (MaybeQuarantine(address, size, context,
FreeFunctionKind::kFreeWithSizeAndAlignment)) {
return;
}
MUSTTAIL return g_allocator_dispatch.next
->free_with_size_and_alignment_function(address, size, alignment,
context);
}
void TryFreeDefaultFn(void* address, void* context) {
if (MaybeQuarantine(address, std::nullopt, context,
FreeFunctionKind::kTryFreeDefault)) {
return;
}
MUSTTAIL return g_allocator_dispatch.next->try_free_default_function(address,
context);
}
static void AlignedFreeFn(void* address, void* context) {
if (MaybeQuarantine(address, std::nullopt, context,
FreeFunctionKind::kAlignedFree)) {
return;
}
MUSTTAIL return g_allocator_dispatch.next->aligned_free_function(address,
context);
}
AllocatorDispatch g_allocator_dispatch = {
nullptr, // alloc_function
nullptr, // alloc_unchecked_function
nullptr, // alloc_zero_initialized_function
nullptr, // alloc_aligned_function
nullptr, // realloc_function
nullptr, // realloc_unchecked_function
FreeFn, // free_function
FreeWithSizeFn, // free_with_size_function
FreeWithAlignmentFn, // free_with_alignment_function
FreeWithSizeAndAlignmentFn, // free_with_size_and_alignment_function
nullptr, // get_size_estimate_function
nullptr, // good_size_function
nullptr, // claimed_address_function
nullptr, // batch_malloc_function
nullptr, // batch_free_function
TryFreeDefaultFn, // try_free_default_function
nullptr, // aligned_malloc_function
nullptr, // aligned_malloc_unchecked_function
nullptr, // aligned_realloc_function
nullptr, // aligned_realloc_unchecked_function
AlignedFreeFn, // aligned_free_function
nullptr // next
};
} // namespace
void InstallMallocHooks(size_t max_allocation_count,
size_t max_total_size,
size_t total_size_high_water_mark,
size_t total_size_low_water_mark,
size_t eviction_chunk_size,
size_t eviction_task_interval_ms,
size_t sampling_frequency) {
sampling_state.Init(sampling_frequency);
RandomEvictionQuarantine::Init(max_allocation_count, max_total_size,
total_size_high_water_mark,
total_size_low_water_mark, eviction_chunk_size,
eviction_task_interval_ms);
allocator_shim::InsertAllocatorDispatch(&g_allocator_dispatch);
}
void FinishFree(const AllocationInfo& allocation) {
#if BUILDFLAG(IS_APPLE)
void* context = allocation.context;
#else
void* context = nullptr;
#endif
const AllocatorDispatch* next = g_allocator_dispatch.next;
switch (allocation.free_fn_kind) {
case FreeFunctionKind::kFree:
next->free_function(allocation.address, context);
break;
case FreeFunctionKind::kFreeWithSize:
next->free_with_size_function(allocation.address, allocation.size,
context);
break;
case FreeFunctionKind::kFreeWithAlignment:
// TODO(crbug.com/412358843): Memory and forward alignment information.
next->free_function(allocation.address, context);
break;
case FreeFunctionKind::kFreeWithSizeAndAlignment:
// TODO(crbug.com/412358843): Similar to above, forward alignment
// information. We shall not forward size information here because it can
// confuse an allocator by alignment mismatch.
next->free_function(allocation.address, context);
break;
case FreeFunctionKind::kTryFreeDefault:
next->try_free_default_function(allocation.address, context);
break;
case FreeFunctionKind::kAlignedFree:
next->aligned_free_function(allocation.address, context);
break;
default:
NOTREACHED();
}
}
} // namespace gwp_asan::internal::lud
|