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

import("//build/config/sanitizers/sanitizers.gni")
import("//build_overrides/partition_alloc.gni")

if (is_apple) {
  import("//build/config/features.gni")
}

# Whether 64-bit pointers are used.
# A static_assert in partition_alloc_config.h verifies that.
if (is_nacl) {
  # NaCl targets don't use 64-bit pointers.
  has_64_bit_pointers = false
} else if (current_cpu == "x64" || current_cpu == "arm64" ||
           current_cpu == "loong64") {
  has_64_bit_pointers = true
} else if (current_cpu == "x86" || current_cpu == "arm") {
  has_64_bit_pointers = false
} else {
  assert(false, "Unknown CPU: $current_cpu")
}

if (use_partition_alloc_as_malloc_default) {
  _default_allocator = "partition"
} else {
  _default_allocator = "none"
}

declare_args() {
  # Whether PartitionAlloc should be available for use or not.
  # true makes PartitionAlloc linked to the executable or shared library and
  # makes it available for use. It doesn't mean that the default allocator
  # is PartitionAlloc, which is governed by |use_partition_alloc_as_malloc|.
  #
  # N.B. generally, embedders should look at this GN arg and at the
  # corresponding buildflag to determine whether to interact with PA
  # source at all (pulling the component in via GN, including headers,
  # etc.). There is nothing stopping a lazy embedder from ignoring this
  # and unconditionally using PA, but such a setup is inadvisable.
  #
  # In Chromium, this is set true, except:
  #
  # 1.  On Cronet bots, because Cronet doesn't use PartitionAlloc at all,
  #     and doesn't wish to incur the library size increase (crbug.com/674570).
  # 2.  On NaCl (through this declaration), where PartitionAlloc doesn't
  #     build at all.
  use_partition_alloc = !is_nacl
}

declare_args() {
  # PartitionAlloc-Everywhere (PA-E).
  use_partition_alloc_as_malloc =
      use_partition_alloc && use_partition_alloc_as_malloc_default
}

declare_args() {
  use_freeslot_bitmap = false

  # Puts the regular and BRP pools right next to each other, so that we can
  # check "belongs to one of the two pools" with a single bitmask operation.
  glue_core_pools = false

  # Introduces pointer compression support in PA.
  #
  # This is effective only for memory allocated from PartitionAlloc, so it is
  # recommended to enable PA-E above, but isn't strictly necessary. Embedders
  # can create and use PA partitions explicitly.
  enable_pointer_compression_support = false

  # Enables a bounds check when two pointers (at least one being raw_ptr) are
  # subtracted (if supported by the underlying implementation).
  enable_pointer_subtraction_check = false
}

declare_args() {
  # Build support for Use-after-Free protection via BackupRefPtr (BRP),
  # making the raw_ptr<T> implementation to RawPtrBackupRefImpl if active.
  #
  # These are effective only for memory allocated from PartitionAlloc, so it is
  # recommended to enable PA-E above, but isn't strictly necessary. Embedders
  # can create and use PA partitions explicitly.
  #
  # Note that |enable_backup_ref_ptr_support = true| doesn't necessarily enable
  # BRP protection. It'll be enabled only for partition created with
  # partition_alloc::PartitionOptions::BackupRefPtr::kEnabled.
  enable_backup_ref_ptr_support =
      use_partition_alloc && enable_backup_ref_ptr_support_default

  # RAW_PTR_EXCLUSION macro is disabled on official builds because it increased
  # binary size. This flag can be used to enable it for official builds too.
  force_enable_raw_ptr_exclusion = false
}

declare_args() {
  # Determines whether `raw_ptr_experimental<T>` is an alias for
  # `raw_ptr<T>` or `T*` (true raw pointer).
  #
  # Members rewritten as `raw_ptr_experimental` rely on this as an
  # escape hatch to degrade to a `T*` if `raw_ptr` performance proves
  # problematic. Defaults to match standard `raw_ptr` support.
  #
  # One side effect of this is that `raw_ptr_experimental<T> foo_` must
  # not use `foo_.get()`; this is incoherent when `foo_` is a `T*`.
  # Use `base::to_address()` instead.
  enable_raw_ptr_experimental = enable_backup_ref_ptr_support
}

assert(!enable_pointer_compression_support || glue_core_pools,
       "Pointer compression relies on core pools being contiguous.")

