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

base / allocator / partition_allocator / src / partition_alloc / partition_alloc_hooks.h [blame]

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef PARTITION_ALLOC_PARTITION_ALLOC_HOOKS_H_
#define PARTITION_ALLOC_PARTITION_ALLOC_HOOKS_H_

#include <atomic>
#include <cstddef>

#include "partition_alloc/partition_alloc_base/compiler_specific.h"
#include "partition_alloc/partition_alloc_base/component_export.h"
#include "partition_alloc/partition_alloc_constants.h"

namespace partition_alloc {

class AllocationNotificationData;
class FreeNotificationData;

// PartitionAlloc supports setting hooks to observe allocations/frees as they
// occur as well as 'override' hooks that allow overriding those operations.
class PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionAllocHooks {
 public:
  // Log allocation and free events.
  typedef void AllocationObserverHook(
      const AllocationNotificationData& notification_data);
  typedef void FreeObserverHook(const FreeNotificationData& notification_data);

  // If it returns true, the allocation has been overridden with the pointer in
  // *out.
  typedef bool AllocationOverrideHook(void** out,
                                      AllocFlags flags,
                                      size_t size,
                                      const char* type_name);
  // If it returns true, then the allocation was overridden and has been freed.
  typedef bool FreeOverrideHook(void* address);
  // If it returns true, the underlying allocation is overridden and *out holds
  // the size of the underlying allocation.
  typedef bool ReallocOverrideHook(size_t* out, void* address);

  // Special hook type, independent of the rest. Triggered when `free()` detects
  // outstanding references to the allocation.
  // IMPORTANT: Make sure the hook always overwrites `[address, address + size)`
  // with a bit pattern that cannot be interpreted as a valid memory address.
  typedef void QuarantineOverrideHook(void* address, size_t size);

  // To unhook, call Set*Hooks with nullptrs.
  static void SetObserverHooks(AllocationObserverHook* alloc_hook,
                               FreeObserverHook* free_hook);
  static void SetOverrideHooks(AllocationOverrideHook* alloc_hook,
                               FreeOverrideHook* free_hook,
                               ReallocOverrideHook realloc_hook);

  // Helper method to check whether hooks are enabled. This is an optimization
  // so that if a function needs to call observer and override hooks in two
  // different places this value can be cached and only loaded once.
  static bool AreHooksEnabled() {
    return hooks_enabled_.load(std::memory_order_relaxed);
  }

  static void AllocationObserverHookIfEnabled(
      const partition_alloc::AllocationNotificationData& notification_data);
  static bool AllocationOverrideHookIfEnabled(void** out,
                                              AllocFlags flags,
                                              size_t size,
                                              const char* type_name);

  static void FreeObserverHookIfEnabled(
      const FreeNotificationData& notification_data);
  static bool FreeOverrideHookIfEnabled(void* address);

  static void ReallocObserverHookIfEnabled(
      const FreeNotificationData& free_notification_data,
      const AllocationNotificationData& allocation_notification_data);
  static bool ReallocOverrideHookIfEnabled(size_t* out, void* address);

  PA_ALWAYS_INLINE static QuarantineOverrideHook* GetQuarantineOverrideHook() {
    return quarantine_override_hook_.load(std::memory_order_acquire);
  }

  static void SetQuarantineOverrideHook(QuarantineOverrideHook* hook);

 private:
  // Single bool that is used to indicate whether observer or allocation hooks
  // are set to reduce the numbers of loads required to check whether hooking is
  // enabled.
  static std::atomic<bool> hooks_enabled_;

  // Lock used to synchronize Set*Hooks calls.
  static std::atomic<AllocationObserverHook*> allocation_observer_hook_;
  static std::atomic<FreeObserverHook*> free_observer_hook_;

  static std::atomic<AllocationOverrideHook*> allocation_override_hook_;
  static std::atomic<FreeOverrideHook*> free_override_hook_;
  static std::atomic<ReallocOverrideHook*> realloc_override_hook_;

  static std::atomic<QuarantineOverrideHook*> quarantine_override_hook_;
};

}  // namespace partition_alloc

#endif  // PARTITION_ALLOC_PARTITION_ALLOC_HOOKS_H_