declare_args() {
  # The supported platforms are supposed to match `_is_brp_supported`, but we
  # enable the feature on Linux early because it's most widely used for security
  # research.
  #
  # The implementation of ASan BRP is purpose-built to inspect Chromium
  # internals and is entangled with `//base` s.t. it cannot be used
  # outside of Chromium.
  use_asan_backup_ref_ptr =
      build_with_chromium && is_asan && (is_win || is_android || is_linux)

  # Use probe-on-destruct unowned ptr detection with ASAN.
  use_asan_unowned_ptr = false
}

# Use the version of raw_ptr<T> that allows the embedder to implement custom
# logic.
use_hookable_raw_ptr = use_asan_backup_ref_ptr

declare_args() {
  # - put_ref_count_in_previous_slot: place the ref-count at the end of the
  #   previous slot (or in metadata if a slot starts on the page boundary), as
  #   opposed to the beginning of the slot.
  # - enable_backup_ref_ptr_slow_checks: enable additional safety checks that
  #   are too expensive to have on by default.
  # - enable_dangling_raw_ptr_checks: enable checking raw_ptr do not become
  #   dangling during their lifetime.
  # - backup_ref_ptr_poison_oob_ptr: poison out-of-bounds (OOB) pointers to
  #   generate an exception in the event that an OOB pointer is dereferenced.
  put_ref_count_in_previous_slot =
      put_ref_count_in_previous_slot_default && enable_backup_ref_ptr_support

  enable_backup_ref_ptr_slow_checks =
      enable_backup_ref_ptr_slow_checks_default && enable_backup_ref_ptr_support

  # Enable the feature flag required to activate backup ref pointers. That is to
  # say `PartitionAllocBackupRefPtr`.
  #
  # This is meant to be used primarily on bots. It is much easier to override
  # the feature flags using a binary flag instead of updating multiple bots's
  # scripts to pass command line arguments.
  enable_backup_ref_ptr_feature_flag = false

  enable_dangling_raw_ptr_checks =
      enable_dangling_raw_ptr_checks_default && enable_backup_ref_ptr_support

  # Enable the feature flag required to check for dangling pointers. That is to
  # say `PartitionAllocDanglingPtr`.
  #
  # This is meant to be used primarily on bots. It is much easier to override
  # the feature flags using a binary flag instead of updating multiple bots's
  # scripts to pass command line arguments.
  enable_dangling_raw_ptr_feature_flag = false

  # Enables the dangling raw_ptr checks feature for the performance experiment.
  # Not every dangling pointers have been fixed or annotated yet. To avoid
  # accounting for the cost of calling the PA's embedder's callbacks when a
  # dangling pointer has been detected, this simulates the raw_ptr to be
  # allowed to dangle.
  #
  # This flag is temporary, and isn't used by PA embedders, so it doesn't need
  # to go through build_overrides
  enable_dangling_raw_ptr_perf_experiment = false

  # Set to `enable_backup_ref_ptr_support && has_64_bit_pointers` when enabling.
  backup_ref_ptr_poison_oob_ptr = false
}

declare_args() {
  # Shadow metadata is still under development and only supports Linux
  # for now.
  enable_shadow_metadata = false

  if (is_apple) {
    # use_blink currently assumes mach absolute ticks (eg, to ensure trace
    # events cohere).
    partition_alloc_enable_mach_absolute_time_ticks = is_mac || use_blink
  }
}

# *Scan is currently only used by Chromium, and supports only 64-bit.
use_starscan = build_with_chromium && has_64_bit_pointers

pcscan_stack_supported =
    use_starscan && (current_cpu == "x64" || current_cpu == "x86" ||
                     current_cpu == "arm" || current_cpu == "arm64")

# We want to provide assertions that guard against inconsistent build
# args, but there is no point in having them fire if we're not building
# PartitionAlloc at all. If `use_partition_alloc` is false, we jam all
# related args to `false`. The prime example is NaCl, where
# PartitionAlloc doesn't build at all.
if (is_nacl) {
  assert(!use_partition_alloc, "PartitionAlloc doesn't build on NaCl")
}
if (!use_partition_alloc) {
  use_partition_alloc_as_malloc = false
  enable_backup_ref_ptr_support = false
  enable_raw_ptr_experimental = false
  use_asan_backup_ref_ptr = false
  use_asan_unowned_ptr = false
  use_hookable_raw_ptr = false
  put_ref_count_in_previous_slot = false
  enable_backup_ref_ptr_slow_checks = false
  enable_dangling_raw_ptr_checks = false
  enable_dangling_raw_ptr_perf_experiment = false
  enable_pointer_subtraction_check = false
  backup_ref_ptr_poison_oob_ptr = false
  use_starscan = false
}

# put_ref_count_in_previous_slot can only be used if
# enable_backup_ref_ptr_support is true.
assert(
    enable_backup_ref_ptr_support || !put_ref_count_in_previous_slot,
    "Can't put ref count in the previous slot if BackupRefPtr isn't enabled at all")

# enable_backup_ref_ptr_slow_checks can only be used if enable_backup_ref_ptr_support
# is true.
assert(enable_backup_ref_ptr_support || !enable_backup_ref_ptr_slow_checks,
       "Can't enable additional BackupRefPtr checks if it isn't enabled at all")

assert(
    enable_backup_ref_ptr_support || !enable_raw_ptr_experimental,
    "Can't make `raw_ptr_experimental` = `raw_ptr` when the latter is wholly disabled")

# enable_dangling_raw_ptr_checks can only be used if enable_backup_ref_ptr_support
# is true.
assert(
    enable_backup_ref_ptr_support || !enable_dangling_raw_ptr_checks,
    "Can't enable dangling raw_ptr checks if BackupRefPtr isn't enabled at all")

# To run the dangling raw_ptr detector experiment, the underlying feature must
# be enabled too.
assert(
    enable_dangling_raw_ptr_checks || !enable_dangling_raw_ptr_perf_experiment,
    "Missing dangling pointer checks feature for its performance experiment")

# To poison OOB pointers for BackupRefPtr, the underlying feature must
# be enabled, too.
assert(
    enable_backup_ref_ptr_support || !backup_ref_ptr_poison_oob_ptr,
    "Can't enable poisoning for OOB pointers if BackupRefPtr isn't enabled at all")
assert(has_64_bit_pointers || !backup_ref_ptr_poison_oob_ptr,
       "Can't enable poisoning for OOB pointers if pointers are only 32-bit")

# AsanBackupRefPtr and AsanUnownedPtr are mutually exclusive variants of raw_ptr.
assert(
    !use_asan_unowned_ptr || !use_asan_backup_ref_ptr,
    "Both AsanUnownedPtr and AsanBackupRefPtr can't be enabled at the same time")

# BackupRefPtr and AsanBackupRefPtr are mutually exclusive variants of raw_ptr.
assert(
    !enable_backup_ref_ptr_support || !use_asan_backup_ref_ptr,
    "Both BackupRefPtr and AsanBackupRefPtr can't be enabled at the same time")

# BackupRefPtr and AsanUnownedPtr are mutually exclusive variants of raw_ptr.
assert(!enable_backup_ref_ptr_support || !use_asan_unowned_ptr,
       "Both BackupRefPtr and AsanUnownedPtr can't be enabled at the same time")

# RawPtrHookableImpl and BackupRefPtr are mutually exclusive variants of raw_ptr.
assert(
    !use_hookable_raw_ptr || !enable_backup_ref_ptr_support,
    "Both RawPtrHookableImpl and BackupRefPtr can't be enabled at the same time")

# RawPtrHookableImpl and AsanUnownedPtr are mutually exclusive variants of raw_ptr.
assert(
    !use_hookable_raw_ptr || !use_asan_unowned_ptr,
    "Both RawPtrHookableImpl and AsanUnownedPtr can't be enabled at the same time")

assert(!use_asan_backup_ref_ptr || is_asan,
       "AsanBackupRefPtr requires AddressSanitizer")

assert(!use_asan_unowned_ptr || is_asan,
       "AsanUnownedPtr requires AddressSanitizer")

if (is_apple) {
  assert(!use_blink || partition_alloc_enable_mach_absolute_time_ticks,
         "use_blink requires partition_alloc_enable_mach_absolute_time_ticks")

  assert(!is_mac || partition_alloc_enable_mach_absolute_time_ticks,
         "mac requires partition_alloc_enable_mach_absolute_time_ticks")
}

# AsanBackupRefPtr is not supported outside Chromium. The implementation is
# entangled with `//base`. The code is only physically located with the
# rest of `raw_ptr` to keep it together.
assert(build_with_chromium || !use_asan_backup_ref_ptr,
       "AsanBackupRefPtr is not supported outside Chromium")

assert(!use_asan_backup_ref_ptr || use_hookable_raw_ptr,
       "AsanBackupRefPtr requires RawPtrHookableImpl")

declare_args() {
  enable_pkeys = is_linux && target_cpu == "x64"
}
assert(!enable_pkeys || (is_linux && target_cpu == "x64"),
       "Pkeys are only supported on x64 linux")